cd /news/agent-protocols/mcp-server-design-patterns-and-anti-… · home › topics › agent-protocols › article
[ARTICLE · art-156779] src=pournasserian.com ↗ pub= topic=agent-protocols verified=true sentiment=· neutral

MCP server design patterns and anti-patterns

A practitioner guide published in October 2026 identifies four design patterns that hold up in production Model Context Protocol (MCP) servers built to the 2026-07-28 specification, led by exposing retrieval as a cheap `search` tool plus a `fetch`-by-ID tool and leaving reasoning to the calling model. The guide, part 32 of the author's MCP series, also recommends explicit handles for cross-call state instead of per-connection state, Multi Round-Trip Requests that settle inputs before returning a task handle, and shipping one capability across layered surfaces from universal tools to MCP Apps. It attributes most production problems to a short list of anti-patterns including one tool per endpoint, per-connection state, unprotected `requestState` and token passthrough, and notes OpenAI's MCP guide asks servers to implement read-only `search` and `fetch` tools to work with ChatGPT's deep research and company knowledge features.

by read7 min views2 publishedOct 24, 2026
MCP server design patterns and anti-patterns
Image: Pournasserian (auto-discovered)

The specification tells you what a Model Context Protocol (MCP) server must do. It says much less about what holds up in practice, so here I’ve collected four recurring patterns that work and the anti-patterns behind most production problems, each with a pointer to the part of the series that explains the fix.

This is part 32 of my series on what an MCP server does under the 2026-07-28 specification, after part 31 on which AI hosts support which features. One frequently asked architecture question, whether to put an AI agent behind a tool, gets the next part to itself. The facts are as I read them in October 2026.

In brief #

  1. Expose retrieval as two tools, a cheap search and a fetch by ID, and leave the reasoning to the calling model.
  2. Keep state across calls in explicit handles, not in the connection: per-connection state breaks behind load balancers, and a handle is checked against the caller on every call.
  3. When long work needs a confirmation, settle the inputs first and then return a task, with no side effects before the last input round.
  4. Ship one capability on several surfaces, from tools that work everywhere to an MCP App where a host can render one, each degrading to the next.
  5. Most production problems come from a short list of anti-patterns, among them one tool per endpoint, per-connection state, unprotectedrequestState and token passthrough.

Four patterns that work #

Three of these are my advice rather than rules from the specification; the handle pattern follows the specification’s own guidance, as part 5 shows.

Search plus fetch

When your server fronts a body of content, expose retrieval as two tools: a cheap search that returns ranked, citable hits, and a fetch that returns a full item by its ID. The calling model does the reasoning, and your server does what it’s uniquely good at: knowing the index, the schema and the ranking.

  • search returnsstructuredContent with IDs, titles, scores, short snippets and source Uniform Resource Identifiers (URIs), plus a compact text rendering incontent .
  • fetch returns the full document, or aresource_link the host can attach.

The pattern also matches what two ChatGPT features, deep research and company knowledge, expect: OpenAI’s MCP guide asks servers that want to work with them to implement two read-only tools, search and fetch. The example instructions in part 4 tell the model to call search_docs first and then fetch_doc, which is this pattern in a sentence.

Explicit handles for state

There are no sessions under the 2026-07-28 specification, so state that spans calls needs a home of its own. Return an opaque handle from a creation tool, accept it on later calls, authorize the caller against it every time, state its lifetime in the description, and return a recoverable tool execution error when it expires. Part 5 explains why and builds one in Python, with an owner and an expiry.

Multi round-trip requests, then a task

For long-running work that needs a confirmation, resolve the inputs synchronously with Multi Round-Trip Requests (MRTR, part 13), then return a task handle and run the work asynchronously. Cause no side effects before the final input round. Part 26 shows how the two phases fit together.

Layered surfaces

Host support is uneven, as part 31 showed, so ship one capability on several surfaces, each degrading to the next. Here is one workflow, an incident postmortem, delivered five ways: tools that work everywhere, server instructions that point to the prompt and the skill, a prompt for integrated development environment (IDE) and chat hosts, a skill for hosts with Skills, and an MCP App for hosts with Apps.

A host that supports only tools still gets the incident data through get_incident and list_timeline_events. Each layer it supports adds to that. Part 12 gives the reasoning behind each choice of primitive.

