Skip to content

Commit eed97c9

Browse files
committed
Merge branch 'main' into ihrpr/elicitation
2 parents eea1519 + 2cf4f0c commit eed97c9

File tree

12 files changed

+812
-209
lines changed

12 files changed

+812
-209
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ putMessageTool.disable()
548548

549549
const upgradeAuthTool = server.tool(
550550
"upgradeAuth",
551-
{ permission: z.enum(["write', vadmin"])},
551+
{ permission: z.enum(["write', admin"])},
552552
// Any mutations here will automatically emit `listChanged` notifications
553553
async ({ permission }) => {
554554
const { ok, err, previous } = await upgradeAuthAndStoreToken(permission)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/sdk",
3-
"version": "1.11.4",
3+
"version": "1.12.2",
44
"description": "Model Context Protocol implementation for TypeScript",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",

src/client/auth.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,31 @@ describe("OAuth Authorization", () => {
366366
expect(authorizationUrl.searchParams.has("scope")).toBe(false);
367367
});
368368

369+
it("includes state parameter when provided", async () => {
370+
const { authorizationUrl } = await startAuthorization(
371+
"https://auth.example.com",
372+
{
373+
clientInformation: validClientInfo,
374+
redirectUrl: "http://localhost:3000/callback",
375+
state: "foobar",
376+
}
377+
);
378+
379+
expect(authorizationUrl.searchParams.get("state")).toBe("foobar");
380+
});
381+
382+
it("excludes state parameter when not provided", async () => {
383+
const { authorizationUrl } = await startAuthorization(
384+
"https://auth.example.com",
385+
{
386+
clientInformation: validClientInfo,
387+
redirectUrl: "http://localhost:3000/callback",
388+
}
389+
);
390+
391+
expect(authorizationUrl.searchParams.has("state")).toBe(false);
392+
});
393+
369394
it("uses metadata authorization_endpoint when provided", async () => {
370395
const { authorizationUrl } = await startAuthorization(
371396
"https://auth.example.com",

src/client/auth.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export interface OAuthClientProvider {
2121
*/
2222
get clientMetadata(): OAuthClientMetadata;
2323

24+
/**
25+
* Returns a OAuth2 state parameter.
26+
*/
27+
state?(): string | Promise<string>;
28+
2429
/**
2530
* Loads information about this OAuth client, as registered already with the
2631
* server, or returns `undefined` if the client is not registered with the
@@ -162,10 +167,13 @@ export async function auth(
162167
}
163168
}
164169

170+
const state = provider.state ? await provider.state() : undefined;
171+
165172
// Start new authorization flow
166173
const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
167174
metadata,
168175
clientInformation,
176+
state,
169177
redirectUrl: provider.redirectUrl,
170178
scope: scope || provider.clientMetadata.scope,
171179
});
@@ -301,11 +309,13 @@ export async function startAuthorization(
301309
clientInformation,
302310
redirectUrl,
303311
scope,
312+
state,
304313
}: {
305314
metadata?: OAuthMetadata;
306315
clientInformation: OAuthClientInformation;
307316
redirectUrl: string | URL;
308317
scope?: string;
318+
state?: string;
309319
},
310320
): Promise<{ authorizationUrl: URL; codeVerifier: string }> {
311321
const responseType = "code";
@@ -347,6 +357,10 @@ export async function startAuthorization(
347357
);
348358
authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl));
349359

360+
if (state) {
361+
authorizationUrl.searchParams.set("state", state);
362+
}
363+
350364
if (scope) {
351365
authorizationUrl.searchParams.set("scope", scope);
352366
}

src/client/sse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ export class SSEClientTransport implements Transport {
9999
}
100100

101101
private async _commonHeaders(): Promise<HeadersInit> {
102-
const headers: HeadersInit = {};
102+
const headers: HeadersInit = { ...this._requestInit?.headers };
103103
if (this._authProvider) {
104104
const tokens = await this._authProvider.tokens();
105105
if (tokens) {
106-
headers["Authorization"] = `Bearer ${tokens.access_token}`;
106+
(headers as Record<string, string>)["Authorization"] = `Bearer ${tokens.access_token}`;
107107
}
108108
}
109109

src/examples/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ A full-featured interactive client that connects to a Streamable HTTP server, de
3333
npx tsx src/examples/client/simpleStreamableHttp.ts
3434
```
3535

36+
Example client with OAuth:
37+
38+
```bash
39+
npx tsx src/examples/client/simpleOAuthClient.js
40+
```
41+
3642
### Backwards Compatible Client
3743

3844
A client that implements backwards compatibility according to the [MCP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility), allowing it to work with both new and legacy servers. This client demonstrates:

0 commit comments

Comments
 (0)