Skip to content

Commit 2237555

Browse files
Typecheck the three test workspace packages, and fix the registerPrompt typing defect they were hiding (#2841)
Signed-off-by: Sharvil Saxena <sharvil.saxena@gmail.com> Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
1 parent 5238fba commit 2237555

17 files changed

Lines changed: 113 additions & 43 deletions
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+
`McpServer.registerPrompt()` now types the callback correctly when no `argsSchema` is given: its one parameter is the server context. Before, reading `ctx.mcpReq` there was a type error although it worked at runtime. Prompts registered with an `argsSchema` are unchanged.

‎packages/server/src/server/mcp.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,19 @@ export class McpServer {
10821082
* );
10831083
* ```
10841084
*/
1085+
registerPrompt(
1086+
name: string,
1087+
config: {
1088+
title?: string;
1089+
description?: string;
1090+
argsSchema?: undefined;
1091+
icons?: Icon[];
1092+
/** Determines whether this prompt retrieval needs an OAuth scope challenge. */
1093+
scopeChallenge?: ScopeChallengeHandler;
1094+
_meta?: Record<string, unknown>;
1095+
},
1096+
cb: PromptCallback
1097+
): RegisteredPrompt;
10851098
registerPrompt<Args extends StandardSchemaWithJSON>(
10861099
name: string,
10871100
config: {
@@ -1118,7 +1131,7 @@ export class McpServer {
11181131
scopeChallenge?: ScopeChallengeHandler;
11191132
_meta?: Record<string, unknown>;
11201133
},
1121-
cb: PromptCallback<StandardSchemaWithJSON> | LegacyPromptCallback<ZodRawShape>
1134+
cb: PromptCallback | PromptCallback<StandardSchemaWithJSON> | LegacyPromptCallback<ZodRawShape>
11221135
): RegisteredPrompt {
11231136
if (this._registeredPrompts[name]) {
11241137
throw new Error(`Prompt ${name} is already registered`);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Pins the types of registerPrompt() without argsSchema: the callback's one parameter is the server context.
2+
import type { ServerContext } from '@modelcontextprotocol/core-internal';
3+
import { describe, expect, expectTypeOf, test } from 'vitest';
4+
5+
import { McpServer } from '../../src/server/mcp';
6+
7+
describe('registerPrompt without argsSchema', () => {
8+
test('types the callback parameter as the server context', () => {
9+
const server = new McpServer({ name: 'test server', version: '1.0' });
10+
11+
server.registerPrompt('ctx-only', {}, async ctx => {
12+
expectTypeOf(ctx).toEqualTypeOf<ServerContext>();
13+
return { messages: [{ role: 'assistant' as const, content: { type: 'text' as const, text: String(ctx.mcpReq.id) } }] };
14+
});
15+
16+
expect(server.server).toBeDefined();
17+
});
18+
19+
test('still accepts a callback that ignores the context', () => {
20+
const server = new McpServer({ name: 'test server', version: '1.0' });
21+
22+
server.registerPrompt('no-args', { description: 'takes nothing' }, async () => ({
23+
messages: [{ role: 'assistant' as const, content: { type: 'text' as const, text: 'ok' } }]
24+
}));
25+
26+
expect(server.server).toBeDefined();
27+
});
28+
});

‎pnpm-lock.yaml‎

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/conformance/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"mcp"
2323
],
2424
"scripts": {
25+
"typecheck": "tsgo -p tsconfig.json --noEmit",
2526
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
2627
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
2728
"check": "npm run typecheck && npm run lint",
@@ -51,6 +52,7 @@
5152
"cors": "catalog:runtimeServerOnly",
5253
"express": "catalog:runtimeServerOnly",
5354
"tsx": "catalog:devTools",
55+
"@typescript/native-preview": "catalog:devTools",
5456
"zod": "catalog:runtimeShared"
5557
}
5658
}

‎test/conformance/src/everythingServer.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ function createMcpServer() {
200200
inputSchema: fromJsonSchema<{ region?: string; level?: number }>({
201201
type: 'object',
202202
properties: {
203-
region: { type: 'string', description: 'mirrored into Mcp-Param-Region', 'x-mcp-header': 'Region' },
203+
region: { type: 'string', description: 'mirrored into Mcp-Param-Region', 'x-mcp-header': 'Region' } as Record<
204+
string,
205+
unknown
206+
>,
204207
level: { type: 'integer', description: 'non-mirrored argument' }
205208
}
206209
})

‎test/conformance/tsconfig.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
"./node_modules/@modelcontextprotocol/core-internal/src/exports/public/index.ts"
1414
],
1515
"@modelcontextprotocol/client": ["./node_modules/@modelcontextprotocol/client/src/index.ts"],
16+
"@modelcontextprotocol/client/_shims": ["./node_modules/@modelcontextprotocol/client/src/shimsNode.ts"],
1617
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
18+
"@modelcontextprotocol/server/_shims": ["./node_modules/@modelcontextprotocol/server/src/shimsNode.ts"],
1719
"@modelcontextprotocol/express": ["./node_modules/@modelcontextprotocol/express/src/index.ts"],
1820
"@modelcontextprotocol/node": ["./node_modules/@modelcontextprotocol/node/src/index.ts"],
1921
"@modelcontextprotocol/vitest-config": ["./node_modules/@modelcontextprotocol/vitest-config/tsconfig.json"],

‎test/helpers/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"mcp"
2323
],
2424
"scripts": {
25+
"typecheck": "tsgo -p tsconfig.json --noEmit",
2526
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
2627
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
2728
"check": "npm run typecheck && npm run lint"
@@ -32,6 +33,7 @@
3233
"vitest": "catalog:devTools",
3334
"@modelcontextprotocol/tsconfig": "workspace:^",
3435
"@modelcontextprotocol/vitest-config": "workspace:^",
35-
"@modelcontextprotocol/eslint-config": "workspace:^"
36+
"@modelcontextprotocol/eslint-config": "workspace:^",
37+
"@typescript/native-preview": "catalog:devTools"
3638
}
3739
}

‎test/integration/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"mcp"
2323
],
2424
"scripts": {
25+
"typecheck": "tsgo -p tsconfig.json --noEmit",
2526
"lint": "eslint test/ && prettier --ignore-path ../../.prettierignore --check .",
2627
"lint:fix": "eslint test/ --fix && prettier --ignore-path ../../.prettierignore --write .",
2728
"check": "npm run typecheck && npm run lint",
@@ -42,6 +43,7 @@
4243
"@modelcontextprotocol/tsconfig": "workspace:^",
4344
"@modelcontextprotocol/vitest-config": "workspace:^",
4445
"@valibot/to-json-schema": "catalog:devTools",
46+
"@typescript/native-preview": "catalog:devTools",
4547
"arktype": "catalog:devTools",
4648
"supertest": "catalog:devTools",
4749
"valibot": "catalog:devTools",

‎test/integration/test/client/client.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ test('should initialize with matching protocol version', async () => {
7373
* Test: Initialize with Supported Older Protocol Version
7474
*/
7575
test('should initialize with supported older protocol version', async () => {
76-
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1];
76+
// Index 1 is always present; `noUncheckedIndexedAccess` cannot see that.
77+
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1]!;
7778
const clientTransport: Transport = {
7879
start: vi.fn().mockResolvedValue(undefined),
7980
close: vi.fn().mockResolvedValue(undefined),
@@ -277,7 +278,8 @@ test('should reject unsupported protocol version', async () => {
277278
* Test: Connect New Client to Old Supported Server Version
278279
*/
279280
test('should connect new client to old, supported server version', async () => {
280-
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1];
281+
// Index 1 is always present; `noUncheckedIndexedAccess` cannot see that.
282+
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1]!;
281283
const server = new Server(
282284
{
283285
name: 'test server',
@@ -725,7 +727,7 @@ test('should only allow setRequestHandler for declared capabilities', () => {
725727

726728
// This should throw because roots listing is not a declared capability
727729
expect(() => {
728-
client.setRequestHandler('roots/list', () => ({}));
730+
client.setRequestHandler('roots/list', () => ({ roots: [] }));
729731
}).toThrow('Client does not support roots capability');
730732
});
731733

0 commit comments

Comments
 (0)