Skip to content

Commit 0912ea1

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/user-agent
2 parents 050e63e + 2a55dfd commit 0912ea1

87 files changed

Lines changed: 19633 additions & 12011 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ on:
33
branches:
44
- main
55
pull_request:
6+
workflow_dispatch:
67
release:
78
types: [published]
89

@@ -18,19 +19,35 @@ jobs:
1819
- uses: actions/checkout@v4
1920
- uses: actions/setup-node@v4
2021
with:
21-
node-version: 18
22+
node-version: 24
2223
cache: npm
2324

2425
- run: npm ci
26+
- run: npm run check
2527
- run: npm run build
28+
29+
test:
30+
runs-on: ubuntu-latest
31+
strategy:
32+
fail-fast: false
33+
matrix:
34+
node-version: [18, 24]
35+
36+
steps:
37+
- uses: actions/checkout@v4
38+
- uses: actions/setup-node@v4
39+
with:
40+
node-version: ${{ matrix.node-version }}
41+
cache: npm
42+
43+
- run: npm ci
2644
- run: npm test
27-
- run: npm run lint
2845

2946
publish:
3047
runs-on: ubuntu-latest
3148
if: github.event_name == 'release'
3249
environment: release
33-
needs: build
50+
needs: [build, test]
3451

3552
permissions:
3653
contents: read
@@ -40,12 +57,22 @@ jobs:
4057
- uses: actions/checkout@v4
4158
- uses: actions/setup-node@v4
4259
with:
43-
node-version: 18
60+
node-version: 24
4461
cache: npm
4562
registry-url: 'https://registry.npmjs.org'
4663

4764
- run: npm ci
4865

49-
- run: npm publish --provenance --access public
66+
- name: Determine npm tag
67+
id: npm-tag
68+
run: |
69+
VERSION=$(node -p "require('./package.json').version")
70+
if [[ "$VERSION" == *"-beta"* ]]; then
71+
echo "tag=--tag beta" >> $GITHUB_OUTPUT
72+
else
73+
echo "tag=" >> $GITHUB_OUTPUT
74+
fi
75+
76+
- run: npm publish --provenance --access public ${{ steps.npm-tag.outputs.tag }}
5077
env:
5178
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.github/workflows/publish.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Publish Any Commit
2+
permissions:
3+
contents: read
4+
on:
5+
pull_request:
6+
push:
7+
branches:
8+
- '**'
9+
tags:
10+
- '!**'
11+
12+
jobs:
13+
pkg-publish:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-node@v4
19+
with:
20+
node-version: 24
21+
cache: npm
22+
23+
- run: npm ci
24+
- name: Build
25+
run: npm run build
26+
- name: Publish
27+
run: npx pkg-pr-new publish
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Update Spec Types
2+
3+
on:
4+
schedule:
5+
# Run nightly at 4 AM UTC
6+
- cron: '0 4 * * *'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
update-spec-types:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Setup Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '24'
24+
25+
- name: Install dependencies
26+
run: npm ci
27+
28+
- name: Fetch latest spec types
29+
run: npm run fetch:spec-types
30+
31+
- name: Check for changes
32+
id: check_changes
33+
run: |
34+
if git diff --quiet src/spec.types.ts; then
35+
echo "has_changes=false" >> $GITHUB_OUTPUT
36+
else
37+
echo "has_changes=true" >> $GITHUB_OUTPUT
38+
LATEST_SHA=$(grep "Last updated from commit:" src/spec.types.ts | cut -d: -f2 | tr -d ' ')
39+
echo "sha=$LATEST_SHA" >> $GITHUB_OUTPUT
40+
fi
41+
42+
- name: Create Pull Request
43+
if: steps.check_changes.outputs.has_changes == 'true'
44+
env:
45+
GH_TOKEN: ${{ github.token }}
46+
run: |
47+
git config user.name "github-actions[bot]"
48+
git config user.email "github-actions[bot]@users.noreply.github.com"
49+
50+
git checkout -B update-spec-types
51+
git add src/spec.types.ts
52+
git commit -m "chore: update spec.types.ts from upstream"
53+
git push -f origin update-spec-types
54+
55+
# Create PR if it doesn't exist, or update if it does
56+
PR_BODY="This PR updates \`src/spec.types.ts\` from the Model Context Protocol specification.
57+
58+
Source file: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/${{ steps.check_changes.outputs.sha }}/schema/draft/schema.ts
59+
60+
This is an automated update triggered by the nightly cron job."
61+
62+
if gh pr view update-spec-types &>/dev/null; then
63+
echo "PR already exists, updating description..."
64+
gh pr edit update-spec-types --body "$PR_BODY"
65+
else
66+
gh pr create \
67+
--title "chore: update spec.types.ts from upstream" \
68+
--body "$PR_BODY" \
69+
--base main \
70+
--head update-spec-types
71+
fi

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ web_modules/
6969
# Output of 'npm pack'
7070
*.tgz
7171

