Building an MCP Server: SDKs, Tool Schemas, Testing, and Deployment — the practical path from a 15-minute local prototype to a hosted remote server
Building an MCP server means: pick an SDK, define tools with a schema and a handler, optionally expose resources/prompts, test locally with the MCP Inspector, then choose stdio or a hosted Streamable HTTP deployment. The official docs put a working stdio server at roughly 15 minutes with Python FastMCP — the concrete first step toward this wiki's goal of a developer shipping a working, secure remote server on the first attempt.
SDKs and project scaffolding
- Python: the FastMCP class uses decorators (@mcp.tool(), @mcp.resource(), @mcp.prompt()) and generates JSON Schema automatically from type hints and docstrings. Requires Python 3.10+ and MCP SDK 1.2.0+. Scaffolding: uv init weather then uv add "mcp[cli]" httpx — a working add/fetch_url server runs over stdio in about 15 minutes.
- TypeScript: the official @modelcontextprotocol/sdk provides an McpServer class with type-safe tool registration using Zod schemas for input validation. Scaffolding uses official npm SDKs, plus third-party boilerplate generators — the mcpc CLI tool and the VS Code AI Toolkit extension's tool catalog (both reported by secondary sources, not the official docs).
- .NET: Microsoft now ships preview support via the Microsoft.McpServer.ProjectTemplates NuGet package, usable from Visual Studio project templates.
Defining tools: input schemas and error handling
A tool is a function with typed inputs, JSON Schema validation, and side effects that the model can invoke based on context. In Python FastMCP, the tool name comes from the function name, the description from the docstring, and the parameter schema from type annotations — all inferred, no manual JSON Schema authoring needed for the common case.
- Validation modes: flexible (default — auto-converts compatible types) or strict (strict_input_validation=True — rejects type mismatches).
- Required vs optional: parameters with a default value are optional; parameters without one are required in the generated tool schema.
- Constraints: Annotated + Field() add range/format constraints, e.g. ge=1, le=2000.
- TypeScript: Zod schemas cover string constraints, number ranges, enums, optional fields, arrays, and nested objects (reported by a third-party validation tutorial, not the official SDK docs directly).
Error handling: standard Python exceptions (ValueError, TypeError) are auto-converted into MCP error responses; ToolError gives explicit control over the message shown to the client. mask_error_details=True hides internal details from error responses (useful before exposing a server remotely). The protocol itself carries an isError boolean on tool results to signal execution failure to the caller — see MCP Core Primitives: Tools, Resources, and Prompts — who is allowed to invoke what, and what a server must declare to offer it for how this fits the tool-result content model.
Stdio-specific trap: a stdio server must never write non-protocol data to stdout — doing so corrupts the JSON-RPC stream and breaks the server. Use print(..., file=sys.stderr) or a logging library instead. HTTP-based servers don't have this constraint: standard stdout logging is safe there because it doesn't interfere with HTTP responses.
Resources and prompts alongside tools
Resources are passive, read-only data sources (file contents, API documentation, database schemas) attached for context without requiring a tool call. Two discovery shapes exist: direct resources with a fixed URI (e.g. calendar://events/2024) and resource templates with dynamic parameters (e.g. travel://activities/{city}/{category}). Every resource declares a unique URI and a MIME type. In Python, @mcp.resource() registers a resource and supports parameterized URIs the same way tools support parameters.
Prompts are user-controlled, reusable instruction templates with parameterized arguments that steer the model toward specific tools/resources; they support parameter completion and can be context-aware (referencing what's actually available in that session).
Protocol operations: resources/list, resources/templates/list, resources/read, resources/subscribe for resources; prompts/list and prompts/get for prompts. See MCP Core Primitives: Tools, Resources, and Prompts — who is allowed to invoke what, and what a server must declare to offer it for the full primitive-level model (who controls what) that these SDK-level calls implement.
Testing locally with MCP Inspector
MCP Inspector is the official Anthropic tool for testing and debugging a server — described as "Postman for MCP," with a browser-based UI. No install step: run npx @modelcontextprotocol/inspector <command> (e.g. pointed at your server's start command). Its connection pane lets you pick the transport to connect over; the spec currently defines two — stdio, for a locally spawned server process, and Streamable HTTP, for a remotely hosted one (this replaced the older HTTP+SSE transport from protocol version 2024-11-05, so SSE itself is no longer a separate top-level transport) — so the same tool covers local dev and remote debugging.
- Tools tab: pick a tool, inspect its schema/description, fill parameters via a form auto-generated from the JSON Schema, click "Call" to see the result. - Resources tab: lists resources with MIME types and descriptions; supports content inspection and subscription testing. - Prompts tab: shows templates, arguments, descriptions; lets you test with custom argument values. - Notifications pane: shows every log/message the server sent during the session.
Recommended workflow: launch with the server → verify connectivity → check capability negotiation → make code changes → rebuild → reconnect → test the affected features → monitor the notifications pane.
For CI, a headless CLI mode skips the browser UI: npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list returns JSON, suitable for pipelines. Beyond Inspector, fully automated testing uses the official @modelcontextprotocol/sdk Client class with StdioClientTransport to spawn the server as a subprocess and call listTools(), callTool(), listResources(), readResource() directly — this drives the exact protocol a real AI client uses, so assertions on schemas/results/errors are deterministic and don't require a model in the loop (reported by a secondary source, not the official Inspector docs).
Deployment: local stdio vs hosted remote server
Local stdio pipes JSON-RPC over stdin/stdout inside a single process — the default for initial development. The client is responsible for the whole process lifecycle: launching, stopping, restarting, setting environment variables, and communicating through the pipes. This is the shape MCP Inspector and most quickstarts start from, but it doesn't fit production use cases that need a remotely reachable server.
Remote deployment means Streamable HTTP: the transport introduced in March 2025 that replaced HTTP+SSE, using a single HTTP endpoint (conventionally /mcp) with bidirectional communication. It supports stateless mode (the default, needed for horizontal scaling — servers scale across replicas without session affinity) and stateful mode (a Mcp-Session-Id header keeps a long-lived session). TypeScript Streamable HTTP servers typically sit behind Express or Hono middleware using the SDK's built-in adapters; Python servers pair FastMCP with FastAPI for the HTTP layer.
Hosting patterns observed across providers:
- Render: reads the PORT environment variable (default 8080 for local dev); supports Infrastructure-as-Code deployment via render.yaml for more complex setups.
- AWS ECS: a three-tier pattern — a public UI Service (Gradio), an Agent Service (Amazon Bedrock) in a private subnet, and the MCP Server itself in a private subnet — using Service Connect for internal service discovery and Express Mode for automated load balancing.
- Azure App Service: two patterns — bring-your-own MCP SDK for full control, or the built-in MCP feature (preview) that converts an existing REST API's OpenAPI 3.x spec into MCP tools automatically, with the platform handling authentication, protocol negotiation, tool discovery, and hot-reload.
- Cloudflare Workers: reported as the fastest path to production-grade remote hosting with Streamable HTTP, including scale-to-zero when idle.
Security baseline for any remote deployment: HTTPS/TLS is required for all Streamable HTTP traffic in transit, integrating with Bearer-token authentication via standard HTTP headers. For the enterprise-grade case, OAuth 2.1 with PKCE, dynamic client registration, and Resource Indicators (RFC 8707) are the pieces that make a remote server safe to expose beyond a single trusted client — see Authorization in MCP: OAuth 2.1, Token Scoping, and the Confused-Deputy Problem — securing a remote server on the first attempt for the authorization model this plugs into, and MCP Transports: stdio for Local Servers, Streamable HTTP for Remote Ones for the stdio-vs-HTTP trade-offs behind this choice in the first place.
Related
- MCP Core Primitives: Tools, Resources, and Prompts — who is allowed to invoke what, and what a server must declare to offer it — the primitive-level definitions (who controls tools vs resources vs prompts) that the SDK decorators and schemas in this page implement in code. - MCP Transports: stdio for Local Servers, Streamable HTTP for Remote Ones — why stdio suits local dev and Streamable HTTP is required once a server needs to be reachable by more than one local client. - Authorization in MCP: OAuth 2.1, Token Scoping, and the Confused-Deputy Problem — securing a remote server on the first attempt — what "secure" means for the remote deployment step here: OAuth 2.1, PKCE, and token scoping once a server is no longer just a local subprocess.
Verified against
- modelcontextprotocol.io/docs/develop/build-server
- modelcontextprotocol.io/docs/tools/inspector
- modelcontextprotocol.io/docs/learn/server-concepts
- modelcontextprotocol.io/specification/latest/basic/transports
- github.com/modelcontextprotocol/typescript-sdk
- gofastmcp.com/servers/tools
- learn.microsoft.com/en-us/dotnet/ai/quickstarts/build-mcp-server
- learn.microsoft.com/en-us/azure/app-service/scenario-ai-model-c…
- aws.amazon.com/blogs/containers/deploying-model-context-protoco…
What links here
Source: Sinapsi — verified compositional memory, queryable by LLMs. Query this wiki live from your assistant over MCP, or build your own verified wiki (public, or private for your team). CC BY 4.0 — reuse with attribution to Sinapsi.