Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b4246ea
save commit
KKonstantinov Nov 14, 2025
25b631c
merge commit
KKonstantinov Nov 22, 2025
4d4f517
SEP-1046: add jwt prebuilt assertion and signing options, add zod sch…
KKonstantinov Nov 22, 2025
7262bf2
test polyfill setup for jose on node 18
KKonstantinov Nov 22, 2025
bf895de
prettier fix
KKonstantinov Nov 22, 2025
683dfaa
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 25, 2025
da86864
remove jwt-bearer grant_type, add jwt-bearer client_assertion_type, r…
KKonstantinov Nov 26, 2025
3ea632f
Merge branch 'feature/sep-1046-client-credentials' of github.com:KKon…
KKonstantinov Nov 26, 2025
166dcab
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 26, 2025
2115343
clean up
KKonstantinov Nov 26, 2025
f1b8aef
clean up
KKonstantinov Nov 26, 2025
faa3c53
split out into extension file
pcarleton Nov 26, 2025
b58771c
fix type error
pcarleton Nov 26, 2025
b1bc454
dont commit a key
pcarleton Nov 26, 2025
86ee089
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 26, 2025
5f99239
add StaticPrivateKeyJwtProvider, add tests, consolidate tests in auth…
KKonstantinov Nov 27, 2025
f16bc71
Add README, add throw if crypto not available in older Node versions
KKonstantinov Nov 27, 2025
6822457
prettier fix
KKonstantinov Nov 27, 2025
8afe461
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 27, 2025
258a5c6
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 28, 2025
82ca0af
Merge branch 'main' into feature/sep-1046-client-credentials
KKonstantinov Nov 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,30 @@ await server.connect(transport);

To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.

### Node.js Web Crypto (globalThis.crypto) compatibility

Some parts of the SDK (for example, JWT-based client authentication in `auth-extensions.ts` via `jose`) rely on the Web Crypto API exposed as `globalThis.crypto`.

- **Node.js v19.0.0 and later**: `globalThis.crypto` is available by default.
- **Node.js v18.x**: `globalThis.crypto` may not be defined by default; in this repository we polyfill it for tests (see `vitest.setup.ts`), and you should do the same in your app if it is missing - or alternatively, run Node with `--experimental-global-webcrypto` as per your
Node version documentation. (See https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#crypto )

If you run tests or applications on Node.js versions where `globalThis.crypto` is missing, you can polyfill it using the built-in `node:crypto` module, similar to the SDK's own `vitest.setup.ts`:

```typescript
import { webcrypto } from 'node:crypto';

if (typeof globalThis.crypto === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).crypto = webcrypto as unknown as Crypto;
}
```

For production use, you can either:

- Run on a Node.js version where `globalThis.crypto` is available by default (recommended), or
- Apply a similar polyfill early in your application's startup code when targeting older Node.js runtimes.

## Examples

### Echo Server
Expand Down Expand Up @@ -1636,6 +1660,64 @@ const result = await client.callTool({
});
```

### OAuth client authentication helpers

For OAuth-secured MCP servers, the client `auth` module exposes a generic `OAuthClientProvider` interface, and `src/client/auth-extensions.ts` provides ready-to-use implementations for common machine-to-machine authentication flows:

- **ClientCredentialsProvider**: Uses the `client_credentials` grant with `client_secret_basic` authentication.
- **PrivateKeyJwtProvider**: Uses the `client_credentials` grant with `private_key_jwt` client authentication, signing a JWT assertion on each token request.
- **StaticPrivateKeyJwtProvider**: Similar to `PrivateKeyJwtProvider`, but accepts a pre-built JWT assertion string via `jwtBearerAssertion` and reuses it for token requests.

You can use these providers with the `StreamableHTTPClientTransport` and the high-level `auth()` helper:

```typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider } from '@modelcontextprotocol/sdk/client/auth-extensions.js';
import { auth } from '@modelcontextprotocol/sdk/client/auth.js';

const serverUrl = new URL('https://mcp.example.com/');

// Example: client_credentials with client_secret_basic
const basicProvider = new ClientCredentialsProvider({
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
clientName: 'example-basic-client'
});

// Example: client_credentials with private_key_jwt (JWT signed locally)
const privateKeyJwtProvider = new PrivateKeyJwtProvider({
clientId: process.env.CLIENT_ID!,
privateKey: process.env.CLIENT_PRIVATE_KEY_PEM!,
algorithm: 'RS256',
clientName: 'example-private-key-jwt-client',
jwtLifetimeSeconds: 300
});

// Example: client_credentials with a pre-built JWT assertion
const staticJwtProvider = new StaticPrivateKeyJwtProvider({
clientId: process.env.CLIENT_ID!,
jwtBearerAssertion: process.env.CLIENT_ASSERTION!,
clientName: 'example-static-private-key-jwt-client'
});

const transport = new StreamableHTTPClientTransport(serverUrl, {
authProvider: privateKeyJwtProvider
});

const client = new Client({
name: 'example-client',
version: '1.0.0'
});

// Perform the OAuth flow (including dynamic client registration if needed)
await auth(privateKeyJwtProvider, { serverUrl, fetchFn: transport.fetch });

await client.connect(transport);
```

If you need lower-level control, you can also use `createPrivateKeyJwtAuth()` directly to implement `addClientAuthentication` on a custom `OAuthClientProvider`.

### Proxy Authorization Requests Upstream

You can proxy OAuth requests to an external authorization provider:
Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"eventsource-parser": "^3.0.0",
"express": "^5.0.1",
"express-rate-limit": "^7.5.0",
"jose": "^6.1.1",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.25 || ^4.0",
Expand Down
Loading
Loading