Skip to content
APIStead
MCP9 min9 Nov 2026

MCP server best practices

Transports, tool design, schemas, security and the mistakes that break real agents in production.

An MCP server is not a REST API with a different logo. It is a tool surface that a model chooses from at runtime. The model reads your tool names, descriptions and schemas, not your source code.

1. Choose the transport deliberately

Use Streamable HTTP for a hosted server. stdio is for local, single-user tools. The old HTTP+SSE transport is deprecated.

The current MCP spec (2026-07-28) is stateless: every request carries its own protocol metadata, and there is no protocol-level session. Older clients still use the initialize handshake. A good server supports both eras.

For a hosted discovery server:

  • expose one endpoint, e.g. POST /mcp;
  • accept JSON-RPC 2.0;
  • respond with JSON or an SSE stream;
  • set Cache-Control: no-store;
  • validate the Origin header;
  • return 405 for unsupported GET/DELETE session operations;
  • keep discovery free and read-only.

2. Name tools like a developer would

Use verb_noun:

  • search_apis
  • list_apis
  • get_api
  • get_routes
  • get_receipt
  • check_reliability

Avoid vague names like query, lookup or do_thing. The model has to choose between your tools and everything else in the host.

3. Write descriptions as instructions

A good description answers:

  • What does this tool do?
  • When should the model use it?
  • When should it not use it?
  • What does it return?
  • What is an example input?

Keep it under ~1,000 characters. Put the detail in the schema, not in a wall of prose.

4. Design schemas for the model

  • Use JSON Schema with types, enums, limits and additionalProperties: false.
  • Mark required fields.
  • Add descriptions to every property.
  • Use realistic examples.
  • Prefer flat objects over deeply nested ones.
  • Return stable IDs alongside labels.
  • Include a text fallback next to structured content.

5. Make errors recoverable

Return machine-readable errors:

{
  "code": "PRICE_ABOVE_CAP",
  "message": "The route costs 0.05 USDC; your cap is 0.01.",
  "retryable": false,
  "hint": "Raise the cap or choose a lower-cost route."
}

Do not return an HTML error page. Do not return 200 with an empty body. Do not make the model guess.

6. Do not put a 402 on discovery

Discovery is free. If search_apis or get_api returns a payment challenge, the agent cannot even decide whether to pay. Payment belongs on the provider's own route.

7. Security

MCP servers are a doorway. Treat them like one.

  • Validate Origin on every HTTP request to prevent DNS rebinding.
  • Authenticate anything that is not public, read-only discovery. OAuth 2.1 + PKCE is the production pattern for user-scoped tools. Static API keys are not enough for sensitive actions.
  • Never pass through tokens issued for another resource.
  • Validate every input on the server, the schema is a hint, not a security boundary.
  • Guard SSRF: block private IPs, loopback, link-local and cloud metadata endpoints; require HTTPS; validate redirects.
  • Screen metadata for prompt injection. A listing description that says “ignore previous instructions” is an attack.
  • Rate limit per IP, per token and per tool.
  • Log tool calls with correlation IDs, but never log secrets.
  • Require confirmation for destructive or money-moving actions.
  • Keep the tool surface small. Too many tools reduce model accuracy and increase blast radius.

8. State and idempotency

The protocol is stateless, but your product is not. Put durable state in a database and return an opaque handle:

  • job ID
  • draft ID
  • cursor
  • transaction hash

Bind the handle to the authenticated principal. Do not treat possession of a handle as authentication. Support Idempotency-Key for writes and paid calls.

9. Observability

Track:

  • tools/list calls;
  • tools/call by tool name;
  • latency p50/p95;
  • error rate by code;
  • rate-limit hits;
  • client name/version from initialize;
  • payment success rate for paid tools.

If you cannot see which tool agents choose and where they fail, you cannot improve the surface.

10. Test with real clients

Run the loop in:

  • MCP Inspector
  • Claude Desktop
  • Cursor
  • Continue
  • a scripted LangChain or OpenAI Agents SDK client

For each: list tools, call one, handle an error, paginate, and (for paid tools) complete a real payment. If any step needs a human or a doc read, fix it.

The checklist

  • Streamable HTTP, JSON-RPC 2.0
  • Free, read-only discovery
  • verb_noun tool names
  • Descriptions with when/when-not
  • Complete JSON Schema + examples
  • Structured output + text fallback
  • Machine-readable errors
  • Origin validation
  • Auth for non-public tools
  • SSRF guards
  • Prompt-injection screening
  • Rate limits
  • Idempotency for writes
  • Observability
  • Tested in Claude, Cursor, Inspector

Want the machine-readable version?

The catalogue and guides are available as markdown and JSON for agents.