Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-fastify-middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modelcontextprotocol/fastify': minor
---

Add Fastify middleware adapter for MCP servers, following the same pattern as the Express and Hono adapters.
5 changes: 5 additions & 0 deletions .changeset/fix-server-protocol-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modelcontextprotocol/server': patch
---

fix(server): propagate negotiated protocol version to transport in _oninitialize
25 changes: 25 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"mode": "pre",
"tag": "alpha",
"initialVersions": {
"@modelcontextprotocol/eslint-config": "2.0.0",
"@modelcontextprotocol/tsconfig": "2.0.0",
"@modelcontextprotocol/vitest-config": "2.0.0",
"@modelcontextprotocol/examples-client": "2.0.0-alpha.0",
"@modelcontextprotocol/examples-client-quickstart": "2.0.0-alpha.0",
"@modelcontextprotocol/examples-server": "2.0.0-alpha.0",
"@modelcontextprotocol/examples-server-quickstart": "2.0.0-alpha.0",
"@modelcontextprotocol/examples-shared": "2.0.0-alpha.0",
"@modelcontextprotocol/client": "2.0.0-alpha.0",
"@modelcontextprotocol/core": "2.0.0-alpha.0",
"@modelcontextprotocol/express": "2.0.0-alpha.0",
"@modelcontextprotocol/fastify": "2.0.0-alpha.0",
"@modelcontextprotocol/hono": "2.0.0-alpha.0",
"@modelcontextprotocol/node": "2.0.0-alpha.0",
"@modelcontextprotocol/server": "2.0.0-alpha.0",
"@modelcontextprotocol/test-conformance": "2.0.0-alpha.0",
"@modelcontextprotocol/test-helpers": "2.0.0-alpha.0",
"@modelcontextprotocol/test-integration": "2.0.0-alpha.0"
},
"changesets": []
}
52 changes: 0 additions & 52 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ on:
- main
pull_request:
workflow_dispatch:
release:
types: [published]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -94,53 +92,3 @@ jobs:
- name: Run ${{ matrix.runtime }} integration tests
run: pnpm --filter @modelcontextprotocol/test-integration test:integration:${{ matrix.runtime }}

publish:
runs-on: ubuntu-latest
if: github.event_name == 'release'
environment: release
needs: [build, test, test-runtimes]

permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v6

- name: Install pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
id: pnpm-install
with:
run_install: false
- uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
registry-url: 'https://registry.npmjs.org'
- run: pnpm install

- name: Determine npm tag
id: npm-tag
run: |
VERSION=$(node -p "require('./package.json').version")
# Check if this is a beta release
if [[ "$VERSION" == *"-beta"* ]]; then
echo "tag=--tag beta" >> $GITHUB_OUTPUT
# Check if this release is from a non-primary branch (patch/maintenance release)
elif [[ "${{ github.event.release.target_commitish }}" != "main" && "${{ github.event.release.target_commitish }}" != "v1.x" ]]; then
# Use "release-X.Y" as tag for old branch releases (e.g., "release-1.23" for 1.23.x)
# npm tags are mutable pointers to versions (like "latest" pointing to 1.24.3).
# Using "release-1.23" means users can `npm install @modelcontextprotocol/sdk@release-1.23`
# to get the latest patch on that minor version, and the tag updates if we
# release 1.23.2, 1.23.3, etc.
# Note: Can't use "v1.23" because npm rejects tags that look like semver ranges.
MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2)
echo "tag=--tag release-${MAJOR_MINOR}" >> $GITHUB_OUTPUT
else
echo "tag=" >> $GITHUB_OUTPUT
fi

- run: pnpm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ jobs:
- name: Publish preview packages
run:
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
'./packages/middleware/express' './packages/middleware/hono' './packages/middleware/node'
'./packages/middleware/express' './packages/middleware/fastify' './packages/middleware/hono' './packages/middleware/node'
20 changes: 20 additions & 0 deletions packages/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `@modelcontextprotocol/client`

The MCP (Model Context Protocol) TypeScript client SDK. Build MCP clients that connect to MCP servers.

> [!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.

> [!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.

## Install

```bash
npm install @modelcontextprotocol/client@alpha
```

## Documentation

- **[Repository README](https://github.com/modelcontextprotocol/typescript-sdk#readme)** — overview, package layout, examples
- **[Client guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/client.md)**
- **[API reference](https://ts.sdk.modelcontextprotocol.io/v2/)**
- **[MCP specification](https://modelcontextprotocol.io)**
70 changes: 70 additions & 0 deletions packages/middleware/fastify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# `@modelcontextprotocol/fastify`

Fastify adapters for the MCP TypeScript server SDK.

This package is a thin Fastify integration layer for [`@modelcontextprotocol/server`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/packages/server).

It does **not** implement MCP itself. Instead, it helps you:

- create a Fastify app with sensible defaults for MCP servers
- add DNS rebinding protection via Host header validation (recommended for localhost servers)

## Install

```bash
npm install @modelcontextprotocol/server @modelcontextprotocol/fastify fastify

# For MCP Streamable HTTP over Node.js (IncomingMessage/ServerResponse):
npm install @modelcontextprotocol/node
```

## Exports

- `createMcpFastifyApp(options?)`
- `hostHeaderValidation(allowedHostnames)`
- `localhostHostValidation()`

## Usage

### Create a Fastify app (localhost DNS rebinding protection by default)

```ts
import { createMcpFastifyApp } from '@modelcontextprotocol/fastify';

const app = createMcpFastifyApp(); // default host is 127.0.0.1; protection enabled
```

### Streamable HTTP endpoint (Fastify)

```ts
import { createMcpFastifyApp } from '@modelcontextprotocol/fastify';
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
import { McpServer } from '@modelcontextprotocol/server';

const app = createMcpFastifyApp();
const mcpServer = new McpServer({ name: 'my-server', version: '1.0.0' });

app.post('/mcp', async (request, reply) => {
// Stateless example: create a transport per request.
// For stateful mode (sessions), keep a transport instance around and reuse it.
const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await mcpServer.connect(transport);

// Clean up when the client closes the connection (e.g. during SSE streaming).
reply.raw.on('close', () => {
transport.close();
});

await transport.handleRequest(request.raw, reply.raw, request.body);
});
```

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.

### Host header validation (DNS rebinding protection)

```ts
import { hostHeaderValidation } from '@modelcontextprotocol/fastify';

app.addHook('onRequest', hostHeaderValidation(['localhost', '127.0.0.1', '[::1]']));
```
12 changes: 12 additions & 0 deletions packages/middleware/fastify/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @ts-check

import baseConfig from '@modelcontextprotocol/eslint-config';

export default [
...baseConfig,
{
settings: {
'import/internal-regex': '^@modelcontextprotocol/(server|core)'
}
}
];
67 changes: 67 additions & 0 deletions packages/middleware/fastify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "@modelcontextprotocol/fastify",
"private": false,
"version": "2.0.0-alpha.0",
"description": "Fastify adapters for the Model Context Protocol TypeScript server SDK - Fastify middleware",
"license": "MIT",
"author": "Anthropic, PBC (https://anthropic.com)",
"homepage": "https://modelcontextprotocol.io",
"bugs": "https://github.com/modelcontextprotocol/typescript-sdk/issues",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/modelcontextprotocol/typescript-sdk.git"
},
"engines": {
"node": ">=20",
"pnpm": ">=10.24.0"
},
"packageManager": "pnpm@10.24.0",
"keywords": [
"modelcontextprotocol",
"mcp",
"fastify",
"middleware"
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
}
},
"files": [
"dist"
],
"scripts": {
"typecheck": "tsgo -p tsconfig.json --noEmit",
"build": "tsdown",
"build:watch": "tsdown --watch",
"prepack": "npm run build",
"lint": "eslint src/ && prettier --ignore-path ../../../.prettierignore --check .",
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../../.prettierignore --write .",
"check": "npm run typecheck && npm run lint",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {},
"peerDependencies": {
"@modelcontextprotocol/server": "workspace:^",
"fastify": "catalog:runtimeServerOnly"
},
"devDependencies": {
"@modelcontextprotocol/server": "workspace:^",
"@modelcontextprotocol/tsconfig": "workspace:^",
"@modelcontextprotocol/vitest-config": "workspace:^",
"@modelcontextprotocol/eslint-config": "workspace:^",
"@eslint/js": "catalog:devTools",
"@typescript/native-preview": "catalog:devTools",
"eslint": "catalog:devTools",
"eslint-config-prettier": "catalog:devTools",
"eslint-plugin-n": "catalog:devTools",
"prettier": "catalog:devTools",
"tsdown": "catalog:devTools",
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools"
}
}
41 changes: 41 additions & 0 deletions packages/middleware/fastify/src/fastify.examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Type-checked examples for `fastify.ts`.
*
* These examples are synced into JSDoc comments via the sync-snippets script.
* Each function's region markers define the code snippet that appears in the docs.
*
* @module
*/

import { createMcpFastifyApp } from './fastify.js';

/**
* Example: Basic usage with default DNS rebinding protection.
*/
function createMcpFastifyApp_default() {
//#region createMcpFastifyApp_default
const app = createMcpFastifyApp();
//#endregion createMcpFastifyApp_default
return app;
}

/**
* Example: Custom host binding with and without DNS rebinding protection.
*/
function createMcpFastifyApp_customHost() {
//#region createMcpFastifyApp_customHost
const appOpen = createMcpFastifyApp({ host: '0.0.0.0' }); // No automatic DNS rebinding protection
const appLocal = createMcpFastifyApp({ host: 'localhost' }); // DNS rebinding protection enabled
//#endregion createMcpFastifyApp_customHost
return { appOpen, appLocal };
}

/**
* Example: Custom allowed hosts for non-localhost binding.
*/
function createMcpFastifyApp_allowedHosts() {
//#region createMcpFastifyApp_allowedHosts
const app = createMcpFastifyApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });
//#endregion createMcpFastifyApp_allowedHosts
return app;
}
Loading
Loading