Merge pull request #546 from fredericbarthelet/register-completions-capabilities

fix: Ensure completions capability is registered on setCompletionRequestHandler call
This commit is contained in:
ihrpr 2025-06-10 13:21:12 +01:00 • committed by GitHub
commit 2cf4f0ca86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 160 additions and 198 deletions

View file

@ -913,18 +913,10 @@ describe("tool()", () => {
name: "test server",
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.tool(
"test",
@ -1056,17 +1048,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
// Register a tool with outputSchema
mcpServer.registerTool(
@ -1169,17 +1154,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
// Register a tool with outputSchema that returns only content without structuredContent
mcpServer.registerTool(
@ -1233,17 +1211,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
// Register a tool with outputSchema that returns invalid data
mcpServer.registerTool(
@ -1308,17 +1279,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
let receivedSessionId: string | undefined;
mcpServer.tool("test-tool", async (extra) => {
@ -1364,17 +1328,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
let receivedRequestId: string | number | undefined;
mcpServer.tool("request-id-test", async (extra) => {
@ -1423,17 +1380,10 @@ describe("tool()", () => {
{ capabilities: { logging: {} } },
);
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
let receivedLogMessage: string | undefined;
const loggingMessage = "hello here is log message 1";
@ -1480,17 +1430,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.tool(
"test",
@ -1546,17 +1489,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.tool("error-test", async () => {
throw new Error("Tool execution failed");
@ -1598,17 +1534,10 @@ describe("tool()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
tools: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.tool("test-tool", async () => ({
content: [
@ -2392,6 +2321,48 @@ describe("resource()", () => {
).rejects.toThrow(/Resource test:\/\/nonexistent not found/);
});
/***
* Test: Registering a resource template with a complete callback should update server capabilities to advertise support for completion
*/
test("should advertise support for completion when a resource template with 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,
complete: {
category: () => ["books", "movies", "music"],
},
}),
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()).toMatchObject({ completions: {} })
})
/***
* Test: Resource Template Parameter Completion
*/
@ -2401,17 +2372,10 @@ describe("resource()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
resources: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.resource(
"test",
@ -2469,17 +2433,10 @@ describe("resource()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
resources: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.resource(
"test",
@ -2540,17 +2497,10 @@ describe("resource()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
resources: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
let receivedRequestId: string | number | undefined;
mcpServer.resource("request-id-test", "test://resource", async (_uri, extra) => {
@ -3052,17 +3002,10 @@ describe("prompt()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
prompts: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.prompt(
"test",
@ -3258,17 +3201,10 @@ describe("prompt()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
prompts: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.prompt("test-prompt", async () => ({
messages: [
@ -3303,6 +3239,49 @@ describe("prompt()", () => {
).rejects.toThrow(/Prompt nonexistent-prompt not found/);
});
/***
* Test: Registering a prompt with a completable argument should update server capabilities to advertise support for completion
*/
test("should advertise support for completion when a prompt with 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: completable(z.string(), () => ["Alice", "Bob", "Charlie"]),
},
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),
]);
expect(client.getServerCapabilities()).toMatchObject({ completions: {} })
})
/***
* Test: Prompt Argument Completion
*/
@ -3312,17 +3291,10 @@ describe("prompt()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
prompts: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.prompt(
"test-prompt",
@ -3380,17 +3352,10 @@ describe("prompt()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
prompts: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
mcpServer.prompt(
"test-prompt",
@ -3450,17 +3415,10 @@ describe("prompt()", () => {
version: "1.0",
});
const client = new Client(
{
name: "test client",
version: "1.0",
},
{
capabilities: {
prompts: {},
},
},
);
const client = new Client({
name: "test client",
version: "1.0",
});
let receivedRequestId: string | number | undefined;
mcpServer.prompt("request-id-test", async (extra) => {

View file

@ -236,6 +236,10 @@ export class McpServer {
CompleteRequestSchema.shape.method.value,
);
this.server.registerCapabilities({
completions: {},
});
this.server.setRequestHandler(
CompleteRequestSchema,
async (request): Promise<CompleteResult> => {