Skip to content

Commit a57303c

Browse files
authored
No automatic completion support unless needed - Revisited yet again (#1237)
1 parent d5dba54 commit a57303c

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

src/server/mcp.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,6 +2669,41 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
26692669
).rejects.toThrow(/Resource test:\/\/nonexistent not found/);
26702670
});
26712671

2672+
/***
2673+
* Test: Registering a resource template without a complete callback should not update server capabilities to advertise support for completion
2674+
*/
2675+
test('should not advertise support for completion when a resource template without a complete callback is defined', async () => {
2676+
const mcpServer = new McpServer({
2677+
name: 'test server',
2678+
version: '1.0'
2679+
});
2680+
const client = new Client({
2681+
name: 'test client',
2682+
version: '1.0'
2683+
});
2684+
2685+
mcpServer.resource(
2686+
'test',
2687+
new ResourceTemplate('test://resource/{category}', {
2688+
list: undefined
2689+
}),
2690+
async () => ({
2691+
contents: [
2692+
{
2693+
uri: 'test://resource/test',
2694+
text: 'Test content'
2695+
}
2696+
]
2697+
})
2698+
);
2699+
2700+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
2701+
2702+
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
2703+
2704+
expect(client.getServerCapabilities()).not.toHaveProperty('completions');
2705+
});
2706+
26722707
/***
26732708
* Test: Registering a resource template with a complete callback should update server capabilities to advertise support for completion
26742709
*/
@@ -3548,6 +3583,46 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
35483583
).rejects.toThrow(/Prompt nonexistent-prompt not found/);
35493584
});
35503585

3586+
/***
3587+
* Test: Registering a prompt without a completable argument should not update server capabilities to advertise support for completion
3588+
*/
3589+
test('should not advertise support for completion when a prompt without a completable argument is defined', async () => {
3590+
const mcpServer = new McpServer({
3591+
name: 'test server',
3592+
version: '1.0'
3593+
});
3594+
const client = new Client({
3595+
name: 'test client',
3596+
version: '1.0'
3597+
});
3598+
3599+
mcpServer.prompt(
3600+
'test-prompt',
3601+
{
3602+
name: z.string()
3603+
},
3604+
async ({ name }) => ({
3605+
messages: [
3606+
{
3607+
role: 'assistant',
3608+
content: {
3609+
type: 'text',
3610+
text: `Hello ${name}`
3611+
}
3612+
}
3613+
]
3614+
})
3615+
);
3616+
3617+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
3618+
3619+
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
3620+
3621+
const capabilities = client.getServerCapabilities() || {};
3622+
const keys = Object.keys(capabilities);
3623+
expect(keys).not.toContain('completions');
3624+
});
3625+
35513626
/***
35523627
* Test: Registering a prompt with a completable argument should update server capabilities to advertise support for completion
35533628
*/

src/server/mcp.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import { Transport } from '../shared/transport.js';
6262
import { validateAndWarnToolName } from '../shared/toolNameValidation.js';
6363
import { ExperimentalMcpServerTasks } from '../experimental/tasks/mcp-server.js';
6464
import type { ToolTaskHandler } from '../experimental/tasks/interfaces.js';
65+
import { ZodOptional } from 'zod';
6566

6667
/**
6768
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
@@ -557,8 +558,6 @@ export class McpServer {
557558
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
558559
});
559560

560-
this.setCompletionRequestHandler();
561-
562561
this._resourceHandlersInitialized = true;
563562
}
564563

@@ -623,8 +622,6 @@ export class McpServer {
623622
}
624623
});
625624

626-
this.setCompletionRequestHandler();
627-
628625
this._promptHandlersInitialized = true;
629626
}
630627

@@ -815,6 +812,14 @@ export class McpServer {
815812
}
816813
};
817814
this._registeredResourceTemplates[name] = registeredResourceTemplate;
815+
816+
// If the resource template has any completion callbacks, enable completions capability
817+
const variableNames = template.uriTemplate.variableNames;
818+
const hasCompleter = Array.isArray(variableNames) && variableNames.some(v => !!template.completeCallback(v));
819+
if (hasCompleter) {
820+
this.setCompletionRequestHandler();
821+
}
822+
818823
return registeredResourceTemplate;
819824
}
820825

@@ -848,6 +853,18 @@ export class McpServer {
848853
}
849854
};
850855
this._registeredPrompts[name] = registeredPrompt;
856+
857+
// If any argument uses a Completable schema, enable completions capability
858+
if (argsSchema) {
859+
const hasCompletable = Object.values(argsSchema).some(field => {
860+
const inner: unknown = field instanceof ZodOptional ? field._def?.innerType : field;
861+
return isCompletable(inner);
862+
});
863+
if (hasCompletable) {
864+
this.setCompletionRequestHandler();
865+
}
866+
}
867+
851868
return registeredPrompt;
852869
}
853870

0 commit comments

Comments
 (0)