Skip to content

Commit fde9748

Browse files
Merge branch 'main' into fweinberger/fix-transport-restart
2 parents db137d9 + babaa50 commit fde9748

24 files changed

Lines changed: 1054 additions & 53 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/fastify': minor
3+
---
4+
5+
Add Fastify middleware adapter for MCP servers, following the same pattern as the Express and Hono adapters.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/server': patch
3+
---
4+
5+
fix(server): propagate negotiated protocol version to transport in _oninitialize

.changeset/pre.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"mode": "pre",
3+
"tag": "alpha",
4+
"initialVersions": {
5+
"@modelcontextprotocol/eslint-config": "2.0.0",
6+
"@modelcontextprotocol/tsconfig": "2.0.0",
7+
"@modelcontextprotocol/vitest-config": "2.0.0",
8+
"@modelcontextprotocol/examples-client": "2.0.0-alpha.0",
9+
"@modelcontextprotocol/examples-client-quickstart": "2.0.0-alpha.0",
10+
"@modelcontextprotocol/examples-server": "2.0.0-alpha.0",
11+
"@modelcontextprotocol/examples-server-quickstart": "2.0.0-alpha.0",
12+
"@modelcontextprotocol/examples-shared": "2.0.0-alpha.0",
13+
"@modelcontextprotocol/client": "2.0.0-alpha.0",
14+
"@modelcontextprotocol/core": "2.0.0-alpha.0",
15+
"@modelcontextprotocol/express": "2.0.0-alpha.0",
16+
"@modelcontextprotocol/fastify": "2.0.0-alpha.0",
17+
"@modelcontextprotocol/hono": "2.0.0-alpha.0",
18+
"@modelcontextprotocol/node": "2.0.0-alpha.0",
19+
"@modelcontextprotocol/server": "2.0.0-alpha.0",
20+
"@modelcontextprotocol/test-conformance": "2.0.0-alpha.0",
21+
"@modelcontextprotocol/test-helpers": "2.0.0-alpha.0",
22+
"@modelcontextprotocol/test-integration": "2.0.0-alpha.0"
23+
},
24+
"changesets": []
25+
}

.github/workflows/main.yml

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
- main
55
pull_request:
66
workflow_dispatch:
7-
release:
8-
types: [published]
97

108
concurrency:
119
group: ${{ github.workflow }}-${{ github.ref }}
@@ -94,53 +92,3 @@ jobs:
9492
- name: Run ${{ matrix.runtime }} integration tests
9593
run: pnpm --filter @modelcontextprotocol/test-integration test:integration:${{ matrix.runtime }}
9694

97-
publish:
98-
runs-on: ubuntu-latest
99-
if: github.event_name == 'release'
100-
environment: release
101-
needs: [build, test, test-runtimes]
102-
103-
permissions:
104-
contents: read
105-
id-token: write
106-
107-
steps:
108-
- uses: actions/checkout@v6
109-
110-
- name: Install pnpm
111-
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
112-
id: pnpm-install
113-
with:
114-
run_install: false
115-
- uses: actions/setup-node@v6
116-
with:
117-
node-version: 24
118-
cache: pnpm
119-
cache-dependency-path: pnpm-lock.yaml
120-
registry-url: 'https://registry.npmjs.org'
121-
- run: pnpm install
122-
123-
- name: Determine npm tag
124-
id: npm-tag
125-
run: |
126-
VERSION=$(node -p "require('./package.json').version")
127-
# Check if this is a beta release
128-
if [[ "$VERSION" == *"-beta"* ]]; then
129-
echo "tag=--tag beta" >> $GITHUB_OUTPUT
130-
# Check if this release is from a non-primary branch (patch/maintenance release)
131-
elif [[ "${{ github.event.release.target_commitish }}" != "main" && "${{ github.event.release.target_commitish }}" != "v1.x" ]]; then
132-
# Use "release-X.Y" as tag for old branch releases (e.g., "release-1.23" for 1.23.x)
133-
# npm tags are mutable pointers to versions (like "latest" pointing to 1.24.3).
134-
# Using "release-1.23" means users can `npm install @modelcontextprotocol/sdk@release-1.23`
135-
# to get the latest patch on that minor version, and the tag updates if we
136-
# release 1.23.2, 1.23.3, etc.
137-
# Note: Can't use "v1.23" because npm rejects tags that look like semver ranges.
138-
MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2)
139-
echo "tag=--tag release-${MAJOR_MINOR}" >> $GITHUB_OUTPUT
140-
else
141-
echo "tag=" >> $GITHUB_OUTPUT
142-
fi
143-
144-
- run: pnpm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
145-
env:
146-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ jobs:
4040
- name: Publish preview packages
4141
run:
4242
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
43-
'./packages/middleware/express' './packages/middleware/hono' './packages/middleware/node'
43+
'./packages/middleware/express' './packages/middleware/fastify' './packages/middleware/hono' './packages/middleware/node'

