From c90d6c9853ab64cf94c2d3d016f615ae4376fa89 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 14:11:10 +0100 Subject: [PATCH 1/6] add context with arguments to completable --- src/server/completable.ts | 3 + src/server/mcp.test.ts | 271 +++++++++++++++++++++++++++++++++++--- src/server/mcp.ts | 7 +- src/types.test.ts | 90 ++++++++++++- src/types.ts | 8 ++ 5 files changed, 357 insertions(+), 22 deletions(-) diff --git a/src/server/completable.ts b/src/server/completable.ts index 3b5bc1644..652eaf72e 100644 --- a/src/server/completable.ts +++ b/src/server/completable.ts @@ -15,6 +15,9 @@ export enum McpZodTypeKind { export type CompleteCallback = ( value: T["_input"], + context?: { + arguments?: Record; + }, ) => T["_input"][] | Promise; export interface CompletableDef diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 36a8f7b88..23600db45 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -3521,12 +3521,12 @@ describe("prompt()", () => { ); expect(result.resources).toHaveLength(2); - + // Resource 1 should have its own metadata expect(result.resources[0].name).toBe("Resource 1"); expect(result.resources[0].description).toBe("Individual resource description"); expect(result.resources[0].mimeType).toBe("text/plain"); - + // Resource 2 should inherit template metadata expect(result.resources[1].name).toBe("Resource 2"); expect(result.resources[1].description).toBe("Template description"); @@ -3592,7 +3592,7 @@ describe("prompt()", () => { ); expect(result.resources).toHaveLength(1); - + // All fields should be from the individual resource, not the template expect(result.resources[0].name).toBe("Overridden Name"); expect(result.resources[0].description).toBe("Overridden description"); @@ -3698,41 +3698,274 @@ describe("Tool title precedence", () => { }); test("getDisplayName unit tests for title precedence", () => { - + // Test 1: Only name expect(getDisplayName({ name: "tool_name" })).toBe("tool_name"); - + // Test 2: Name and title - title wins - expect(getDisplayName({ - name: "tool_name", - title: "Tool Title" + expect(getDisplayName({ + name: "tool_name", + title: "Tool Title" })).toBe("Tool Title"); - + // Test 3: Name and annotations.title - annotations.title wins - expect(getDisplayName({ + expect(getDisplayName({ name: "tool_name", annotations: { title: "Annotations Title" } })).toBe("Annotations Title"); - + // Test 4: All three - title wins (correct precedence) - expect(getDisplayName({ - name: "tool_name", + expect(getDisplayName({ + name: "tool_name", title: "Regular Title", annotations: { title: "Annotations Title" } })).toBe("Regular Title"); - + // Test 5: Empty title should not be used - expect(getDisplayName({ - name: "tool_name", + expect(getDisplayName({ + name: "tool_name", title: "", annotations: { title: "Annotations Title" } })).toBe("Annotations Title"); - + // Test 6: Undefined vs null handling - expect(getDisplayName({ - name: "tool_name", + expect(getDisplayName({ + name: "tool_name", title: undefined, annotations: { title: "Annotations Title" } })).toBe("Annotations Title"); }); + + test("should support resource template completion with resolved context", 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("github://repos/{owner}/{repo}", { + list: undefined, + complete: { + repo: (value, context) => { + if (context?.arguments?.["owner"] === "org1") { + return ["project1", "project2", "project3"].filter(r => r.startsWith(value)); + } else if (context?.arguments?.["owner"] === "org2") { + return ["repo1", "repo2", "repo3"].filter(r => r.startsWith(value)); + } + return []; + }, + }, + }), + async () => ({ + contents: [ + { + uri: "github://repos/test/test", + text: "Test content", + }, + ], + }), + ); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.server.connect(serverTransport), + ]); + + // Test with microsoft owner + const result1 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/resource", + uri: "github://repos/{owner}/{repo}", + }, + argument: { + name: "repo", + value: "p", + }, + context: { + arguments: { + owner: "org1", + }, + }, + }, + }, + CompleteResultSchema, + ); + + expect(result1.completion.values).toEqual(["project1", "project2", "project3"]); + expect(result1.completion.total).toBe(3); + + // Test with facebook owner + const result2 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/resource", + uri: "github://repos/{owner}/{repo}", + }, + argument: { + name: "repo", + value: "r", + }, + context: { + arguments: { + owner: "org2", + }, + }, + }, + }, + CompleteResultSchema, + ); + + expect(result2.completion.values).toEqual(["repo1", "repo2", "repo3"]); + expect(result2.completion.total).toBe(3); + + // Test with no resolved context + const result3 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/resource", + uri: "github://repos/{owner}/{repo}", + }, + argument: { + name: "repo", + value: "t", + }, + }, + }, + CompleteResultSchema, + ); + + expect(result3.completion.values).toEqual([]); + expect(result3.completion.total).toBe(0); + }); + + test("should support prompt argument completion with resolved context", 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(), (value, context) => { + if (context?.arguments?.["category"] === "developers") { + return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); + } else if (context?.arguments?.["category"] === "managers") { + return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); + } + return ["Guest"].filter(n => n.startsWith(value)); + }), + }, + 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), + ]); + + // Test with developers category + const result1 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/prompt", + name: "test-prompt", + }, + argument: { + name: "name", + value: "A", + }, + context: { + arguments: { + category: "developers", + }, + }, + }, + }, + CompleteResultSchema, + ); + + expect(result1.completion.values).toEqual(["Alice"]); + + // Test with managers category + const result2 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/prompt", + name: "test-prompt", + }, + argument: { + name: "name", + value: "D", + }, + context: { + arguments: { + category: "managers", + }, + }, + }, + }, + CompleteResultSchema, + ); + + expect(result2.completion.values).toEqual(["David"]); + + // Test with no resolved context + const result3 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/prompt", + name: "test-prompt", + }, + argument: { + name: "name", + value: "G", + }, + }, + }, + CompleteResultSchema, + ); + + expect(result3.completion.values).toEqual(["Guest"]); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index ac6cf7727..3d9673da7 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -293,7 +293,7 @@ export class McpServer { } const def: CompletableDef = field._def; - const suggestions = await def.complete(request.params.argument.value); + const suggestions = await def.complete(request.params.argument.value, request.params.context); return createCompletionResult(suggestions); } @@ -324,7 +324,7 @@ export class McpServer { return EMPTY_COMPLETION_RESULT; } - const suggestions = await completer(request.params.argument.value); + const suggestions = await completer(request.params.argument.value, request.params.context); return createCompletionResult(suggestions); } @@ -1068,6 +1068,9 @@ export class McpServer { */ export type CompleteResourceTemplateCallback = ( value: string, + context?: { + arguments?: Record; + }, ) => string[] | Promise; /** diff --git a/src/types.test.ts b/src/types.test.ts index d163f03d0..bc1091105 100644 --- a/src/types.test.ts +++ b/src/types.test.ts @@ -4,7 +4,8 @@ import { ResourceLinkSchema, ContentBlockSchema, PromptMessageSchema, - CallToolResultSchema + CallToolResultSchema, + CompleteRequestSchema } from "./types.js"; describe("Types", () => { @@ -223,4 +224,91 @@ describe("Types", () => { } }); }); + + describe("CompleteRequest", () => { + test("should validate a CompleteRequest without resolved field", () => { + const request = { + method: "completion/complete", + params: { + ref: { type: "ref/prompt", name: "greeting" }, + argument: { name: "name", value: "A" } + } + }; + + const result = CompleteRequestSchema.safeParse(request); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.method).toBe("completion/complete"); + expect(result.data.params.ref.type).toBe("ref/prompt"); + expect(result.data.params.context).toBeUndefined(); + } + }); + + test("should validate a CompleteRequest with resolved field", () => { + const request = { + method: "completion/complete", + params: { + ref: { type: "ref/resource", uri: "github://repos/{owner}/{repo}" }, + argument: { name: "repo", value: "t" }, + context: { + arguments: { + "{owner}": "microsoft" + } + } + } + }; + + const result = CompleteRequestSchema.safeParse(request); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.params.context?.arguments).toEqual({ + "{owner}": "microsoft" + }); + } + }); + + test("should validate a CompleteRequest with empty resolved field", () => { + const request = { + method: "completion/complete", + params: { + ref: { type: "ref/prompt", name: "test" }, + argument: { name: "arg", value: "" }, + context: { + arguments: {} + } + } + }; + + const result = CompleteRequestSchema.safeParse(request); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.params.context?.arguments).toEqual({}); + } + }); + + test("should validate a CompleteRequest with multiple resolved variables", () => { + const request = { + method: "completion/complete", + params: { + ref: { type: "ref/resource", uri: "api://v1/{tenant}/{resource}/{id}" }, + argument: { name: "id", value: "123" }, + context: { + arguments: { + "{tenant}": "acme-corp", + "{resource}": "users" + } + } + } + }; + + const result = CompleteRequestSchema.safeParse(request); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.params.context?.arguments).toEqual({ + "{tenant}": "acme-corp", + "{resource}": "users" + }); + } + }); + }); }); diff --git a/src/types.ts b/src/types.ts index 8e8b7d33e..1bc225919 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1169,6 +1169,14 @@ export const CompleteRequestSchema = RequestSchema.extend({ value: z.string(), }) .passthrough(), + context: z.optional( + z.object({ + /** + * Previously-resolved variables in a URI template or prompt. + */ + arguments: z.optional(z.record(z.string(), z.string())), + }) + ), }), }); From 42c3967d8b04f2a95550102967f65763719fceb5 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 14:23:17 +0100 Subject: [PATCH 2/6] add to readme --- README.md | 111 ++++++++++++++++++++++++++++++++++++++++- src/server/mcp.test.ts | 28 +++++++---- 2 files changed, 128 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ff6c482f5..63acfb065 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,33 @@ server.registerResource( }] }) ); + +// Resource with context-aware completion +server.registerResource( + "repository", + new ResourceTemplate("github://repos/{owner}/{repo}", { + list: undefined, + complete: { + // Provide intelligent completions based on previously resolved parameters + repo: (value, context) => { + if (context?.arguments?.["owner"] === "org1") { + return ["project1", "project2", "project3"].filter(r => r.startsWith(value)); + } + return ["default-repo"].filter(r => r.startsWith(value)); + } + } + }), + { + title: "GitHub Repository", + description: "Repository information" + }, + async (uri, { owner, repo }) => ({ + contents: [{ + uri: uri.href, + text: `Repository: ${owner}/${repo}` + }] + }) +); ``` ### Tools @@ -233,12 +260,14 @@ Tools can return `ResourceLink` objects to reference resources without embedding Prompts are reusable templates that help LLMs interact with your server effectively: ```typescript +import { completable } from "@modelcontextprotocol/sdk/server/completable.js"; + server.registerPrompt( "review-code", { title: "Code Review", description: "Review code for best practices and potential issues", - arguments: { code: z.string() } + argsSchema: { code: z.string() } }, ({ code }) => ({ messages: [{ @@ -250,6 +279,35 @@ server.registerPrompt( }] }) ); + +// Prompt with context-aware completion +server.registerPrompt( + "team-greeting", + { + title: "Team Greeting", + description: "Generate a greeting for team members", + argsSchema: { + // Completable arguments can use context for intelligent suggestions + name: completable(z.string(), (value, context) => { + if (context?.arguments?.["department"] === "engineering") { + return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); + } else if (context?.arguments?.["department"] === "sales") { + return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); + } + return ["Guest"].filter(n => n.startsWith(value)); + }) + } + }, + ({ name }) => ({ + messages: [{ + role: "assistant", + content: { + type: "text", + text: `Hello ${name}, welcome to the team!` + } + }] + }) +); ``` ### Display Names and Metadata @@ -637,6 +695,57 @@ server.registerTool( ## Advanced Usage +### Context-Aware Completions + +MCP supports intelligent completions that can use previously resolved values as context. This is useful for creating dependent parameter completions where later parameters depend on earlier ones: + +```typescript +import { completable } from "@modelcontextprotocol/sdk/server/completable.js"; + +// For resource templates +server.registerResource( + "database-query", + new ResourceTemplate("db://{database}/{table}/{query}", { + list: undefined, + complete: { + // Table completions depend on the selected database + table: (value, context) => { + const database = context?.arguments?.["database"]; + if (database === "users_db") { + return ["profiles", "sessions", "preferences"].filter(t => t.startsWith(value)); + } else if (database === "products_db") { + return ["items", "categories", "inventory"].filter(t => t.startsWith(value)); + } + return []; + } + } + }), + metadata, + handler +); + +// For prompts with completable arguments +server.registerPrompt( + "api-request", + { + argsSchema: { + endpoint: z.string(), + // Method completions can be context-aware + method: completable(z.string(), (value, context) => { + const endpoint = context?.arguments?.["endpoint"]; + if (endpoint?.includes("/readonly/")) { + return ["GET"].filter(m => m.startsWith(value.toUpperCase())); + } + return ["GET", "POST", "PUT", "DELETE"].filter(m => m.startsWith(value.toUpperCase())); + }) + } + }, + handler +); +``` + +The context object contains an `arguments` field with previously resolved parameter values, allowing you to provide more intelligent and contextual completions. + ### Dynamic Servers If you want to offer an initial set of tools/prompts/resources, but later add additional ones based on user action or external state change, you can add/update/remove them _after_ the Server is connected. This will automatically emit the corresponding `listChanged` notifications: diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 23600db45..685987bf7 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -3747,7 +3747,7 @@ describe("Tool title precedence", () => { version: "1.0", }); - mcpServer.resource( + mcpServer.registerResource( "test", new ResourceTemplate("github://repos/{owner}/{repo}", { list: undefined, @@ -3762,6 +3762,10 @@ describe("Tool title precedence", () => { }, }, }), + { + title: "GitHub Repository", + description: "Repository information" + }, async () => ({ contents: [ { @@ -3865,17 +3869,21 @@ describe("Tool title precedence", () => { version: "1.0", }); - mcpServer.prompt( + mcpServer.registerPrompt( "test-prompt", { - name: completable(z.string(), (value, context) => { - if (context?.arguments?.["category"] === "developers") { - return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); - } else if (context?.arguments?.["category"] === "managers") { - return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); - } - return ["Guest"].filter(n => n.startsWith(value)); - }), + title: "Team Greeting", + description: "Generate a greeting for team members", + argsSchema: { + name: completable(z.string(), (value, context) => { + if (context?.arguments?.["category"] === "developers") { + return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); + } else if (context?.arguments?.["category"] === "managers") { + return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); + } + return ["Guest"].filter(n => n.startsWith(value)); + }), + } }, async ({ name }) => ({ messages: [ From 0e86a6d139e0a3da564101cb751f53d3c3fb00fe Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 14:36:35 +0100 Subject: [PATCH 3/6] improve readme --- README.md | 68 ++++++++---------------------------------- src/server/mcp.test.ts | 51 ++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 63acfb065..08b711c2e 100644 --- a/README.md +++ b/README.md @@ -287,23 +287,30 @@ server.registerPrompt( title: "Team Greeting", description: "Generate a greeting for team members", argsSchema: { - // Completable arguments can use context for intelligent suggestions + department: completable(z.string(), (value) => { + // Department suggestions + return ["engineering", "sales", "marketing", "support"].filter(d => d.startsWith(value)); + }), name: completable(z.string(), (value, context) => { - if (context?.arguments?.["department"] === "engineering") { + // Name suggestions based on selected department + const department = context?.arguments?.["department"]; + if (department === "engineering") { return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); - } else if (context?.arguments?.["department"] === "sales") { + } else if (department === "sales") { return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); + } else if (department === "marketing") { + return ["Grace", "Henry", "Iris"].filter(n => n.startsWith(value)); } return ["Guest"].filter(n => n.startsWith(value)); }) } }, - ({ name }) => ({ + ({ department, name }) => ({ messages: [{ role: "assistant", content: { type: "text", - text: `Hello ${name}, welcome to the team!` + text: `Hello ${name}, welcome to the ${department} team!` } }] }) @@ -695,57 +702,6 @@ server.registerTool( ## Advanced Usage -### Context-Aware Completions - -MCP supports intelligent completions that can use previously resolved values as context. This is useful for creating dependent parameter completions where later parameters depend on earlier ones: - -```typescript -import { completable } from "@modelcontextprotocol/sdk/server/completable.js"; - -// For resource templates -server.registerResource( - "database-query", - new ResourceTemplate("db://{database}/{table}/{query}", { - list: undefined, - complete: { - // Table completions depend on the selected database - table: (value, context) => { - const database = context?.arguments?.["database"]; - if (database === "users_db") { - return ["profiles", "sessions", "preferences"].filter(t => t.startsWith(value)); - } else if (database === "products_db") { - return ["items", "categories", "inventory"].filter(t => t.startsWith(value)); - } - return []; - } - } - }), - metadata, - handler -); - -// For prompts with completable arguments -server.registerPrompt( - "api-request", - { - argsSchema: { - endpoint: z.string(), - // Method completions can be context-aware - method: completable(z.string(), (value, context) => { - const endpoint = context?.arguments?.["endpoint"]; - if (endpoint?.includes("/readonly/")) { - return ["GET"].filter(m => m.startsWith(value.toUpperCase())); - } - return ["GET", "POST", "PUT", "DELETE"].filter(m => m.startsWith(value.toUpperCase())); - }) - } - }, - handler -); -``` - -The context object contains an `arguments` field with previously resolved parameter values, allowing you to provide more intelligent and contextual completions. - ### Dynamic Servers If you want to offer an initial set of tools/prompts/resources, but later add additional ones based on user action or external state change, you can add/update/remove them _after_ the Server is connected. This will automatically emit the corresponding `listChanged` notifications: diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 685987bf7..15be3d987 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -3875,23 +3875,29 @@ describe("Tool title precedence", () => { title: "Team Greeting", description: "Generate a greeting for team members", argsSchema: { + department: completable(z.string(), (value) => { + return ["engineering", "sales", "marketing", "support"].filter(d => d.startsWith(value)); + }), name: completable(z.string(), (value, context) => { - if (context?.arguments?.["category"] === "developers") { + const department = context?.arguments?.["department"]; + if (department === "engineering") { return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value)); - } else if (context?.arguments?.["category"] === "managers") { + } else if (department === "sales") { return ["David", "Eve", "Frank"].filter(n => n.startsWith(value)); + } else if (department === "marketing") { + return ["Grace", "Henry", "Iris"].filter(n => n.startsWith(value)); } return ["Guest"].filter(n => n.startsWith(value)); }), } }, - async ({ name }) => ({ + async ({ department, name }) => ({ messages: [ { role: "assistant", content: { type: "text", - text: `Hello ${name}`, + text: `Hello ${name}, welcome to the ${department} team!`, }, }, ], @@ -3906,7 +3912,7 @@ describe("Tool title precedence", () => { mcpServer.server.connect(serverTransport), ]); - // Test with developers category + // Test with engineering department const result1 = await client.request( { method: "completion/complete", @@ -3921,7 +3927,7 @@ describe("Tool title precedence", () => { }, context: { arguments: { - category: "developers", + department: "engineering", }, }, }, @@ -3931,7 +3937,7 @@ describe("Tool title precedence", () => { expect(result1.completion.values).toEqual(["Alice"]); - // Test with managers category + // Test with sales department const result2 = await client.request( { method: "completion/complete", @@ -3946,7 +3952,7 @@ describe("Tool title precedence", () => { }, context: { arguments: { - category: "managers", + department: "sales", }, }, }, @@ -3956,8 +3962,33 @@ describe("Tool title precedence", () => { expect(result2.completion.values).toEqual(["David"]); - // Test with no resolved context + // Test with marketing department const result3 = await client.request( + { + method: "completion/complete", + params: { + ref: { + type: "ref/prompt", + name: "test-prompt", + }, + argument: { + name: "name", + value: "G", + }, + context: { + arguments: { + department: "marketing", + }, + }, + }, + }, + CompleteResultSchema, + ); + + expect(result3.completion.values).toEqual(["Grace"]); + + // Test with no resolved context + const result4 = await client.request( { method: "completion/complete", params: { @@ -3974,6 +4005,6 @@ describe("Tool title precedence", () => { CompleteResultSchema, ); - expect(result3.completion.values).toEqual(["Guest"]); + expect(result4.completion.values).toEqual(["Guest"]); }); }); From c0c78ad412c36403ec70fae28f767dd0a756eaa5 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 15:04:23 +0100 Subject: [PATCH 4/6] add how to use Completions to the readme --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index 08b711c2e..8d319f67a 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,32 @@ server.registerPrompt( ); ``` +### Completions + +MCP supports argument completions to help users fill in prompt arguments and resource template parameters. See the examples above for [resource completions](#resources) and [prompt completions](#prompts). + +#### Client Usage + +```typescript +// Request completions for any argument +const result = await client.complete({ + ref: { + type: "ref/prompt", // or "ref/resource" + name: "example" // or uri: "template://..." + }, + argument: { + name: "argumentName", + value: "partial" // What the user has typed so far + }, + context: { // Optional: Include previously resolved arguments + arguments: { + previousArg: "value" + } + } +}); +console.log(result.completion.values); // ["suggestion1", "suggestion2", ...] +``` + ### Display Names and Metadata All resources, tools, and prompts support an optional `title` field for better UI presentation. The `title` is used as a display name, while `name` remains the unique identifier. @@ -870,6 +896,18 @@ const result = await client.callTool({ arg1: "value" } }); + +// Request completions +const completions = await client.complete({ + ref: { + type: "ref/prompt", + name: "example-prompt" + }, + argument: { + name: "arg1", + value: "partial" + } +}); ``` ### Proxy Authorization Requests Upstream From 4549250a4301ac2f44b60923e38ef2db1f8b7051 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 15:04:33 +0100 Subject: [PATCH 5/6] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d319f67a..a73d8a381 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,7 @@ const result = await client.complete({ } } }); -console.log(result.completion.values); // ["suggestion1", "suggestion2", ...] + ``` ### Display Names and Metadata From 5b875aa542337f253d9a31b166f068ed519a724b Mon Sep 17 00:00:00 2001 From: ihrpr Date: Mon, 16 Jun 2025 15:19:21 +0100 Subject: [PATCH 6/6] clean up --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index a73d8a381..272eeb129 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [Resources](#resources) - [Tools](#tools) - [Prompts](#prompts) + - [Completions](#completions) - [Running Your Server](#running-your-server) - [stdio](#stdio) - [Streamable HTTP](#streamable-http) @@ -897,17 +898,6 @@ const result = await client.callTool({ } }); -// Request completions -const completions = await client.complete({ - ref: { - type: "ref/prompt", - name: "example-prompt" - }, - argument: { - name: "arg1", - value: "partial" - } -}); ``` ### Proxy Authorization Requests Upstream