72-
# Output of 'npm run fetch:spec-types'
73-
spec.types.ts
74-
7572
# Yarn Integrity file
7673
.yarn-integrity
7774

@@ -133,3 +130,6 @@ out
133130

134131
.DS_Store
135132
dist/
133+
134+
# IDE
135+
.idea/

.prettierignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ node_modules
77
**/build
88
**/dist
99
.github/CODEOWNERS
10-
pnpm-lock.yaml
10+
pnpm-lock.yaml
11+
12+
# Ignore generated files
13+
src/spec.types.ts

README.md

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ The Model Context Protocol allows applications to provide context for LLMs in a
4747
## Installation
4848

4949
```bash
50-
npm install @modelcontextprotocol/sdk
50+
npm install @modelcontextprotocol/sdk zod
5151
```
5252

53+
This SDK has a **required peer dependency** on `zod` for schema validation. The SDK internally imports from `zod/v4`, but maintains backwards compatibility with projects using Zod v3.25 or later. You can use either API in your code by importing from `zod/v3` or `zod/v4`:
54+
5355
## Quick Start
5456

5557
Let's create a simple MCP server that exposes a calculator tool and some data. Save the following as `server.ts`:
@@ -58,7 +60,7 @@ Let's create a simple MCP server that exposes a calculator tool and some data. S
5860
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
5961
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
6062
import express from 'express';
61-
import { z } from 'zod';
63+
import * as z from 'zod/v4';
6264

