How to Build an MCP Server (Step by Step) in 2026

Nishtha chauhan
Nishtha chauhan
|Published on |8 Mins
Cover Image for How to Build an MCP Server (Step by Step) in 2026

Your host can show a green “connected” indicator while your MCP server still exposes the wrong tool, accepts unsafe input, or breaks when a dependency fails. Avoid that trap by building one narrow capability and testing its contract before you hand it to a host.

The short answer: to build an MCP server, choose an official SDK, register one focused tool with an explicit input contract, run it over stdio or Streamable HTTP, inspect it directly, and test both success and failure paths. This guide uses Python for the runnable example, then shows where transport, host setup, and HTTP security fit.

Copy this build-and-test path first

Use this checklist before you add a second tool, a database, or a host configuration:

  1. Define one task, its inputs, its success result, and its side effects.

  2. Create the server with a current official SDK.

  3. Register one tool with schema-level and handler-level validation.

  4. Run it over stdio for a local host-launched process, or Streamable HTTP for a network service.

  5. Inspect the advertised tool and schema.

  6. Call it with valid input, invalid input, and a controlled dependency failure.

  7. Connect a host only after those checks pass.

  8. For remote access, authenticate requests and authorize each operation inside the server.

That order is deliberate: it lets you falsify the server’s contract while the surface area is small.

Ebook Preview

Get the Mobile Testing Playbook Used by 800+ QA Teams

Discover 50+ battle-tested strategies to catch critical bugs before production and ship 5-star apps faster.

100% Free. No spam. Unsubscribe anytime.

Step 1: Define one capability you can test

Write the tool contract before writing code. A useful first capability has a specific verb, bounded input, predictable output, and known dependency. “Search a book catalog by title or author” is testable; “help with books” is not.

Capture these five details:

  • User task: What should the host be able to ask for?

  • Input: Which fields are required, and what limits apply?

  • Success result: What information will the caller receive?

  • Side effects: Is the tool read-only, or can it change data?

  • Dependency: What database, API, file, or service can fail?

An MCP tool is a formal interface rather than merely a function. The 2026-07-28 tools specification defines tool names, descriptions, input schemas, optional output schemas, annotations, and results. That is why a narrow first tool pays off: you can inspect each part and decide exactly where bad input or a downstream outage should be handled.

Step 2: Set up a Python MCP server

The current Python SDK documentation uses MCPServer and supports starting a server through mcp.run(). It describes stdio as the local process transport and Streamable HTTP as the networked option. Follow the Python SDK’s current run guide when you install or upgrade, because SDK commands and APIs can change between versions.

Create a small project:

Then create server.py:

This is intentionally a placeholder search, not a production catalog. Its value is the contract: one named tool, one typed input, a description, explicit local validation, and one stable result shape. Replace the return statement with a bounded real operation only after the test path below works.

If your service is already TypeScript, use that language instead of introducing Python solely for MCP. The current TypeScript SDK v2 first-server guide documents McpServer, schema-based tool registration, and stdio serving. Keep package names and imports beside that current guide rather than copying a v1 snippet from an older tutorial.

Step 3: Add resources or prompts only when the job needs them

A server can expose tools, resources, and prompts. Tools perform callable operations; resources give clients separately addressable, file-like data; prompts provide reusable templates for user-facing workflows. The official build tutorial introduces all three capabilities.

Start with the one tool above when the user needs one action. Add a resource when a client needs to retrieve read-only data independently. Add a prompt when you have a repeatable interaction pattern that a user should select. Adding every capability to a starter project makes the contract harder to test without making the first use case better.

For the broader architecture behind hosts, clients, and servers, Quash’s MCP overview is useful background; this tutorial stays focused on the implementation path.

Step 4: Choose stdio or Streamable HTTP before deployment

Choose the transport from the deployment boundary, not from personal preference.

Question

Choose stdio

Choose Streamable HTTP

Who starts the server?

The host launches a child process

A service listens on a network endpoint

Where will you use it?

A local integration

A deployed or remotely reachable integration

How do clients connect?

Through the launched process

Through an HTTP endpoint

What boundary needs protection?

The local launcher and machine

HTTPS, authentication, authorization, and allowed origins

How should you test it?

Start the actual command in Inspector

Point Inspector at the actual endpoint

The Streamable HTTP specification says this transport replaced the earlier HTTP+SSE transport and changed again in the 2026-07-28 protocol revision. For a new remote server, use Streamable HTTP; do not begin with HTTP+SSE advice copied from an older post.

For local development, run the Python server under the Inspector workflow:

For an HTTP deployment, configure the server’s run call for Streamable HTTP, for example:

Do not send diagnostics to stdout when using stdio. Stdout carries protocol messages, so print() in Python or console.log() in a TypeScript stdio server can corrupt the JSON-RPC exchange. Send diagnostics to stderr instead.

Step 5: Inspect the contract before connecting a host

The MCP Inspector is the reference tool for testing and debugging MCP servers. It offers browser, CLI, and terminal interfaces through @modelcontextprotocol/inspector, so you can use the same checks interactively and in a repeatable shell workflow.