packages/client/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# `@modelcontextprotocol/client`
2+
3+
The MCP (Model Context Protocol) TypeScript client SDK. Build MCP clients that connect to MCP servers.
4+
5+
> [!WARNING] **This is an alpha release.** Expect breaking changes until v2 stabilizes. We're publishing early to gather feedback — please try it and open issues — but we can't guarantee API stability yet. We'll aim to minimize disruption between alphas.
6+
7+
> [!NOTE] This is **v2** of the MCP TypeScript SDK. It replaces the monolithic `@modelcontextprotocol/sdk` package from v1. See the **[migration guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration.md)** if you're coming from v1.
8+
9+
## Install
10+
11+
```bash
12+
npm install @modelcontextprotocol/client@alpha
13+
```
14+
15+
## Documentation
16+
17+
- **[Repository README](https://github.com/modelcontextprotocol/typescript-sdk#readme)** — overview, package layout, examples
18+
- **[Client guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/client.md)**
19+
- **[API reference](https://ts.sdk.modelcontextprotocol.io/v2/)**
20+
- **[MCP specification](https://modelcontextprotocol.io)**
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# `@modelcontextprotocol/fastify`
2+
3+
Fastify adapters for the MCP TypeScript server SDK.
4+
5+
This package is a thin Fastify integration layer for [`@modelcontextprotocol/server`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/packages/server).
6+
7+
It does **not** implement MCP itself. Instead, it helps you:
8+
9+
- create a Fastify app with sensible defaults for MCP servers
10+
- add DNS rebinding protection via Host header validation (recommended for localhost servers)
11+
12+
## Install
13+
14+
```bash
15+
npm install @modelcontextprotocol/server @modelcontextprotocol/fastify fastify
16+
17+
# For MCP Streamable HTTP over Node.js (IncomingMessage/ServerResponse):
18+
npm install @modelcontextprotocol/node
19+
```
20+
21+
## Exports
22+
23+
- `createMcpFastifyApp(options?)`
24+
- `hostHeaderValidation(allowedHostnames)`
25+
- `localhostHostValidation()`
26+
27+
## Usage
28+
29+
### Create a Fastify app (localhost DNS rebinding protection by default)
30+
31+
```ts
32+
import { createMcpFastifyApp } from '@modelcontextprotocol/fastify';
33+
34+
const app = createMcpFastifyApp(); // default host is 127.0.0.1; protection enabled
35+
```
36+
37+
### Streamable HTTP endpoint (Fastify)
38+
39+
```ts
40+
import { createMcpFastifyApp } from '@modelcontextprotocol/fastify';
41+
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
42+
import { McpServer } from '@modelcontextprotocol/server';
43+
44+
const app = createMcpFastifyApp();
45+
const mcpServer = new McpServer({ name: 'my-server', version: '1.0.0' });
46+
47+
app.post('/mcp', async (request, reply) => {
48+
// Stateless example: create a transport per request.
49+
// For stateful mode (sessions), keep a transport instance around and reuse it.
50+
const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
51+
await mcpServer.connect(transport);
52+
53+
// Clean up when the client closes the connection (e.g. during SSE streaming).
54+
reply.raw.on('close', () => {
55+
transport.close();
56+
});
57+
58+
await transport.handleRequest(request.raw, reply.raw, request.body);
59+
});
60+
```
61+
62+
If you create a new `McpServer` per request in stateless mode, also call `mcpServer.close()` in the `close` handler. To reject non-POST requests with 405 Method Not Allowed, add routes for GET and DELETE that send a JSON-RPC error response.
63+
64+
### Host header validation (DNS rebinding protection)
65+
66+
```ts
67+
import { hostHeaderValidation } from '@modelcontextprotocol/fastify';
68+
69+
app.addHook('onRequest', hostHeaderValidation(['localhost', '127.0.0.1', '[::1]']));
70+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @ts-check
2+
3+
import baseConfig from '@modelcontextprotocol/eslint-config';
4+
5+
export default [
6+
...baseConfig,
7+
{
8+
settings: {
9+
'import/internal-regex': '^@modelcontextprotocol/(server|core)'
10+
}
11+
}
12+
];
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "@modelcontextprotocol/fastify",
3+
"private": false,
4+
"version": "2.0.0-alpha.0",
5+
"description": "Fastify adapters for the Model Context Protocol TypeScript server SDK - Fastify middleware",
6+
"license": "MIT",
7+
"author": "Anthropic, PBC (https://anthropic.com)",
8+
"homepage": "https://modelcontextprotocol.io",
9+
"bugs": "https://github.com/modelcontextprotocol/typescript-sdk/issues",
10+
"type": "module",
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/modelcontextprotocol/typescript-sdk.git"
14+
},
15+
"engines": {
16+
"node": ">=20",
17+
"pnpm": ">=10.24.0"
18+
},
19+
"packageManager": "pnpm@10.24.0",
20+
"keywords": [
21+
"modelcontextprotocol",
22+
"mcp",
23+
"fastify",
24+
"middleware"
25+
],
26+
"exports": {
27+
".": {
28+
"types": "./dist/index.d.mts",
29+
"import": "./dist/index.mjs"
30+
}
31+
},
32+
"files": [
33+
"dist"
34+
],
35+
"scripts": {
36+
"typecheck": "tsgo -p tsconfig.json --noEmit",
37+
"build": "tsdown",
38+
"build:watch": "tsdown --watch",
39+
"prepack": "npm run build",
40+
"lint": "eslint src/ && prettier --ignore-path ../../../.prettierignore --check .",
41+
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../../.prettierignore --write .",
42+
"check": "npm run typecheck && npm run lint",
43+
"test": "vitest run",
44+
"test:watch": "vitest"
45+
},
46+
"dependencies": {},
47+
"peerDependencies": {
48+
"@modelcontextprotocol/server": "workspace:^",
49+
"fastify": "catalog:runtimeServerOnly"
50+
},
51+
"devDependencies": {
52+
"@modelcontextprotocol/server": "workspace:^",
53+
"@modelcontextprotocol/tsconfig": "workspace:^",
54+
"@modelcontextprotocol/vitest-config": "workspace:^",
55+
"@modelcontextprotocol/eslint-config": "workspace:^",
56+
"@eslint/js": "catalog:devTools",
57+
"@typescript/native-preview": "catalog:devTools",
58+
"eslint": "catalog:devTools",
59+
"eslint-config-prettier": "catalog:devTools",
60+
"eslint-plugin-n": "catalog:devTools",
61+
"prettier": "catalog:devTools",
62+
"tsdown": "catalog:devTools",
63+
"typescript": "catalog:devTools",
64+
"typescript-eslint": "catalog:devTools",
65+
"vitest": "catalog:devTools"
66+
}
67+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Type-checked examples for `fastify.ts`.
3+
*
4+
* These examples are synced into JSDoc comments via the sync-snippets script.
5+
* Each function's region markers define the code snippet that appears in the docs.
6+
*
7+
* @module
8+
*/
9+
10+
import { createMcpFastifyApp } from './fastify.js';
11+
12+
/**
13+
* Example: Basic usage with default DNS rebinding protection.
14+
*/
15+
function createMcpFastifyApp_default() {
16+
//#region createMcpFastifyApp_default
17+
const app = createMcpFastifyApp();
18+
//#endregion createMcpFastifyApp_default
19+
return app;
20+
}
21+
22+
/**
23+
* Example: Custom host binding with and without DNS rebinding protection.
24+
*/
25+
function createMcpFastifyApp_customHost() {
26+
//#region createMcpFastifyApp_customHost
27+
const appOpen = createMcpFastifyApp({ host: '0.0.0.0' }); // No automatic DNS rebinding protection
28+
const appLocal = createMcpFastifyApp({ host: 'localhost' }); // DNS rebinding protection enabled
29+
//#endregion createMcpFastifyApp_customHost
30+
return { appOpen, appLocal };
31+
}
32+
33+
/**
34+
* Example: Custom allowed hosts for non-localhost binding.
35+
*/
36+
function createMcpFastifyApp_allowedHosts() {
37+
//#region createMcpFastifyApp_allowedHosts
38+
const app = createMcpFastifyApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });
39+
//#endregion createMcpFastifyApp_allowedHosts
40+
return app;
41+
}

0 commit comments

Comments
 (0)