No automatic completion support unless needed - Revisited yet again (#1237)
This commit is contained in:
parent
d5dba5484c
commit
a57303c335
2 changed files with 96 additions and 4 deletions
|
|
@ -2669,6 +2669,41 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
).rejects.toThrow(/Resource test:\/\/nonexistent not found/);
|
||||
});
|
||||
|
||||
/***
|
||||
* Test: Registering a resource template without a complete callback should not update server capabilities to advertise support for completion
|
||||
*/
|
||||
test('should not advertise support for completion when a resource template without a complete callback is defined', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
});
|
||||
const client = new Client({
|
||||
name: 'test client',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
mcpServer.resource(
|
||||
'test',
|
||||
new ResourceTemplate('test://resource/{category}', {
|
||||
list: undefined
|
||||
}),
|
||||
async () => ({
|
||||
contents: [
|
||||
{
|
||||
uri: 'test://resource/test',
|
||||
text: 'Test content'
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
||||
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
|
||||
|
||||
expect(client.getServerCapabilities()).not.toHaveProperty('completions');
|
||||
});
|
||||
|
||||
/***
|
||||
* Test: Registering a resource template with a complete callback should update server capabilities to advertise support for completion
|
||||
*/
|
||||
|
|
@ -3548,6 +3583,46 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
).rejects.toThrow(/Prompt nonexistent-prompt not found/);
|
||||
});
|
||||
|
||||
/***
|
||||
* Test: Registering a prompt without a completable argument should not update server capabilities to advertise support for completion
|
||||
*/
|
||||
test('should not advertise support for completion when a prompt without a completable argument is defined', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
});
|
||||
const client = new Client({
|
||||
name: 'test client',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
mcpServer.prompt(
|
||||
'test-prompt',
|
||||
{
|
||||
name: z.string()
|
||||
},
|
||||
async ({ name }) => ({
|
||||
messages: [
|
||||
{
|
||||
role: 'assistant',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `Hello ${name}`
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
||||
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
|
||||
|
||||
const capabilities = client.getServerCapabilities() || {};
|
||||
const keys = Object.keys(capabilities);
|
||||
expect(keys).not.toContain('completions');
|
||||
});
|
||||
|
||||
/***
|
||||
* Test: Registering a prompt with a completable argument should update server capabilities to advertise support for completion
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import { Transport } from '../shared/transport.js';
|
|||
import { validateAndWarnToolName } from '../shared/toolNameValidation.js';
|
||||
import { ExperimentalMcpServerTasks } from '../experimental/tasks/mcp-server.js';
|
||||
import type { ToolTaskHandler } from '../experimental/tasks/interfaces.js';
|
||||
import { ZodOptional } from 'zod';
|
||||
|
||||
/**
|
||||
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
|
||||
|
|
@ -557,8 +558,6 @@ export class McpServer {
|
|||
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
|
||||
});
|
||||
|
||||
this.setCompletionRequestHandler();
|
||||
|
||||
this._resourceHandlersInitialized = true;
|
||||
}
|
||||
|
||||
|
|
@ -623,8 +622,6 @@ export class McpServer {
|
|||
}
|
||||
});
|
||||
|
||||
this.setCompletionRequestHandler();
|
||||
|
||||
this._promptHandlersInitialized = true;
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +812,14 @@ export class McpServer {
|
|||
}
|
||||
};
|
||||
this._registeredResourceTemplates[name] = registeredResourceTemplate;
|
||||
|
||||
// If the resource template has any completion callbacks, enable completions capability
|
||||
const variableNames = template.uriTemplate.variableNames;
|
||||
const hasCompleter = Array.isArray(variableNames) && variableNames.some(v => !!template.completeCallback(v));
|
||||
if (hasCompleter) {
|
||||
this.setCompletionRequestHandler();
|
||||
}
|
||||
|
||||
return registeredResourceTemplate;
|
||||
}
|
||||
|
||||
|
|
@ -848,6 +853,18 @@ export class McpServer {
|
|||
}
|
||||
};
|
||||
this._registeredPrompts[name] = registeredPrompt;
|
||||
|
||||
// If any argument uses a Completable schema, enable completions capability
|
||||
if (argsSchema) {
|
||||
const hasCompletable = Object.values(argsSchema).some(field => {
|
||||
const inner: unknown = field instanceof ZodOptional ? field._def?.innerType : field;
|
||||
return isCompletable(inner);
|
||||
});
|
||||
if (hasCompletable) {
|
||||
this.setCompletionRequestHandler();
|
||||
}
|
||||
}
|
||||
|
||||
return registeredPrompt;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue