From 30b5544e8b88fe9a46243259699645f83cab0553 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Fri, 2 May 2025 08:40:58 +0000 Subject: [PATCH 1/3] feat: Add scope parameter to startAuthorization function - Add optional scope parameter to startAuthorization function signature - Include scope in authorization URL query parameters when provided - Update auth function to pass through scope from provider.clientMetadata - Add tests to verify scope is correctly included/excluded from auth URL This enhancement allows clients to specify authorization scopes during the OAuth flow. --- src/client/auth.test.ts | 25 +++++++++++++++++++++++++ src/client/auth.ts | 9 ++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/client/auth.test.ts b/src/client/auth.test.ts index 629feab7..eba7074b 100644 --- a/src/client/auth.test.ts +++ b/src/client/auth.test.ts @@ -177,6 +177,31 @@ describe("OAuth Authorization", () => { expect(codeVerifier).toBe("test_verifier"); }); + it("includes scope parameter when provided", async () => { + const { authorizationUrl } = await startAuthorization( + "https://auth.example.com", + { + clientInformation: validClientInfo, + redirectUrl: "http://localhost:3000/callback", + scope: "read write profile", + } + ); + + expect(authorizationUrl.searchParams.get("scope")).toBe("read write profile"); + }); + + it("excludes scope parameter when not provided", async () => { + const { authorizationUrl } = await startAuthorization( + "https://auth.example.com", + { + clientInformation: validClientInfo, + redirectUrl: "http://localhost:3000/callback", + } + ); + + expect(authorizationUrl.searchParams.has("scope")).toBe(false); + }); + it("uses metadata authorization_endpoint when provided", async () => { const { authorizationUrl } = await startAuthorization( "https://auth.example.com", diff --git a/src/client/auth.ts b/src/client/auth.ts index a279121b..b170cefc 100644 --- a/src/client/auth.ts +++ b/src/client/auth.ts @@ -145,7 +145,8 @@ export async function auth( const { authorizationUrl, codeVerifier } = await startAuthorization(serverUrl, { metadata, clientInformation, - redirectUrl: provider.redirectUrl + redirectUrl: provider.redirectUrl, + scope: provider.clientMetadata.scope }); await provider.saveCodeVerifier(codeVerifier); @@ -202,10 +203,12 @@ export async function startAuthorization( metadata, clientInformation, redirectUrl, + scope, }: { metadata?: OAuthMetadata; clientInformation: OAuthClientInformation; redirectUrl: string | URL; + scope?: string; }, ): Promise<{ authorizationUrl: URL; codeVerifier: string }> { const responseType = "code"; @@ -246,6 +249,10 @@ export async function startAuthorization( codeChallengeMethod, ); authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl)); + + if (scope) { + authorizationUrl.searchParams.set("scope", scope); + } return { authorizationUrl, codeVerifier }; } From 52dc7f116080a143ff65a559a4f10a3420ec4d57 Mon Sep 17 00:00:00 2001 From: yuna0x0 Date: Tue, 6 May 2025 01:49:33 +0800 Subject: [PATCH 2/3] Fix isZodRawShape return false on empty object and add test --- src/server/mcp.test.ts | 47 ++++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 7 +++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index c9be5c76..5f50df68 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -622,6 +622,53 @@ describe("tool()", () => { }); }); + test("should register tool with description, empty params, and annotations", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + const client = new Client({ + name: "test client", + version: "1.0", + }); + + mcpServer.tool( + "test", + "A tool with everything but empty params", + {}, + { title: "Complete Test Tool with empty params", readOnlyHint: true, openWorldHint: false }, + async () => ({ + content: [{ type: "text", text: "Test response" }] + }) + ); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.server.connect(serverTransport), + ]); + + const result = await client.request( + { method: "tools/list" }, + ListToolsResultSchema, + ); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe("test"); + expect(result.tools[0].description).toBe("A tool with everything but empty params"); + expect(result.tools[0].inputSchema).toMatchObject({ + type: "object", + properties: {} + }); + expect(result.tools[0].annotations).toEqual({ + title: "Complete Test Tool with empty params", + readOnlyHint: true, + openWorldHint: false + }); + }); + test("should validate tool args", async () => { const mcpServer = new McpServer({ name: "test server", diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 6204eb2a..dccfa04e 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -663,8 +663,11 @@ export class McpServer { // Helper to check if an object is a Zod schema (ZodRawShape) const isZodRawShape = (obj: unknown): obj is ZodRawShape => { if (typeof obj !== "object" || obj === null) return false; - // Check that at least one property is a ZodType instance - return Object.values(obj as object).some(v => v instanceof ZodType); + + const isEmptyObject = z.object({}).strict().safeParse(obj).success; + + // Check if object is empty or at least one property is a ZodType instance + return isEmptyObject || Object.values(obj as object).some(v => v instanceof ZodType); }; let description: string | undefined; From 51ce07fada7d1cdedaf4ca28820a3911ae1329ec Mon Sep 17 00:00:00 2001 From: ihrpr Date: Thu, 8 May 2025 18:05:27 +0100 Subject: [PATCH 3/3] bump version to 1.11.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6a65847..fa6f9545 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/sdk", - "version": "1.11.0", + "version": "1.11.1", "description": "Model Context Protocol implementation for TypeScript", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)",