MCP Core Concepts Best Practices
Designing an MCP server well means every tool, resource, and prompt is predictable to the clients and models that use it, not just functional in your own testing.
Search across all documentation pages
Designing an MCP server well means every tool, resource, and prompt is predictable to the clients and models that use it, not just functional in your own testing.
This checklist gathers the practices worth following across tool schemas, resource URIs, prompt templates, and how you deploy the server itself.
create_task reads as an action; task or handle gives the model nothing to match intent against.Args: block for every tool. The docstring is the primary text the model uses to judge relevance, and per-argument notes clarify units and formats a type hint alone cannot express.mode or action flag hides multiple behaviors behind one schema and makes model selection less reliable.Literal, or Pydantic models to validate inputs before your handler runs. This catches bad arguments as a schema error instead of a runtime crash inside your code.None or an empty string. A silent failure gives the model no signal that anything went wrong.articles://{id} or docs://{filename} makes resources predictable to discover and reason about.Group A, tool design. Most first servers are built around tools, and most model-selection problems trace back to vague names, thin docstrings, or overly broad tool schemas.
Yes. Many useful servers expose only tools and resources. Prompts are worth the design effort specifically when wording consistency across multiple clients is a real, observed need.
Because the failure modes are different: tool problems usually show up as the model calling the wrong thing or with bad arguments, while resource problems usually show up as data leakage, path traversal, or hidden side effects.
Building every capability as a tool, including pure reads, which buries safe data lookups inside the same primitive as destructive actions and makes the server harder to reason about.
Whenever the server moves from a local prototype toward being shared with a team or exposed remotely; the network and authentication concerns in that group only apply once a server leaves a single trusted machine.
Plain type hints are enough for simple scalar arguments. Reach for a Pydantic model when a tool needs field-level constraints, nested structures, or validation logic reused across multiple tools.
Strict enough that a resolved path is always checked against the intended root directory before any read happens, since a raw filename or path parameter should never be trusted directly from the caller.
Yes. stdio is the simpler default with the least operational overhead. Move to HTTP/SSE (or a tunnel) only once you have a concrete need for multiple clients or independent persistence.
Internal networks still have multiple users and services that should not all have equal access. Treat an internal HTTP/SSE server with the same authentication discipline as any other internal service.
Often yes, if more than one client application on that team invokes the same instruction. Centralizing still prevents drift even within a single team once more than one codebase depends on the wording.
If it accumulates many optional parameters to cover unrelated use cases, or gains a mode/action flag that branches into different behaviors, that is a strong signal to split it into narrower tools.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the current Model Context Protocol specification. Model names, SDK versions, and the MCP spec move quickly - verify current specifics at platform.claude.com/docs and modelcontextprotocol.io before relying on them.
Reviewed by Chris St. John·Last updated Jul 16, 2026