Run this smoke test against the transport you actually intend to ship:

  1. Start the server. Confirm the process stays alive and emits no protocol noise on stdout.

  2. Discover its protocol identity. In the 2026-07-28 era, call server/discover if your SDK or client exposes it. The discovery specification requires servers to report supported versions, capabilities, and identity before other requests.

  3. List tools. Confirm the exact name, description, input schema, output schema where present, and annotations.

  4. Call a representative valid input. For this example, try a short book title or author and confirm the result has the shape you promised.

  5. Call boundary inputs. Test an empty string, missing field, wrong type, and a value beyond your chosen limit. The request should fail before unsafe work reaches a dependency.

  6. Trigger a controlled failure. Make the catalog lookup fail in a test environment. Inspect the MCP tool result, including isError where applicable, instead of relying only on a process or HTTP status.

  7. Check structured output. If you declare an output schema and return structured content, validate the actual result against it. Keep a compatibility text representation when your selected protocol and client combination requires it.

  8. Repeat over the chosen transport. A correct stdio run does not prove your HTTP route, proxy, or authorization works.

  9. Save the evidence. Keep the launch command, inputs, expected results, protocol version, and failure cases in your repository or CI job.

A host connection is not a substitute for this list. It proves only that the host reached or launched the server.

Step 6: Connect one host after the smoke test passes

Host configuration is host-specific. With the Python SDK, the Claude Desktop installation path can be:

Treat that command as a Claude Desktop convenience, not a universal installer. Other hosts can require their own UI or configuration file, but they need the same core information: either the stdio command plus arguments or the Streamable HTTP URL.

After you register the server, test from the host as well: verify it lists search_books, invoke it with a valid query, and try the invalid-input case. If the host has tool-approval UI, confirm that the tool’s description and side-effect behavior make sense to the person approving it.

Step 7: Secure a remote MCP server as a service

A remote server that exposes private data or actions needs authorization at the HTTP boundary and authorization inside each tool. The MCP authorization tutorial explains the OAuth 2.1 model for protecting sensitive server resources and operations; the Python SDK’s authorization reference describes the server-side token-verification role.

Before exposing a remote endpoint, do all of the following:

  • terminate TLS at the service or a trusted proxy;

  • validate bearer tokens, including the issuer, audience or resource, expiry, and required scopes for your chosen identity provider;

  • authorize every tool operation using the authenticated identity and least privilege;

  • validate the Origin header to reduce DNS-rebinding risk;

  • bind a local-only development server to 127.0.0.1, not every interface;

  • apply timeouts, body-size limits, rate limits, and bounded tool inputs; and

  • keep tokens, credentials, and unnecessary personal data out of tool metadata, results, and logs.

These controls apply to HTTP. A local stdio server has no HTTP Authorization header; its security boundary is the host and user account that launch the process. Do not assume a bearer-token layer designed for an HTTP route protects a local child process.

Step 8: Check the 2026 protocol boundary

The 2026-07-28 protocol revision is worth checking before you follow any tutorial that refers to sessions, a GET-based stream, or HTTP+SSE as the default. In this revision, server/discover is the capability handshake, and the protocol is stateless by default. Long-lived notifications are opt-in through subscriptions/listen, which replaced the former resource subscription RPC and HTTP GET endpoint.

You do not need subscriptions for this static one-tool server. You do need to verify that your chosen SDK, Inspector, and host agree on the protocol era. Test a legacy client separately only when you have a real compatibility requirement.

Troubleshoot the failures that hide in a “working” server

Symptom

Likely cause

What to check

Inspector sees broken or unreadable messages

Logs went to stdout

Move diagnostics to stderr and restart

Host sees no tools

Wrong launch command or registration failed

Run the exact command in Inspector and list tools

Valid calls work but invalid calls crash

Validation happens after dependency work

Enforce schema limits and handler validation first

Result does not match what the host expects

Tool result and declared output contract differ

Compare the returned content and structured data to the schema

Remote requests stop before a tool runs

Token, scope, or Origin rejection

Test no token, invalid token, insufficient scope, and allowed origin separately

Host connects but calls do nothing

Transport or endpoint mismatch

Confirm stdio versus Streamable HTTP and the exact configured endpoint

Conclusion

Build the smallest server that can prove one useful capability. If you can inspect its schema, reject bad input, see a controlled failure, and repeat those checks over the transport you will deploy, you have a contract you can safely give a host. Add capabilities only after that contract is stable—and make your next change earn its place in the same smoke test.

FAQ

Should you start with stdio or Streamable HTTP?

Start with stdio when a local host will launch the process. Start with Streamable HTTP when you need a deployed endpoint or network clients. The deployment boundary should decide the transport.

Do you need resources and prompts in a first MCP server?

No. A single focused tool is enough for a first server. Add resources or prompts only when they solve a separate, specific product need.

Why does stdout break a stdio MCP server?

Stdout is the protocol channel for a stdio server. Extra diagnostic output can corrupt the messages exchanged between host and server, so send logs to stderr.

What should you test before you connect a host?

List the tool contract, call it with representative valid and invalid inputs, and exercise a controlled dependency failure. Repeat the checks over the intended transport.

Is HTTP+SSE the default for a new remote MCP server?

No. The current Streamable HTTP specification identifies Streamable HTTP as the replacement for the earlier HTTP+SSE transport. Use the current SDK documentation for your selected protocol version.

Can an HTTP bearer token secure a local stdio server?

No. Bearer-token authorization applies to HTTP requests. For stdio, the process launcher and local machine boundary control who can run and reach the server.