This commit is contained in:
parent
f60dff0674
commit
f4137630c0
4 changed files with 933 additions and 207 deletions
|
|
@ -128,9 +128,12 @@ describe('createMcpHandler — modern path', () => {
|
|||
postRequest({ jsonrpc: '2.0', id: 5, method: 'server/discover', params: { _meta: ENVELOPE } })
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
const body = (await response.json()) as { result: { supportedVersions: string[]; serverInfo: { name: string } } };
|
||||
const body = (await response.json()) as {
|
||||
result: { supportedVersions: string[]; _meta: Record<string, { name: string }> };
|
||||
};
|
||||
expect(body.result.supportedVersions).toEqual([MODERN_REVISION]);
|
||||
expect(body.result.serverInfo.name).toBe('entry-test-server');
|
||||
// #3002: identity in the result `_meta`, never the body.
|
||||
expect(body.result._meta['io.modelcontextprotocol/serverInfo']!.name).toBe('entry-test-server');
|
||||
});
|
||||
|
||||
it('backfills the deprecated accessors and the negotiated revision from the validated envelope (per-request instance state)', async () => {
|
||||
|
|
@ -309,7 +312,10 @@ describe('createMcpHandler — modern path', () => {
|
|||
expect(response.status).toBe(400);
|
||||
const body = (await response.json()) as JSONRPCErrorBody;
|
||||
expect(body.error.code).toBe(-32_602);
|
||||
expect(JSON.stringify(body.error.data)).toContain('clientInfo');
|
||||
// clientCapabilities is the missing REQUIRED key; clientInfo is a
|
||||
// SHOULD since spec PR #3002 and its absence is never an error.
|
||||
expect(JSON.stringify(body.error.data)).toContain('clientCapabilities');
|
||||
expect(JSON.stringify(body.error.data)).not.toContain('clientInfo');
|
||||
expect(body.id).toBe(1);
|
||||
expect(state.contexts).toHaveLength(0);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -208,7 +208,16 @@ describe('createMcpHandler — subscriptions/listen', () => {
|
|||
expect(resultFrame).toEqual({
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
result: { resultType: 'complete', _meta: { 'io.modelcontextprotocol/subscriptionId': 1 } }
|
||||
result: {
|
||||
resultType: 'complete',
|
||||
_meta: {
|
||||
'io.modelcontextprotocol/subscriptionId': 1,
|
||||
// #3002: the close result carries the serving instance's
|
||||
// identity like every other result (SubscriptionsListenResultMeta
|
||||
// extends ResultMetaObject).
|
||||
'io.modelcontextprotocol/serverInfo': { name: 'listen-test-server', version: '1.0.0' }
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import {
|
|||
isJSONRPCResultResponse,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
PROTOCOL_VERSION_META_KEY,
|
||||
SERVER_INFO_META_KEY,
|
||||
setNegotiatedProtocolVersion,
|
||||
SUPPORTED_PROTOCOL_VERSIONS
|
||||
} from '@modelcontextprotocol/core-internal';
|
||||
|
|
@ -99,12 +100,39 @@ describe('server/discover handler gating', () => {
|
|||
if (isJSONRPCResultResponse(response)) {
|
||||
const result = DiscoverResultSchema.parse(response.result);
|
||||
expect(result.supportedVersions).toEqual([MODERN]);
|
||||
expect(result.serverInfo).toEqual({ name: 'modern-server', version: '2.0.0' });
|
||||
// #3002: identity travels in the result `_meta`, never the body.
|
||||
expect(result._meta?.[SERVER_INFO_META_KEY]).toEqual({ name: 'modern-server', version: '2.0.0' });
|
||||
expect('serverInfo' in (response.result as Record<string, unknown>)).toBe(false);
|
||||
expect(result.instructions).toBe('hello');
|
||||
}
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('serves discover for a client that omits clientInfo (SHOULD since spec PR #3002)', async () => {
|
||||
const server = new Server(
|
||||
{ name: 'modern-server', version: '2.0.0' },
|
||||
{ capabilities: { tools: {} }, supportedProtocolVersions: DUAL_ERA_VERSIONS }
|
||||
);
|
||||
const withoutClientInfo: JSONRPCRequest = {
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'server/discover',
|
||||
params: {
|
||||
_meta: {
|
||||
[PROTOCOL_VERSION_META_KEY]: MODERN,
|
||||
[CLIENT_CAPABILITIES_META_KEY]: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
const response = await sendRaw(server, withoutClientInfo, { markModern: true });
|
||||
expect(isJSONRPCResultResponse(response)).toBe(true);
|
||||
if (isJSONRPCResultResponse(response)) {
|
||||
const result = DiscoverResultSchema.parse(response.result);
|
||||
expect(result.supportedVersions).toEqual([MODERN]);
|
||||
}
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('a modern-era instance whose supported list carries no modern revision still answers -32601 (handler not installed)', async () => {
|
||||
const server = new Server({ name: 'test', version: '1.0.0' }, { capabilities: {} });
|
||||
const response = await sendRaw(server, discoverRequest, { markModern: true });
|
||||
|
|
|
|||
|
|
@ -121,9 +121,12 @@ describe('serveStdio — subscriptions/listen', () => {
|
|||
// the wire closes. No notifications/cancelled (the pre-#2953 path).
|
||||
const results = inbound.filter(m => 'result' in m) as { id: unknown; result: unknown }[];
|
||||
expect(results.map(m => m.id)).toEqual(['s1', 's2']);
|
||||
// #3002: each close result carries the serving instance's identity
|
||||
// (SubscriptionsListenResultMeta extends ResultMetaObject).
|
||||
const serverInfo = { name: 's', version: '1' };
|
||||
expect(results.map(m => m.result)).toEqual([
|
||||
{ resultType: 'complete', _meta: { [SUBSCRIPTION_ID_META_KEY]: 's1' } },
|
||||
{ resultType: 'complete', _meta: { [SUBSCRIPTION_ID_META_KEY]: 's2' } }
|
||||
{ resultType: 'complete', _meta: { [SUBSCRIPTION_ID_META_KEY]: 's1', 'io.modelcontextprotocol/serverInfo': serverInfo } },
|
||||
{ resultType: 'complete', _meta: { [SUBSCRIPTION_ID_META_KEY]: 's2', 'io.modelcontextprotocol/serverInfo': serverInfo } }
|
||||
]);
|
||||
expect(inbound.some(m => (m as JSONRPCNotification).method === 'notifications/cancelled')).toBe(false);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue