docs: rebuild the v2 documentation around a task-based page tree (#2397)
This commit is contained in:
parent
708d5455d9
commit
b153505428
1 changed files with 11884 additions and 4971 deletions
136
docs/get-started/first-server.md
Normal file
136
docs/get-started/first-server.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
status: calibration
|
||||
shape: tutorial
|
||||
---
|
||||
|
||||
# Build your first server
|
||||
|
||||
Build an MCP **server** — a program that exposes tools a model can call — and call its one tool, a US weather-alert lookup, from a client.
|
||||
|
||||
## Set up the project
|
||||
|
||||
You need Node.js 20 or later and nothing else. Create the project and install the SDK.
|
||||
|
||||
```sh
|
||||
mkdir weather && cd weather
|
||||
npm init -y
|
||||
npm pkg set type=module
|
||||
npm install @modelcontextprotocol/server zod tsx
|
||||
mkdir src
|
||||
```
|
||||
|
||||
`type=module` matters — the SDK ships ES modules only. `tsx` runs TypeScript directly, so there is no build step.
|
||||
|
||||
## Register a tool
|
||||
|
||||
Create `src/index.ts`: a `createServer` factory that builds an `McpServer` and registers one **tool** — a function the connected model can call.
|
||||
|
||||
```ts source="../../examples/guides/get-started/firstServer.examples.ts#firstServer_registerTool"
|
||||
import { McpServer } from '@modelcontextprotocol/server';
|
||||
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
||||
import * as z from 'zod/v4';
|
||||
|
||||
const NWS_API = 'https://api.weather.gov';
|
||||
|
||||
interface AlertsResponse {
|
||||
features: { properties: { event?: string; headline?: string } }[];
|
||||
}
|
||||
|
||||
function createServer(): McpServer {
|
||||
const server = new McpServer({ name: 'weather', version: '1.0.0' });
|
||||
|
||||
server.registerTool(
|
||||
'get-alerts',
|
||||
{
|
||||
description: 'Get the active weather alerts for a US state',
|
||||
inputSchema: z.object({
|
||||
state: z.string().length(2).describe('Two-letter US state code, e.g. CA')
|
||||
})
|
||||
},
|
||||
async ({ state }) => {
|
||||
const code = state.toUpperCase();
|
||||
const url = `${NWS_API}/alerts/active?area=${code}`;
|
||||
const res = await fetch(url, { headers: { 'User-Agent': 'mcp-weather-tutorial/1.0' } });
|
||||
if (!res.ok) {
|
||||
return { content: [{ type: 'text', text: `NWS API error: HTTP ${res.status}` }], isError: true };
|
||||
}
|
||||
const { features } = (await res.json()) as AlertsResponse;
|
||||
if (features.length === 0) {
|
||||
return { content: [{ type: 'text', text: `No active alerts for ${code}.` }] };
|
||||
}
|
||||
const lines = features.map(f => f.properties.headline ?? f.properties.event ?? 'Unnamed alert');
|
||||
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
||||
}
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
```
|
||||
|
||||
`registerTool` takes a name, a config, and an async handler. `inputSchema` is a Zod schema — the only schema you write. From that one schema the SDK derives the JSON Schema the model sees, validates arguments before your handler runs, and infers the handler's argument types.
|
||||
|
||||
The handler returns **content**, a list of typed blocks — one `text` block here. `isError: true` marks a failed result the model can read and react to.
|
||||
|
||||
::: tip
|
||||
Call `get-alerts` with `{ "state": "California" }` and the SDK rejects it before your handler runs. The result is the failure the model sees:
|
||||
|
||||
```text
|
||||
Input validation error: Invalid arguments for tool get-alerts: state: Too big: expected string to have <=2 characters
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Serve it over stdio
|
||||
|
||||
At the end of the file, hand the factory to `serveStdio`.
|
||||
|
||||
```ts source="../../examples/guides/get-started/firstServer.examples.ts#firstServer_serve"
|
||||
void serveStdio(createServer);
|
||||
console.error('weather MCP server running on stdio');
|
||||
```
|
||||
|
||||
`serveStdio` owns the **stdio transport**: it reads requests on stdin, writes responses to stdout, and calls `createServer` to build the instance that serves the connection.
|
||||
|
||||
::: warning
|
||||
stdout is the protocol channel. Log with `console.error` — one `console.log` corrupts the JSON-RPC stream.
|
||||
:::
|
||||
|
||||
## Run it
|
||||
|
||||
Start the server from the project root.
|
||||
|
||||
```sh
|
||||
npx tsx src/index.ts
|
||||
```
|
||||
|
||||
The banner lands on stderr, leaving stdout for the protocol:
|
||||
|
||||
```text
|
||||
weather MCP server running on stdio
|
||||
```
|
||||
|
||||
Nothing else happens: an stdio server waits on stdin for a client to start the conversation. Stop it with `Ctrl+C`.
|
||||
|
||||
## Call the tool
|
||||
|
||||
The **MCP Inspector** is a local web app for calling a server's tools directly — it launches the command you give it and connects over stdio.
|
||||
|
||||
```sh
|
||||
npx @modelcontextprotocol/inspector npx tsx src/index.ts
|
||||
```
|
||||
|
||||
In the browser tab it opens, click **Connect**, open the **Tools** tab, select `get-alerts`, enter a two-letter state code such as `TX`, and run it. The text block in the result lists each active alert headline for that state — the same content a model receives when it calls your tool.
|
||||
|
||||
## Pick a transport
|
||||
|
||||
Your server speaks stdio because a host launches it as a local process and owns its lifetime. To host one endpoint that many clients connect to, serve the same `createServer` factory over [HTTP](../serving/http.md) instead.
|
||||
|
||||
Next on this path, [Plug into a real host](./real-host.md) registers this server in VS Code, Claude Code, and Cursor; [Tools](../servers/tools.md) goes deeper on what a tool can return.
|
||||
|
||||
## Recap
|
||||
|
||||
- `registerTool(name, config, handler)` registers a tool; `inputSchema` is the one Zod schema you write.
|
||||
- The SDK validates every call against that schema and rejects bad arguments before your handler runs.
|
||||
- `serveStdio(createServer)` builds the server from your factory and serves it on stdin/stdout.
|
||||
- stdout carries the protocol; log to stderr.
|
||||
- `npx @modelcontextprotocol/inspector <command>` exercises any stdio server without a host.
|
||||
Loading…
Reference in a new issue