Anti-patterns that cause most production problems #

I’ve grouped the twelve by where they live. Where the specification itself sets the fix, the group says so; the rest is my advice.

In tools and their results

Five concern the tools themselves and what they return. Two of the fixes are the specification’s: servers SHOULD return tools in a deterministic order, and under the MCP Apps extension, tools MUST return meaningful content even when a user interface (UI) is available.

Anti-pattern Why it hurts Instead
Throwing exceptions on bad input Becomes a protocol error, and the model can’t self-correct isError: true with an actionable message (part 9 )
One tool per Representational State Transfer (REST) endpoint, 50 or more tools Context bloat and poor tool selection Fewer, task-shaped tools with sharp descriptions ( part 9 )
| Dumping whole documents into results | Hits host output limits and costs tokens | Summaries plus `resource_link` s ([part 9](https://pournasserian.com/writing/mcp-2026-tool-errors-and-design) ) | 
| UI-only answers | Useless in text-only hosts | Meaningful `content` always ([part 25](https://pournasserian.com/writing/mcp-2026-apps-building) ) | 
| Unstable list ordering | Prompt-cache misses, flaky pagination | Deterministic sort ( [part 6](https://pournasserian.com/writing/mcp-2026-caching-and-pagination) ) | 

One detail for Python: in the official software development kit (SDK), mcp 2.3.0, an unexpected exception in an MCPServer tool doesn’t become a protocol error (only a deliberate MCPError does). The SDK turns it into an isError result that says only Error executing tool <name>, so the model still can’t tell what to fix. Raise ToolError with a message that says what a valid value looks like, as part 9 shows.

In state and multi-step flows

Four concern state and multi-step flows, which have to work without sessions. One fix is the specification’s: if requestState influences authorization, resource access or business logic, the server MUST protect its integrity, for example with a Hash-based Message Authentication Code (HMAC) or Authenticated Encryption with Associated Data (AEAD).

Anti-pattern Why it hurts Instead
Per-connection state Breaks behind load balancers under the 2026-07-28 specification Explicit handles in a shared store ( part 5 )
Side effects before input_required Abandoned retries leave half-done work No writes until the last input round ( part 14 )
Unprotected requestState Tampering, replay, cross-user reuse AEAD or HMAC, plus the principal, an expiry and a digest of the request ( part 14 )

| Elicitation-only flows | Fail in unattended agents | A non-interactive path with clear errors ( part 15 ) |

In security and trust

The last three are about trust, and two of the fixes are the specification’s: a server MUST NOT accept or transit any token that isn’t valid for its own resources, and servers SHOULD NOT mark sensitive parameters with x-mcp-header, because intermediaries can see header values.

| Anti-pattern | Why it hurts | Instead |

|---|---|---|
| Token passthrough | Confused deputy, audience violations | Audience-bound, delegated tokens ( [part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) ) | 
| Secrets in skills, instructions or `x-mcp-header` | Leaks to models, logs and intermediaries | Keep secrets server-side ( [part 21](https://pournasserian.com/writing/mcp-2026-threat-model) ) | 

| Descriptions in the style of prompt injection, such as “always use this server” | Looks like tool poisoning, so hosts and reviewers distrust it | Factual, scoped descriptions ( part 4 ) |

Method and caveats #

  • Built from the patterns and the anti-patterns of my guide’s chapter on design patterns, written against the 2026-07-28 specification; product facts as of October 2026.
  • The search plus fetch, task and layered-surface patterns are my advice; the handle pattern follows the specification’s guidance. Five anti-patterns break a rule of the specification or of its MCP Apps extension (unstable tool order, UI-only answers, unprotected requestState , token passthrough and secrets inx-mcp-header ), and each group names its rules; the other seven are my advice.
  • Where OpenAI’s documentation is more precise than my notes (what ChatGPT expects search andfetch for), this article follows OpenAI’s MCP guide as checked on 8 October 2026.
  • Where the Python SDK differs from my notes (what an unexpected exception in a tool becomes), this article describes mcp 2.3.0, as I read its source on 9 October 2026.
── more in #agent-protocols 4 stories · sorted by recency
── more on @model context protocol 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/mcp-server-design-pa…] indexed:0 read:7min 2026-10-24 · —