MCP Directory

Create an MCP Server: The 20-Minute Path

Build one useful local tool first, verify the protocol boundary, and delay remote complexity until the server earns it.

MCP Directory·August 4, 2026·6 min read
A software developer working on code at a dual monitor setup in a modern office.
Photo by Zayed Hossain on Pexels

To create an MCP server in 20 minutes, start with one read-only Python tool, run it over stdio, inspect its schema and result, then register the exact launch command in your client. Do not begin with OAuth, a database writer, or remote hosting. First prove that discovery, input validation, execution, and errors all work locally.

The 60-second version

The shortest honest path is a local stdio server with one deterministic tool. It needs a name, a typed function, a transport, and a client entry—nothing else.

  1. Install Python 3.10+, uv, and MCP SDK 2.0+.
  2. Create a project with uv init twenty-minute-mcp.
  3. Add the SDK with uv add "mcp[cli]".
  4. Put the server code below in server.py.
  5. Run uv run server.py, then test it with Inspector.
  6. Add the launch command to your MCP client and restart the client.

This server exposes a single MCP tool, not a chatbot. MCP defines tools as schema-described functions that a client discovers with tools/list and invokes with tools/call; your host still owns the model conversation. The official server quickstart confirms the current Python requirement and stdio pattern.

Pick your SDK

Choose Python unless your existing service is TypeScript. The language decision rarely affects protocol capability; it affects deployment fit, validation style, and how quickly your team can debug the wrapper around its real business logic.

ChoiceBest whenTrade-off
Python SDKYou wrap data scripts, internal APIs, or Python librariesConcise decorators and inferred schemas; packaging a local runtime can be awkward
TypeScript SDKYou already ship Node services or need its web middleware ecosystemExplicit schemas and build output; more setup for a one-file prototype

The current Python SDK derives tool definitions from type hints and docstrings. The current TypeScript SDK uses McpServer, registerTool, and a schema library such as Zod; its official repository also documents runtime adapters. I would not switch languages merely because an old tutorial has more stars. If this prototype will become a maintained integration, follow the longer build-an-MCP-server guide after proving the call path.

Write the server

Write one bounded, read-only operation with types and a precise description. This example is intentionally boring: it proves MCP wiring without hiding failures behind a third-party API.

from mcp.server import MCPServer

mcp = MCPServer("text-utilities")


@mcp.tool()
def word_count(text: str) -> dict[str, int]:
    """Count whitespace-separated words and Unicode characters in text."""
    return {
        "words": len(text.split()),
        "characters": len(text),
    }


if __name__ == "__main__":
    mcp.run(transport="stdio")

Save it as server.py and run uv run server.py. A stdio server appearing to sit silently is normal: the client launches it and exchanges newline-delimited JSON-RPC over stdin and stdout. The transport specification says stdout may contain only valid MCP messages, while logs belong on stderr; one stray print() can corrupt the session (MCP transports specification).

The return type matters. Typed, structured data is easier for clients to validate and for models to use than a decorative sentence. Keep domain logic in ordinary functions and make the decorated handler thin; then you can unit-test behavior without starting an MCP session.

Close-up of colorful CSS code lines on a computer screen for web development.
Photo by Pixabay on Pexels

Wire it into a client

Configure the client to launch exactly the command that worked in your terminal, using absolute paths. For Claude Desktop on macOS or Windows, add this documented mcpServers entry and replace both placeholders:

{
  "mcpServers": {
    "text-utilities": {
      "command": "/ABSOLUTE/PATH/TO/uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/twenty-minute-mcp",
        "run",
        "server.py"
      ]
    }
  }
}

Find uv with which uv on macOS/Linux or where uv on Windows. Validate the edited file with the MCP config validator, fully quit the client, and reopen it. The official local-server connection guide documents Claude Desktop’s config location, restart requirement, and server indicator.

Confirmation is not “the process exists.” Open the client’s connectors or tools UI and verify that text-utilities appears and exposes word_count with one string input. Then ask it to count a known phrase and compare the result manually. If the tool is absent, inspect client logs before changing code.

Test it

Test discovery, boundaries, failures, and side effects—not just the happy-path answer. MCP Inspector is the fastest neutral check because it shows what the server advertises before a model decides whether to call anything.

