docs: correctness and coverage pass for the 2026-07-28 revision (#2389)

This commit is contained in:
Felix Weinberger 2026-06-29 18:44:25 +01:00 • committed by GitHub
commit 199a28e9e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -88,13 +88,9 @@ Update your `package.json` to add `type: "module"` and a build script:
```json
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": ["build"]
"build": "tsc"
}
}
```
@ -129,17 +125,11 @@ Add these to the top of your `src/index.ts`:
```ts source="../examples/server-quickstart/src/index.ts#prelude"
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
const NWS_API_BASE = 'https://api.weather.gov';
const USER_AGENT = 'weather-app/1.0';
// Create server instance
const server = new McpServer({
name: 'weather',
version: '1.0.0',
});
```
### Helper functions
@ -217,149 +207,156 @@ interface ForecastResponse {
### Registering tools
Each tool is registered with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#registerTool | server.registerTool()}, which takes the tool name, a configuration object (with description and input schema), and a callback that implements the tool logic. Let's register our two weather tools:
Each tool is registered with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#registerTool | server.registerTool()}, which takes the tool name, a configuration object (with description and input schema), and a callback that implements the tool logic. Create the server inside a `createServer()` factory and register both weather tools on it. The serving entry in the next step builds the instance it serves by calling this factory, so keep it cheap and side-effect-free:
```ts source="../examples/server-quickstart/src/index.ts#registerTools"
// Register weather tools
server.registerTool(
'get-alerts',
{
title: 'Get Weather Alerts',
description: 'Get weather alerts for a state',
inputSchema: z.object({
state: z.string().length(2)
.describe('Two-letter state code (e.g. CA, NY)'),
}),
},
async ({ state }) => {
const stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);
// Create a server with both weather tools registered. The serving entry calls
// this factory to build the instance it serves, so keep it cheap and
// side-effect-free.
function createServer(): McpServer {
const server = new McpServer({
name: 'weather',
version: '1.0.0',
});
server.registerTool(
'get-alerts',
{
title: 'Get Weather Alerts',
description: 'Get weather alerts for a state',
inputSchema: z.object({
state: z.string().length(2)
.describe('Two-letter state code (e.g. CA, NY)'),
}),
},
async ({ state }) => {
const stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);
if (!alertsData) {
return {
content: [{
type: 'text',
text: 'Failed to retrieve alerts data',
}],
isError: true,
};
}
const features = alertsData.features || [];
if (features.length === 0) {
return {
content: [{
type: 'text',
text: `No active alerts for ${stateCode}`,
}],
};
}
const formattedAlerts = features.map(formatAlert);
if (!alertsData) {
return {
content: [{
type: 'text' as const,
text: 'Failed to retrieve alerts data',
type: 'text',
text: `Active alerts for ${stateCode}:\n\n${formattedAlerts.join('\n')}`,
}],
};
}
},
);
const features = alertsData.features || [];
server.registerTool(
'get-forecast',
{
title: 'Get Weather Forecast',
description: 'Get weather forecast for a location',
inputSchema: z.object({
latitude: z.number().min(-90).max(90)
.describe('Latitude of the location'),
longitude: z.number().min(-180).max(180)
.describe('Longitude of the location'),
}),
},
async ({ latitude, longitude }) => {
// Get grid point data
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);
if (!pointsData) {
return {
content: [{
type: 'text',
text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
}],
isError: true,
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [{
type: 'text',
text: 'Failed to get forecast URL from grid point data',
}],
isError: true,
};
}
// Get forecast data
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [{
type: 'text',
text: 'Failed to retrieve forecast data',
}],
isError: true,
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [{
type: 'text',
text: 'No forecast periods available',
}],
};
}
// Format forecast periods
const formattedForecast = periods.map((period: ForecastPeriod) =>
[
`${period.name || 'Unknown'}:`,
`Temperature: ${period.temperature || 'Unknown'}°${period.temperatureUnit || 'F'}`,
`Wind: ${period.windSpeed || 'Unknown'} ${period.windDirection || ''}`,
`${period.shortForecast || 'No forecast available'}`,
'---',
].join('\n'),
);
if (features.length === 0) {
return {
content: [{
type: 'text' as const,
text: `No active alerts for ${stateCode}`,
type: 'text',
text: `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join('\n')}`,
}],
};
}
},
);
const formattedAlerts = features.map(formatAlert);
return {
content: [{
type: 'text' as const,
text: `Active alerts for ${stateCode}:\n\n${formattedAlerts.join('\n')}`,
}],
};
},
);
server.registerTool(
'get-forecast',
{
title: 'Get Weather Forecast',
description: 'Get weather forecast for a location',
inputSchema: z.object({
latitude: z.number().min(-90).max(90)
.describe('Latitude of the location'),
longitude: z.number().min(-180).max(180)
.describe('Longitude of the location'),
}),
},
async ({ latitude, longitude }) => {
// Get grid point data
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);
if (!pointsData) {
return {
content: [{
type: 'text' as const,
text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
}],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [{
type: 'text' as const,
text: 'Failed to get forecast URL from grid point data',
}],
};
}
// Get forecast data
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [{
type: 'text' as const,
text: 'Failed to retrieve forecast data',
}],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [{
type: 'text' as const,
text: 'No forecast periods available',
}],
};
}
// Format forecast periods
const formattedForecast = periods.map((period: ForecastPeriod) =>
[
`${period.name || 'Unknown'}:`,
`Temperature: ${period.temperature || 'Unknown'}°${period.temperatureUnit || 'F'}`,
`Wind: ${period.windSpeed || 'Unknown'} ${period.windDirection || ''}`,
`${period.shortForecast || 'No forecast available'}`,
'---',
].join('\n'),
);
return {
content: [{
type: 'text' as const,
text: `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join('\n')}`,
}],
};
},
);
return server;
}
```
### Running the server
Finally, implement the main function to run the server:
Finally, serve the factory on stdio with {@linkcode @modelcontextprotocol/server!server/serveStdio.serveStdio | serveStdio}. The entry owns the transport and negotiates the protocol revision with each client, so the same factory serves current hosts (such as VS Code) and clients that speak the 2026-07-28 draft revision; see [Serving the 2026-07-28 draft revision on stdio](./server.md#serving-the-2026-07-28-draft-revision-on-stdio) in the server guide for the options:
```ts source="../examples/server-quickstart/src/index.ts#main"
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Weather MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error in main():', error);
process.exit(1);
});
void serveStdio(createServer);
console.error('Weather MCP Server running on stdio');
```
> [!IMPORTANT]