Skip to content

Commit aba1d39

Browse files
feat(compat): widen completable() constraint to StandardSchemaV1
v1 accepted any zod schema (AnySchema). v2 narrowed to StandardSchemaWithJSON, which requires ~standard.jsonSchema. completable() itself only needs validate (to type the callback's value param); JSON-Schema generation happens at the outer registerPrompt argsSchema level, not per-field. Widening to StandardSchemaV1 restores the v1 surface (any Standard Schema lib, zod >=3.24). Adds raw-shape registerPrompt test with a completable() field.
1 parent 3155be7 commit aba1d39

2 files changed

Lines changed: 29 additions & 9 deletions

File tree

packages/server/src/server/completable.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import type { StandardSchemaWithJSON } from '@modelcontextprotocol/core';
1+
import type { StandardSchemaV1 } from '@modelcontextprotocol/core';
22

33
export const COMPLETABLE_SYMBOL: unique symbol = Symbol.for('mcp.completable');
44

5-
export type CompleteCallback<T extends StandardSchemaWithJSON = StandardSchemaWithJSON> = (
6-
value: StandardSchemaWithJSON.InferInput<T>,
5+
export type CompleteCallback<T extends StandardSchemaV1 = StandardSchemaV1> = (
6+
value: StandardSchemaV1.InferInput<T>,
77
context?: {
88
arguments?: Record<string, string>;
99
}
10-
) => StandardSchemaWithJSON.InferInput<T>[] | Promise<StandardSchemaWithJSON.InferInput<T>[]>;
10+
) => StandardSchemaV1.InferInput<T>[] | Promise<StandardSchemaV1.InferInput<T>[]>;
1111

12-
export type CompletableMeta<T extends StandardSchemaWithJSON = StandardSchemaWithJSON> = {
12+
export type CompletableMeta<T extends StandardSchemaV1 = StandardSchemaV1> = {
1313
complete: CompleteCallback<T>;
1414
};
1515

16-
export type CompletableSchema<T extends StandardSchemaWithJSON> = T & {
16+
export type CompletableSchema<T extends StandardSchemaV1> = T & {
1717
[COMPLETABLE_SYMBOL]: CompletableMeta<T>;
1818
};
1919

@@ -48,7 +48,7 @@ export type CompletableSchema<T extends StandardSchemaWithJSON> = T & {
4848
*
4949
* @see {@linkcode server/mcp.McpServer.registerPrompt | McpServer.registerPrompt} for using completable schemas in prompt argument definitions
5050
*/
51-
export function completable<T extends StandardSchemaWithJSON>(schema: T, complete: CompleteCallback<T>): CompletableSchema<T> {
51+
export function completable<T extends StandardSchemaV1>(schema: T, complete: CompleteCallback<T>): CompletableSchema<T> {
5252
Object.defineProperty(schema as object, COMPLETABLE_SYMBOL, {
5353
value: { complete } as CompletableMeta<T>,
5454
enumerable: false,
@@ -61,14 +61,14 @@ export function completable<T extends StandardSchemaWithJSON>(schema: T, complet
6161
/**
6262
* Checks if a schema is completable (has completion metadata).
6363
*/
64-
export function isCompletable(schema: unknown): schema is CompletableSchema<StandardSchemaWithJSON> {
64+
export function isCompletable(schema: unknown): schema is CompletableSchema<StandardSchemaV1> {
6565
return !!schema && typeof schema === 'object' && COMPLETABLE_SYMBOL in (schema as object);
6666
}
6767

6868
/**
6969
* Gets the completer callback from a completable schema, if it exists.
7070
*/
71-
export function getCompleter<T extends StandardSchemaWithJSON>(schema: T): CompleteCallback<T> | undefined {
71+
export function getCompleter<T extends StandardSchemaV1>(schema: T): CompleteCallback<T> | undefined {
7272
const meta = (schema as unknown as { [COMPLETABLE_SYMBOL]?: CompletableMeta<T> })[COMPLETABLE_SYMBOL];
7373
return meta?.complete as CompleteCallback<T> | undefined;
7474
}

packages/server/test/server/mcp.compat.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { InMemoryTransport, isStandardSchema, LATEST_PROTOCOL_VERSION } from '@m
33
import { describe, expect, it, vi } from 'vitest';
44
import * as z from 'zod/v4';
55
import { McpServer } from '../../src/index.js';
6+
import { completable } from '../../src/server/completable.js';
67

78
describe('registerTool/registerPrompt accept raw Zod shape (auto-wrapped)', () => {
89
it('registerTool accepts a raw shape for inputSchema and auto-wraps it', () => {
@@ -56,6 +57,25 @@ describe('registerTool/registerPrompt accept raw Zod shape (auto-wrapped)', () =
5657
expect(isStandardSchema(prompts['p']?.argsSchema)).toBe(true);
5758
});
5859

60+
it('registerPrompt raw shape accepts completable() fields (v1 pattern)', () => {
61+
const server = new McpServer({ name: 't', version: '1.0.0' });
62+
63+
server.registerPrompt(
64+
'p',
65+
{
66+
argsSchema: {
67+
language: completable(z.string(), v => ['typescript', 'python'].filter(l => l.startsWith(v)))
68+
}
69+
},
70+
async ({ language }) => ({
71+
messages: [{ role: 'user' as const, content: { type: 'text' as const, text: language } }]
72+
})
73+
);
74+
75+
const prompts = (server as unknown as { _registeredPrompts: Record<string, { argsSchema?: unknown }> })._registeredPrompts;
76+
expect(isStandardSchema(prompts['p']?.argsSchema)).toBe(true);
77+
});
78+
5979
it('callback receives validated, typed args end-to-end via tools/call', async () => {
6080
const server = new McpServer({ name: 't', version: '1.0.0' });
6181

0 commit comments

Comments
 (0)