socket.help
← Blog

How to test an AI agent’s tool calls with a mock MCP server

Point your agent at a temporary MCP server with fixed tools and static replies, then check how it calls tools, handles bad arguments, and recovers from errors.

Test the agent, not the backend behind the tool

When an agent misbehaves, it is hard to tell whether the model chose the wrong tool, sent malformed arguments, or reacted badly to a real system’s response. Testing against production tools adds side effects and noise: a refund tool that actually refunds is not something you want in a test loop.

A mock MCP server removes the backend from the question. Each tool has a fixed name, description, argument schema, and static reply. The agent sees a normal MCP server over Streamable HTTP, so you can watch how it discovers tools, which ones it picks, and what it does with a success or a failure, with the same reply every time.

Design a small set of tools

Start with the tools your agent will really use, and copy their names and descriptions closely. Models choose tools largely from descriptions, so a vague description in the mock produces the same confusion it would in production. A server can have up to 10 tools within 80 KiB of total configuration.

Arguments are an object with up to 20 properties of type string, number, integer, or boolean, and a required list. Nested objects and arrays are not supported. Each reply is static text up to 10,000 bytes. Set isError to true to make a tool fail, and delayMs up to 2,000 to make it slow.

  • A lookup tool that returns a short, realistic success reply.
  • A tool with required arguments, so you can see whether the agent supplies them.
  • A tool that always fails with isError, so you can check how the agent recovers.
  • A slow tool with a delay, so you can test your client’s timeout and progress display.

Create the server

Create a server from the Mock MCP server page, or send the configuration to the API. New servers are private. The response includes the endpointUrl, an accessToken that MCP clients send in the X-Access-Token header, a separate managementToken for updating and deleting the server, and the expiry time.

Create a private mock MCP server
curl -X POST 'https://socket.help/api/mcp-servers?visibility=private' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Order agent test",
    "tools": [
      {
        "name": "get_order",
        "description": "Look up an order by its ID.",
        "inputSchema": {
          "type": "object",
          "properties": { "id": { "type": "string", "description": "Order ID" } },
          "required": ["id"]
        },
        "response": "Order test-order-001: shipped, 2 items."
      },
      {
        "name": "refund_order",
        "description": "Refund an order by its ID.",
        "inputSchema": {
          "type": "object",
          "properties": { "id": { "type": "string" }, "amount": { "type": "number" } },
          "required": ["id", "amount"]
        },
        "response": "Refund service unavailable. Try again later.",
        "isError": true,
        "delayMs": 1500
      }
    ]
  }'

Connect a client and make a call

Add the endpointUrl to any MCP client that supports Streamable HTTP and lets you set a custom header, and give it the access token as X-Access-Token. The server supports protocol versions 2025-11-25, 2025-06-18, and 2025-03-26, and answers initialize, ping, tools/list, and tools/call with JSON responses.

You can also call it directly with curl, which is a quick way to check the configuration before involving a model. Requests must accept both application/json and text/event-stream, as the MCP transport requires, even though replies are plain JSON.

Call a tool directly
curl -X POST https://socket.help/mcp/mock/SERVER_ID \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "X-Access-Token: $MCP_ACCESS_TOKEN" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_order","arguments":{"id":"test-order-001"}}}'

Check both kinds of failure

The MCP specification separates two kinds of error, and a good agent handles them differently. A tool execution error comes back as a normal result with isError set to true. It is meant for the model to read, so the agent can explain the problem or retry with different arguments. A protocol error is a JSON-RPC error object, such as an unknown tool or invalid arguments.

The mock produces both. A tool configured with isError returns its reply as a failed result. A call that names a tool that does not exist, leaves out a required argument, adds an argument that is not in the schema, or sends the wrong type gets JSON-RPC error -32602 with a message such as Missing argument: amount. Watch whether your agent invents arguments, retries the same failing call in a loop, or tells the user honestly that the refund did not go through.

Switch scenarios without reconnecting

Write a few prompts that should lead to each tool, and one that should lead to none. Run each prompt several times, because model output varies, and note which tool was called with which arguments. A mock with fixed replies keeps the server side constant, so differences between runs come from the agent, its prompt, or its tool descriptions.

Replace the configuration with a PUT request to /api/mcp-servers/SERVER_ID using the management token. The endpoint and expiry stay the same, so you can flip a tool from success to failure between test runs without changing your client. A GET on the same path returns the configuration and a uses count, which is a quick check that the agent actually reached the server.

Read the configuration and usage count
curl https://socket.help/api/mcp-servers/SERVER_ID \
  -H "Authorization: Bearer $MCP_MANAGEMENT_TOKEN"

What a mock server does not do

Replies are static. The server does not run code, read the arguments to build a reply, make outbound requests, or remember earlier calls. It does not record a per-call history either, so log the agent’s tool calls on the client side. It offers tools only, not resources or prompts, and it does not open SSE streams; a GET request returns 405.

Every mock tool is listed with read-only annotations, even one named refund_order, so a client that relies on annotations to decide when to ask for confirmation may behave differently than with your real server. Servers start with a one-hour lifetime and accept up to 120 requests per minute. Requests sent from a web page on another origin are rejected, so use a desktop, CLI, or server-side client.

Questions people ask

Which MCP transport does the mock server use?

Streamable HTTP with JSON responses over POST. It does not issue session IDs or open SSE streams.

Can a mock tool return different replies for different arguments?

No. Each tool has one static reply. Arguments are validated against the schema but do not change the response. Use separate tools or update the configuration between runs.

How do I simulate a slow or failing tool?

Set delayMs up to 2,000 milliseconds for latency, and isError to true to return the reply as a tool execution error.

Does the agent need an account?

No. Anyone can create a server. Keep the returned access and management tokens, because they are shown once.

Sources