6365
// Create an MCP server
6466
const server = new McpServer({
@@ -130,7 +132,7 @@ app.listen(port, () => {
130132
});
131133
```
132134

133-
Install the deps with `npm install @modelcontextprotocol/sdk express zod@3`, and run with `npx -y tsx server.ts`.
135+
Install the deps with `npm install @modelcontextprotocol/sdk express zod`, and run with `npx -y tsx server.ts`.
134136

135137
You can connect to it using any MCP client that supports streamable http, such as:
136138

@@ -477,7 +479,7 @@ MCP servers can request LLM completions from connected clients that support samp
477479
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
478480
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
479481
import express from 'express';
480-
import { z } from 'zod';
482+
import * as z from 'zod/v4';
481483

482484
const mcpServer = new McpServer({
483485
name: 'tools-with-sample-server',
@@ -561,7 +563,7 @@ For most use cases where session management isn't needed:
561563
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
562564
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
563565
import express from 'express';
564-
import { z } from 'zod';
566+
import * as z from 'zod/v4';
565567

566568
const app = express();
567569
app.use(express.json());
@@ -796,7 +798,7 @@ A simple server demonstrating resources, tools, and prompts:
796798

797799
```typescript
798800
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
799-
import { z } from 'zod';
801+
import * as z from 'zod/v4';
800802

801803
const server = new McpServer({
802804
name: 'echo-server',
@@ -866,7 +868,7 @@ A more complex example showing database integration:
866868
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
867869
import sqlite3 from 'sqlite3';
868870
import { promisify } from 'util';
869-
import { z } from 'zod';
871+
import * as z from 'zod/v4';
870872

871873
const server = new McpServer({
872874
name: 'sqlite-explorer',
@@ -961,7 +963,7 @@ If you want to offer an initial set of tools/prompts/resources, but later add ad
961963
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
962964
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
963965
import express from 'express';
964-
import { z } from 'zod';
966+
import * as z from 'zod/v4';
965967

966968
const server = new McpServer({
967969
name: 'Dynamic Example',
@@ -1175,7 +1177,7 @@ await server.connect(transport);
11751177

11761178
### Eliciting User Input
11771179

1178-
MCP servers can request additional information from users through the elicitation feature. This is useful for interactive workflows where the server needs user input or confirmation:
1180+
MCP servers can request non-sensitive information from users through the form elicitation capability. This is useful for interactive workflows where the server needs user input or confirmation:
11791181

11801182
```typescript
11811183
// Server-side: Restaurant booking tool that asks for alternatives
@@ -1208,6 +1210,7 @@ server.registerTool(
12081210
if (!available) {
12091211
// Ask user if they want to try alternative dates
12101212
const result = await server.server.elicitInput({
1213+
mode: 'form',
12111214
message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
12121215
requestedSchema: {
12131216
type: 'object',
@@ -1274,7 +1277,7 @@ server.registerTool(
12741277
);
12751278
```
12761279

1277-
Client-side: Handle elicitation requests
1280+
On the client side, handle form elicitation requests:
12781281

12791282
```typescript
12801283
// This is a placeholder - implement based on your UI framework
@@ -1299,7 +1302,85 @@ client.setRequestHandler(ElicitRequestSchema, async request => {
12991302
});
13001303
```
13011304

1302-
**Note**: Elicitation requires client support. Clients must declare the `elicitation` capability during initialization.
1305+
When calling `server.elicitInput`, prefer to explicitly set `mode: 'form'` for new code. Omitting the mode continues to work for backwards compatibility and defaults to form elicitation.
1306+
1307+
Elicitation is a client capability. Clients must declare the `elicitation` capability during initialization:
1308+
1309+
```typescript
1310+
const client = new Client(
1311+
{
1312+
name: 'example-client',
1313+
version: '1.0.0'
1314+
},
1315+
{
1316+
capabilities: {
1317+
elicitation: {
1318+
form: {}
1319+
}
1320+
}
1321+
}
1322+
);
1323+
```
1324+
1325+
**Note**: Form elicitation **must** only be used to gather non-sensitive information. For sensitive information such as API keys or secrets, use URL elicitation instead.
1326+
1327+
### Eliciting URL Actions
1328+
1329+
MCP servers can prompt the user to perform a URL-based action through URL elicitation. This is useful for securely gathering sensitive information such as API keys or secrets, or for redirecting users to secure web-based flows.
1330+
1331+
```typescript
1332+
// Server-side: Prompt the user to navigate to a URL
1333+
const result = await server.server.elicitInput({
1334+
mode: 'url',
1335+
message: 'Please enter your API key',
1336+
elicitationId: '550e8400-e29b-41d4-a716-446655440000',
1337+
url: 'http://localhost:3000/api-key'
1338+
});
1339+
1340+
// Alternative, return an error from within a tool:
1341+
throw new UrlElicitationRequiredError([
1342+
{
1343+
mode: 'url',
1344+
message: 'This tool requires a payment confirmation. Open the link to confirm payment!',
1345+
url: `http://localhost:${MCP_PORT}/confirm-payment?session=${sessionId}&elicitation=${elicitationId}&cartId=${encodeURIComponent(cartId)}`,
1346+
elicitationId: '550e8400-e29b-41d4-a716-446655440000'
1347+
}
1348+
]);
1349+
```
1350+
1351+
On the client side, handle URL elicitation requests:
1352+
1353+
```typescript
1354+
client.setRequestHandler(ElicitRequestSchema, async request => {
1355+
if (request.params.mode !== 'url') {
1356+
throw new McpError(ErrorCode.InvalidParams, `Unsupported elicitation mode: ${request.params.mode}`);
1357+
}
1358+
1359+
// At a minimum, implement a UI that:
1360+
// - Display the full URL and server reason to prevent phishing
1361+
// - Explicitly ask the user for consent, with clear decline/cancel options
1362+
// - Open the URL in the system (not embedded) browser
1363+
// Optionally, listen for a `nofifications/elicitation/complete` message from the server
1364+
});
1365+
```
1366+
1367+
Elicitation is a client capability. Clients must declare the `elicitation` capability during initialization:
1368+
1369+
```typescript
1370+
const client = new Client(
1371+
{
1372+
name: 'example-client',
1373+
version: '1.0.0'
1374+
},
1375+
{
1376+
capabilities: {
1377+
elicitation: {
1378+
url: {}
1379+
}
1380+
}
1381+
}
1382+
);
1383+
```
13031384

13041385
### Writing MCP Clients
13051386

eslint.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export default tseslint.config(
1515
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
1616
}
1717
},
18+
{
19+
ignores: ['src/spec.types.ts']
20+
},
1821
{
1922
files: ['src/client/**/*.ts', 'src/server/**/*.ts'],
2023
ignores: ['**/*.test.ts'],

0 commit comments

Comments
 (0)