Run:

npx @modelcontextprotocol/inspector uv --directory /ABSOLUTE/PATH/TO/twenty-minute-mcp run server.py

In the UI, connect, list tools, select word_count, and call it with hello MCP world. Expect words to be 3 and characters to be 15. The official Inspector guide covers direct commands plus inspection of tools, resources, prompts, notifications, and logs.

Before trust, check these cases:

  • Empty input and a very large string.
  • Newlines, emoji, and non-English text.
  • The advertised input schema matches the handler.
  • Errors are useful but do not reveal secrets or stack traces.
  • Repeated calls return consistent results and do not leak state.

For a real integration, add unit tests around domain logic, an integration test that lists and calls tools, timeouts for every network request, and explicit maximum input/output sizes. A passing manual call is a smoke test, not production evidence.

The mistakes that cost an afternoon

Most “MCP failures” are process-launch and framing failures, not protocol mysteries. Check the boring edges first; rebuilding the tool usually wastes time.

  • Logging to stdout. With stdio, use Python’s logging module or stderr. stdout is the protocol channel.
  • Relative paths. GUI clients may start servers from an unexpected working directory. The official debugging guide recommends absolute executable, project, and environment-file paths.
  • A missing runtime environment. A command that works in your shell may fail in a GUI with a smaller PATH. Use the absolute uv path and declare required environment values in client configuration.
  • Editing without restarting. Many clients keep the subprocess and cached tool list alive. Fully quit and reopen after code or config changes.
  • Vague tool descriptions. “Process data” gives the model no reliable selection signal. Name the action, inputs, output, and limits.
  • Catching every exception. Returning “something went wrong” erases diagnosis. Catch expected upstream failures, attach safe context, and let programming bugs surface in server logs.
  • Starting with write access. A delete-or-send tool multiplies the cost of every schema and authorization mistake. Prove a read path first.

My hard rule: never let an MCP handler become the authorization boundary by accident. Validate identity and permission in the underlying service too, because tool metadata guides a model; it does not enforce business policy. The MCP project's security guidance recommends sandboxing local servers, restricting filesystem and network access, and minimizing authorization scopes.

Ship it

Keep stdio for one user on one machine; move to Streamable HTTP when multiple clients need a centrally operated service. Remote is an architectural change, not a transport toggle you deploy casually.

Local stdioRemote Streamable HTTP
Client starts one subprocessServer runs independently for many clients
OS user permissions dominateAuthentication and per-user authorization are mandatory
No listening network portHTTPS endpoint, origin validation, rate limits, and operations are required
Simple secrets in local configCentral secret storage and token lifecycle are needed

The protocol’s architecture overview describes local stdio as a typical one-client connection and remote HTTP as a typical many-client service. For HTTP, the transport specification requires Origin validation and recommends localhost binding for local use plus authentication for connections.

I would ship locally until user distribution or uptime becomes the real problem. For remote, add OAuth or another client-supported authentication flow, tenant isolation, audit logs, request deadlines, rate limits, health checks, and graceful shutdown before publishing a URL. Do not expose a development endpoint directly to the internet.

Finally, version your tool behavior, document permissions, and make destructive actions conspicuous. Compare established patterns in our best MCP servers, but keep the first release small: one dependable tool beats ten ambiguous ones.

FAQ

Do I need to pay to create an MCP server?

No, you do not have to pay to create an MCP server. The protocol and official SDKs are open source; costs come from any model client, paid upstream API, database, or remote hosting you choose.

Can I create an MCP server without coding?

Not reliably for custom behavior. A generator may scaffold a project, but you still need to define tool schemas, permissions, error handling, and tests; for a common integration, start by checking an existing server instead.

Should my first MCP server use stdio or HTTP?

Use stdio for your first local server. Choose Streamable HTTP only when independently hosted access, multiple users, or centralized operations justify authentication and network security work.

How do I keep an MCP server safe?

Give it the least permission needed and begin with read-only tools. Validate authorization in the underlying service, keep secrets out of code and logs, constrain file and network access, and require confirmation for consequential actions.

Put this into practice

Browse MCP servers by capability, or check your own setup's tool budget and security.

More in Build & ship

Browse all build & ship articles.