# Build an MCP server: SDKs, a minimal example, and what comes after

A minimal MCP server is genuinely small: declare a tool, give it a schema, wire up stdio. This page gets you from zero to a server Claude can call, then points at the official docs for everything past that — it's a starting point, not a reference.

## Pick an SDK

| SDK | Package | Runs on |
| --- | --- | --- |
| TypeScript (v2) | `@modelcontextprotocol/server` | Node.js ≥ 20 |
| Python (v2) | `mcp` | Python ≥ 3.10 |

Both are maintained by the MCP project and both hit 2.0 in July 2026 alongside the current spec. One migration note before you copy code off a blog post: v1 of the TypeScript SDK was a single package called `@modelcontextprotocol/sdk`, and most older tutorials show it. New servers should start on v2.

## A minimal TypeScript server

This is the official quickstart shape — one tool, validated input, stdio transport:

```sh
npm init -y
npm install @modelcontextprotocol/server
```

```typescript
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const server = new McpServer({ name: 'greeting-server', version: '1.0.0' });

server.registerTool(
  'greet',
  {
    description: 'Greet someone by name',
    inputSchema: z.object({ name: z.string() })
  },
  async ({ name }) => ({
    content: [{ type: 'text', text: `Hello, ${name}!` }]
  })
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main();
```

That's the whole contract: `registerTool` takes a name, a description plus Zod input schema, and a handler returning `content` blocks. The schema is what the model reads to decide when and how to call your tool — write the description for a reader who can't see your code, because that's exactly what the model is.

The Python SDK is the same idea with less ceremony — a type-hinted function and a docstring become the schema and description:

```python
# pip install "mcp[cli]"
from mcp.server import MCPServer

mcp = MCPServer("Demo")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
```

## Test it with the Inspector

Don't debug through a chat window. The [MCP Inspector](https://mcpyet.com/guides/mcp-inspector.md) connects to your server directly and shows the schema exactly as clients will see it:

```sh
npx -y tsx src/index.ts   # sanity check: should start and wait silently
npx @modelcontextprotocol/inspector npx -y tsx src/index.ts
```

List the tools, call `greet` with a name, read the result. One rule to internalize while you're here: **never `console.log` in a stdio server** — stdout is the protocol channel, and stray prints corrupt it. Log to stderr.

Then connect it to a real client:

```sh
claude mcp add greeter -- npx -y tsx /absolute/path/to/src/index.ts
```

## Distribute it

A server nobody can install isn't finished. The conventions, so users can run yours with the one-liners every client config expects:

- **npm:** publish with a `bin` entry so `npx -y your-package` just works. Naming convention: `mcp-server-<thing>`, or scoped `@yourorg/mcp-<thing>`.
- **PyPI:** declare a script entry point so `uvx your-package` launches it. Convention: `mcp-server-<thing>`.
- **README:** lead with the copy-paste config block for Claude, Cursor and VS Code, and an explicit list of required environment variables. That README is your setup UI.
- **Registry:** list it in the official MCP registry so clients and directories can discover it.

## Where to go deeper

This page stops where the real docs begin. For resources and prompts, streamable HTTP and hosting, OAuth, sessions, and the rest of the surface:

- <https://modelcontextprotocol.io> — the spec and concept docs
- <https://ts.sdk.modelcontextprotocol.io> — TypeScript SDK server guide, transports, API reference
- <https://py.sdk.modelcontextprotocol.io> — the same for Python

And before you ship anything that holds credentials, read the [MCP security guide](https://mcpyet.com/guides/mcp-security.md) from the server author's side of the table: scoped keys, no secrets in tool arguments, and the knowledge that whatever your tools return goes straight into someone's model context.

## Questions

**Which language should I build an MCP server in?**
TypeScript or Python — the two SDKs maintained by the MCP project itself. Official SDKs also exist for Kotlin, Java, C#, Go, Ruby, Rust, Swift and PHP, but the TypeScript and Python ones get features first and have the most examples to crib from.

**What's the difference between @modelcontextprotocol/sdk and @modelcontextprotocol/server?**
Versioning. v1 shipped as one monolithic package, @modelcontextprotocol/sdk. v2 — the current stable line, implementing the 2026-07-28 spec — splits it into @modelcontextprotocol/server, @modelcontextprotocol/client and framework adapters. New servers should start on v2; most tutorials you'll find online still show v1.

**Do I need to implement resources and prompts too?**
No. Tools alone make a useful server, and most servers ship nothing else. Resources (readable data) and prompts (reusable templates) are worth adding when a client you care about actually surfaces them.

**stdio or HTTP — which transport should I ship?**
Start with stdio: it's what every desktop client runs, and the transport is one import. Add streamable HTTP when you want a hosted server multiple users reach over the network — that's also the point where the spec's OAuth requirements apply.

**How do users' API keys reach my server?**
For local servers, through environment variables declared in the client's config — read process.env at startup and exit with a clear error if the key is missing. Never make users paste keys into tool arguments; that puts secrets into the model's context.

Related guides: [MCP Inspector](https://mcpyet.com/guides/mcp-inspector.md) · [MCP security](https://mcpyet.com/guides/mcp-security.md) · [What is MCP?](https://mcpyet.com/what-is-mcp.md)

---

Source: https://mcpyet.com/guides/build-mcp-server/ — data refreshed 2026-08-11
