pi-soly 1.9.3 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ask/index.ts +12 -11
- package/ask/picker.ts +356 -76
- package/ask/prompt.ts +6 -2
- package/ask/tests/picker.test.ts +273 -82
- package/codemap.ts +276 -0
- package/hotreload.ts +239 -0
- package/init.ts +302 -0
- package/mcp/CHANGELOG.md +384 -0
- package/mcp/LICENSE +21 -0
- package/mcp/OAUTH.md +355 -0
- package/mcp/README.md +410 -0
- package/mcp/__tests__/agent-dir-paths.upstream-test.ts +80 -0
- package/mcp/__tests__/cli.upstream-test.ts +97 -0
- package/mcp/__tests__/commands-onboarding.upstream-test.ts +157 -0
- package/mcp/__tests__/config.upstream-test.ts +303 -0
- package/mcp/__tests__/consent-manager.upstream-test.ts +151 -0
- package/mcp/__tests__/direct-tools-auto-auth.upstream-test.ts +218 -0
- package/mcp/__tests__/direct-tools.upstream-test.ts +299 -0
- package/mcp/__tests__/elicitation-handler.upstream-test.ts +346 -0
- package/mcp/__tests__/elicitation-sdk-integration.upstream-test.ts +140 -0
- package/mcp/__tests__/errors.upstream-test.ts +218 -0
- package/mcp/__tests__/fixtures/elicitation-server.mjs +98 -0
- package/mcp/__tests__/host-html-template.upstream-test.ts +278 -0
- package/mcp/__tests__/index-lifecycle.upstream-test.ts +484 -0
- package/mcp/__tests__/init-elicitation.upstream-test.ts +86 -0
- package/mcp/__tests__/interactive-visualizer-server.upstream-test.ts +28 -0
- package/mcp/__tests__/logger.upstream-test.ts +175 -0
- package/mcp/__tests__/mcp-auth-flow-client-credentials.upstream-test.ts +612 -0
- package/mcp/__tests__/mcp-auth-storage.upstream-test.ts +47 -0
- package/mcp/__tests__/mcp-callback-server-unref.upstream-test.ts +264 -0
- package/mcp/__tests__/mcp-oauth-provider.upstream-test.ts +216 -0
- package/mcp/__tests__/mcp-panel-auth.upstream-test.ts +206 -0
- package/mcp/__tests__/mcp-panel-exclude-tools.upstream-test.ts +69 -0
- package/mcp/__tests__/mcp-panel-keybindings.upstream-test.ts +186 -0
- package/mcp/__tests__/npx-resolver.upstream-test.ts +54 -0
- package/mcp/__tests__/oauth-handler.upstream-test.ts +77 -0
- package/mcp/__tests__/onboarding-state.upstream-test.ts +52 -0
- package/mcp/__tests__/package-manifest.upstream-test.ts +22 -0
- package/mcp/__tests__/proxy-modes-auto-auth.upstream-test.ts +253 -0
- package/mcp/__tests__/proxy-modes-discovery.upstream-test.ts +84 -0
- package/mcp/__tests__/proxy-modes-manual-auth.upstream-test.ts +91 -0
- package/mcp/__tests__/proxy-modes-ui-messages.upstream-test.ts +72 -0
- package/mcp/__tests__/sampling-handler.upstream-test.ts +285 -0
- package/mcp/__tests__/server-manager-http-auth.upstream-test.ts +144 -0
- package/mcp/__tests__/server-manager-sampling.upstream-test.ts +236 -0
- package/mcp/__tests__/tool-metadata.upstream-test.ts +111 -0
- package/mcp/__tests__/tool-result-renderer.upstream-test.ts +147 -0
- package/mcp/__tests__/ui-integration.upstream-test.ts +551 -0
- package/mcp/__tests__/ui-resource-handler.upstream-test.ts +303 -0
- package/mcp/__tests__/ui-server.upstream-test.ts +967 -0
- package/mcp/__tests__/ui-session-messages.upstream-test.ts +72 -0
- package/mcp/__tests__/ui-streaming.upstream-test.ts +543 -0
- package/mcp/agent-dir.ts +20 -0
- package/mcp/app-bridge.bundle.js +67 -0
- package/mcp/cli.js +184 -0
- package/mcp/commands.ts +422 -0
- package/mcp/config.ts +666 -0
- package/mcp/consent-manager.ts +64 -0
- package/mcp/direct-tools.ts +439 -0
- package/mcp/elicitation-handler.ts +347 -0
- package/mcp/errors.ts +219 -0
- package/mcp/glimpse-ui.ts +80 -0
- package/mcp/host-html-template.ts +427 -0
- package/mcp/index.ts +362 -0
- package/mcp/init.ts +362 -0
- package/mcp/lifecycle.ts +93 -0
- package/mcp/logger.ts +169 -0
- package/mcp/mcp-auth-flow.ts +559 -0
- package/mcp/mcp-auth-flow.upstream-test.ts +259 -0
- package/mcp/mcp-auth.ts +302 -0
- package/mcp/mcp-auth.upstream-test.ts +373 -0
- package/mcp/mcp-callback-server.ts +372 -0
- package/mcp/mcp-callback-server.upstream-test.ts +416 -0
- package/mcp/mcp-oauth-provider.ts +369 -0
- package/mcp/mcp-oauth-provider.upstream-test.ts +518 -0
- package/mcp/mcp-panel.ts +829 -0
- package/mcp/mcp-setup-panel.ts +580 -0
- package/mcp/metadata-cache.ts +201 -0
- package/mcp/notify.ts +111 -0
- package/mcp/npx-resolver.ts +424 -0
- package/mcp/oauth-handler.ts +57 -0
- package/mcp/onboarding-state.ts +68 -0
- package/mcp/package.json +106 -0
- package/mcp/panel-keys.ts +37 -0
- package/mcp/proxy-modes.ts +949 -0
- package/mcp/resource-tools.ts +17 -0
- package/mcp/sampling-handler.ts +268 -0
- package/mcp/server-manager.ts +545 -0
- package/mcp/state.ts +41 -0
- package/mcp/tool-metadata.ts +216 -0
- package/mcp/tool-registrar.ts +46 -0
- package/mcp/tool-result-renderer.ts +161 -0
- package/mcp/types.ts +448 -0
- package/mcp/ui-resource-handler.ts +146 -0
- package/mcp/ui-server.ts +623 -0
- package/mcp/ui-session.ts +386 -0
- package/mcp/ui-stream-types.ts +89 -0
- package/mcp/utils.ts +129 -0
- package/mcp/vitest.config.ts +14 -0
- package/migrate.ts +258 -0
- package/notification.ts +218 -0
- package/notifications-log.ts +83 -0
- package/package.json +20 -3
- package/status.ts +140 -0
package/mcp/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nico Bailon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/mcp/OAUTH.md
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# OAuth 2.1 Authentication for MCP
|
|
2
|
+
|
|
3
|
+
This document describes the OAuth 2.1 + PKCE authentication implementation for the Pi MCP Adapter using the official MCP SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Pi MCP Adapter uses the official MCP SDK's built-in OAuth implementation, which provides:
|
|
8
|
+
|
|
9
|
+
- **Automatic OAuth endpoint discovery** (RFC 9728) - No manual configuration needed
|
|
10
|
+
- **Dynamic client registration** (RFC 7591) - No clientId needed for most servers
|
|
11
|
+
- **Automatic callback handling** - Built-in HTTP server handles callbacks automatically
|
|
12
|
+
- **Automatic token refresh** - SDK handles token refresh transparently
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- ✅ **PKCE (S256)** - Mandatory code challenge method for OAuth 2.1
|
|
17
|
+
- ✅ **Automatic Callback Server** - Local browser redirects automatically when available
|
|
18
|
+
- ✅ **Manual Remote Flow** - Copy auth URLs and pasted redirect URLs/codes for headless SSH sessions
|
|
19
|
+
- ✅ **Dynamic Client Registration** - Automatically registers with OAuth servers
|
|
20
|
+
- ✅ **Auto-Discovery** - Discovers OAuth endpoints from server metadata
|
|
21
|
+
- ✅ **Automatic Token Refresh** - SDK handles expired tokens automatically
|
|
22
|
+
- ✅ **State Parameter Validation** - CSRF protection
|
|
23
|
+
- ✅ **Secure Token Storage** - Stored in `~/.pi/agent/mcp-oauth/sha256-<server-hash>/tokens.json`
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Minimal Configuration (Recommended)
|
|
28
|
+
|
|
29
|
+
For most MCP servers, you only need the URL:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"my-oauth-server": {
|
|
35
|
+
"url": "https://api.example.com/mcp"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
OAuth is automatically enabled for HTTP servers. The SDK will:
|
|
42
|
+
- Auto-detect if the server requires OAuth
|
|
43
|
+
- Discover OAuth endpoints from the server
|
|
44
|
+
- Register a dynamic client (if supported by the server)
|
|
45
|
+
- Handle the entire OAuth flow including callback
|
|
46
|
+
|
|
47
|
+
### Optional Configuration
|
|
48
|
+
|
|
49
|
+
You can optionally provide a pre-registered client:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"my-oauth-server": {
|
|
55
|
+
"url": "https://api.example.com/mcp",
|
|
56
|
+
"auth": "oauth",
|
|
57
|
+
"oauth": {
|
|
58
|
+
"clientId": "your-client-id",
|
|
59
|
+
"clientSecret": "your-client-secret",
|
|
60
|
+
"scope": "read write",
|
|
61
|
+
"redirectUri": "http://localhost:3118/callback"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
### Configuration Options
|
|
71
|
+
|
|
72
|
+
- `url` - The MCP server URL (required)
|
|
73
|
+
- `auth` - Set to `"oauth"` to force OAuth, `false` to disable, or omit to auto-detect
|
|
74
|
+
- `oauth.grantType` - `"authorization_code"` (default, browser flow) or `"client_credentials"` (non-interactive)
|
|
75
|
+
- `oauth.clientId` - Pre-registered client ID (optional, SDK tries dynamic registration if not provided)
|
|
76
|
+
- `oauth.clientSecret` - Client secret for confidential clients (optional)
|
|
77
|
+
- `oauth.scope` - Requested OAuth scopes (optional)
|
|
78
|
+
- `oauth.redirectUri` - Exact browser callback URI to advertise and bind, such as `http://localhost:3118/callback` (optional)
|
|
79
|
+
- `oauth.clientName` - Client display name used for dynamic registration (optional, defaults to `Pi Coding Agent`)
|
|
80
|
+
- `oauth.clientUri` - Client homepage URI used for dynamic registration (optional)
|
|
81
|
+
|
|
82
|
+
Dynamic clients normally omit `oauth.redirectUri`; the adapter starts the callback server lazily on the default loopback host (`localhost`) and asks the OS for an available local port when auth begins. Use `oauth.redirectUri` when the provider requires a pre-registered callback, such as Slack MCP's Claude-compatible `http://localhost:3118/callback`. The URI must use `http://` with `localhost`, `127.0.0.1`, or `[::1]`, include an explicit port, and its host/path become the bound callback endpoint.
|
|
83
|
+
|
|
84
|
+
### Non-Interactive `client_credentials`
|
|
85
|
+
|
|
86
|
+
For machine-to-machine OAuth, configure `grantType: "client_credentials"`.
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"my-service": {
|
|
92
|
+
"url": "https://api.example.com/mcp",
|
|
93
|
+
"auth": "oauth",
|
|
94
|
+
"oauth": {
|
|
95
|
+
"grantType": "client_credentials",
|
|
96
|
+
"clientId": "service-client-id",
|
|
97
|
+
"clientSecret": "service-client-secret",
|
|
98
|
+
"scope": "read write"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This flow does not open a browser or use callback handling. `oauth.redirectUri` is ignored for `client_credentials`; `oauth.clientName` and `oauth.clientUri` still apply to dynamic client registration metadata.
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
### Step 1: Authenticate
|
|
110
|
+
|
|
111
|
+
Run the `/mcp-auth` command with the server name:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
/mcp-auth my-oauth-server
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Manual `/mcp-auth` is the default flow. If you set `settings.autoAuth: true`, proxy/direct tool execution will trigger OAuth automatically when a server returns `needs-auth`, then retry the original operation once.
|
|
118
|
+
|
|
119
|
+
This will:
|
|
120
|
+
1. Start the callback server lazily on an OS-assigned local port, or on the exact `oauth.redirectUri` port for pre-registered callbacks
|
|
121
|
+
2. Discover OAuth endpoints automatically
|
|
122
|
+
3. Register a dynamic client (if no clientId provided)
|
|
123
|
+
4. Open your browser for authentication
|
|
124
|
+
5. Wait for the automatic callback
|
|
125
|
+
6. Complete the OAuth flow
|
|
126
|
+
7. Store tokens securely
|
|
127
|
+
|
|
128
|
+
### Remote/headless authentication
|
|
129
|
+
|
|
130
|
+
When Pi runs over SSH or in a headless environment, use the proxy tool to retrieve the authorization URL instead of relying on OS browser launch:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
mcp({ action: "auth-start", server: "my-oauth-server" })
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Open the returned URL in your local browser. After approval, copy the full redirected localhost URL from the browser address bar (the page may fail to load locally) and complete the same pending auth flow:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
mcp({
|
|
140
|
+
action: "auth-complete",
|
|
141
|
+
server: "my-oauth-server",
|
|
142
|
+
args: '{"redirectUrl":"http://localhost:19876/callback?code=...&state=..."}'
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can also pass only the `code` query parameter with `args: '{"code":"..."}'`. Redirect URL completion validates the saved OAuth state; raw code completion is available for providers that display a code directly.
|
|
147
|
+
|
|
148
|
+
### Step 2: Use the Server
|
|
149
|
+
|
|
150
|
+
Once authenticated, use the server normally:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
mcp({ server: "my-oauth-server" })
|
|
154
|
+
mcp({ tool: "my-tool", args: '{"key": "value"}' })
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The SDK automatically:
|
|
158
|
+
- Adds the access token to requests
|
|
159
|
+
- Refreshes expired tokens automatically
|
|
160
|
+
- Re-authenticates if tokens are invalid
|
|
161
|
+
|
|
162
|
+
To clear stored OAuth credentials and force a fresh authorization:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
/mcp logout my-oauth-server
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## How It Works
|
|
169
|
+
|
|
170
|
+
### Authentication Flow
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
┌─────────┐ ┌──────────────┐ ┌─────────────────┐
|
|
174
|
+
│ Pi │────▶│ MCP Server │────▶│ OAuth Server │
|
|
175
|
+
│ │ │ │ │ │
|
|
176
|
+
│ 1. Init │ │ 2. Discovery │ │ 3. Register │
|
|
177
|
+
│ │ │ │ │ │
|
|
178
|
+
│ │◀────│ │◀────│ 4. Auth URL │
|
|
179
|
+
│ │ │ │ │ │
|
|
180
|
+
│ │────▶│ Callback │◀────│ 5. Browser │
|
|
181
|
+
│ │ │ Server │ │ Redirect │
|
|
182
|
+
│ │ │ │ │ │
|
|
183
|
+
│ │◀────│ │◀────│ 6. Code │
|
|
184
|
+
│ │ │ │ │ │
|
|
185
|
+
│ │────▶│ │────▶│ 7. Exchange │
|
|
186
|
+
│ │ │ │ │ │
|
|
187
|
+
│ │◀────│ │◀────│ 8. Tokens │
|
|
188
|
+
└─────────┘ └──────────────┘ └─────────────────┘
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Auto-Discovery
|
|
192
|
+
|
|
193
|
+
The SDK attempts to discover OAuth endpoints using:
|
|
194
|
+
|
|
195
|
+
1. **RFC 9728 Metadata** - Fetches `/.well-known/oauth-protected-resource`
|
|
196
|
+
2. **WWW-Authenticate Header** - Parses `resource_metadata` from 401 responses
|
|
197
|
+
|
|
198
|
+
### Dynamic Client Registration
|
|
199
|
+
|
|
200
|
+
If no `clientId` is provided, the SDK:
|
|
201
|
+
|
|
202
|
+
1. Discovers the registration endpoint from OAuth metadata
|
|
203
|
+
2. Registers a new client with:
|
|
204
|
+
- `client_name`: configured `oauth.clientName` or "Pi Coding Agent"
|
|
205
|
+
- `client_uri`: configured `oauth.clientUri` or the adapter repository URL
|
|
206
|
+
- `redirect_uris`: `["http://localhost:<active-callback-port>/callback"]`, or the configured `oauth.redirectUri`
|
|
207
|
+
- `grant_types`: `["authorization_code", "refresh_token"]`
|
|
208
|
+
3. Stores the registered client credentials and the redirect URIs returned by the authorization server
|
|
209
|
+
|
|
210
|
+
When a fresh browser auth starts, cached dynamic client info with tokens is re-registered if its stored redirect URIs are missing or do not include the current redirect URI. Token refresh does not perform this redirect check, so existing refresh-token grants keep working even after a callback setting changes.
|
|
211
|
+
|
|
212
|
+
### Callback Server
|
|
213
|
+
|
|
214
|
+
A Node.js HTTP server runs on a loopback callback endpoint and handles the active callback path:
|
|
215
|
+
|
|
216
|
+
- Dynamic registration starts the callback server only when auth begins, binds the default host `localhost`, and asks the OS for an available local port
|
|
217
|
+
- Pre-registered clients (`oauth.clientId`) without `oauth.redirectUri` require the exact configured callback port from `MCP_OAUTH_CALLBACK_PORT` or the default `19876` on `localhost`
|
|
218
|
+
- `oauth.redirectUri` binds the exact loopback host, port, and path from that URI and advertises the same URI to the provider
|
|
219
|
+
|
|
220
|
+
- Handles `code`, `state`, and `error` parameters
|
|
221
|
+
- Displays success/error HTML pages
|
|
222
|
+
- Validates state parameter for CSRF protection
|
|
223
|
+
- Has a 5-minute timeout for pending authorizations
|
|
224
|
+
|
|
225
|
+
## Token Storage
|
|
226
|
+
|
|
227
|
+
Tokens are stored per-server in `~/.pi/agent/mcp-oauth/sha256-<server-hash>/tokens.json`. The hash is derived from the configured MCP server name, so any valid config key can be used without becoming a filesystem path component:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"tokens": {
|
|
232
|
+
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
233
|
+
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
|
|
234
|
+
"expiresAt": 1709769600,
|
|
235
|
+
"scope": "read write"
|
|
236
|
+
},
|
|
237
|
+
"clientInfo": {
|
|
238
|
+
"clientId": "auto-registered-client-id",
|
|
239
|
+
"clientSecret": "auto-generated-secret",
|
|
240
|
+
"redirectUris": ["http://localhost:49152/callback"]
|
|
241
|
+
},
|
|
242
|
+
"serverUrl": "https://api.example.com/mcp"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Example directory structure:
|
|
247
|
+
```
|
|
248
|
+
~/.pi/agent/mcp-oauth/
|
|
249
|
+
├── sha256-<linear-server-name-hash>/
|
|
250
|
+
│ └── tokens.json
|
|
251
|
+
├── sha256-<github-server-name-hash>/
|
|
252
|
+
│ └── tokens.json
|
|
253
|
+
└── ...
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The `serverUrl` field ensures credentials are invalidated if the server URL changes.
|
|
257
|
+
|
|
258
|
+
## Security Considerations
|
|
259
|
+
|
|
260
|
+
### PKCE
|
|
261
|
+
|
|
262
|
+
All OAuth flows use PKCE with the S256 method, preventing authorization code interception attacks.
|
|
263
|
+
|
|
264
|
+
### State Parameter
|
|
265
|
+
|
|
266
|
+
A cryptographically secure random state parameter is generated for each flow and validated on callback.
|
|
267
|
+
|
|
268
|
+
### File Permissions
|
|
269
|
+
|
|
270
|
+
Token files (`tokens.json`) are created with `0o600` permissions and stored in hashed per-server directories with `0o700` permissions (readable only by owner).
|
|
271
|
+
|
|
272
|
+
### URL Validation
|
|
273
|
+
|
|
274
|
+
Credentials are tied to a specific server URL. If the URL changes, the credentials are invalidated and re-authentication is required.
|
|
275
|
+
|
|
276
|
+
## Troubleshooting
|
|
277
|
+
|
|
278
|
+
### "No OAuth tokens found"
|
|
279
|
+
|
|
280
|
+
Run `/mcp-auth <server>` to authenticate.
|
|
281
|
+
|
|
282
|
+
### "Failed to discover OAuth endpoints"
|
|
283
|
+
|
|
284
|
+
The SDK automatically discovers OAuth endpoints from the MCP server. If discovery fails, the server may require a pre-registered client ID:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"mcpServers": {
|
|
289
|
+
"server": {
|
|
290
|
+
"url": "https://api.example.com/mcp",
|
|
291
|
+
"auth": "oauth",
|
|
292
|
+
"oauth": {
|
|
293
|
+
"clientId": "your-client-id",
|
|
294
|
+
"scope": "read"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### "Dynamic client registration not supported"
|
|
302
|
+
|
|
303
|
+
Some servers require pre-registered clients. Obtain a client ID from your OAuth provider and add it to the config.
|
|
304
|
+
|
|
305
|
+
### Callback server already in use
|
|
306
|
+
|
|
307
|
+
Dynamic browser OAuth uses a lazy OS-assigned port on the default loopback host (`localhost`), so the configured default port being busy should not block dynamic registration.
|
|
308
|
+
|
|
309
|
+
For pre-registered OAuth clients (`oauth.clientId`), the callback redirect URI must match exactly. Set `oauth.redirectUri` to the full registered callback, such as Slack MCP's Claude-compatible `http://localhost:3118/callback`, or free/set `MCP_OAUTH_CALLBACK_PORT` when you rely on the default `/callback` path without an explicit redirect URI.
|
|
310
|
+
|
|
311
|
+
### Browser doesn't open
|
|
312
|
+
|
|
313
|
+
If the browser fails to open (e.g., in SSH sessions), the authorization URL will be displayed. Copy it manually to your browser.
|
|
314
|
+
|
|
315
|
+
## Architecture
|
|
316
|
+
|
|
317
|
+
The OAuth implementation uses the following modules:
|
|
318
|
+
|
|
319
|
+
- `mcp-auth.ts` - Auth storage and retrieval (hashed per-server `tokens.json` files)
|
|
320
|
+
- `mcp-oauth-provider.ts` - SDK OAuthClientProvider implementation
|
|
321
|
+
- `mcp-callback-server.ts` - Node.js HTTP callback server
|
|
322
|
+
- `mcp-auth-flow.ts` - High-level auth flow using SDK transport
|
|
323
|
+
|
|
324
|
+
## SDK Integration
|
|
325
|
+
|
|
326
|
+
The implementation uses these SDK exports:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import {
|
|
330
|
+
auth,
|
|
331
|
+
UnauthorizedError,
|
|
332
|
+
OAuthClientProvider,
|
|
333
|
+
} from "@modelcontextprotocol/sdk/client/auth.js"
|
|
334
|
+
|
|
335
|
+
import {
|
|
336
|
+
StreamableHTTPClientTransport,
|
|
337
|
+
} from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The `McpOAuthProvider` class implements `OAuthClientProvider` and is passed to `StreamableHTTPClientTransport`:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const transport = new StreamableHTTPClientTransport(url, {
|
|
344
|
+
authProvider: new McpOAuthProvider(serverName, serverUrl, config, callbacks),
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## References
|
|
349
|
+
|
|
350
|
+
- [MCP SDK Documentation](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
351
|
+
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
|
|
352
|
+
- [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11)
|
|
353
|
+
- [PKCE (RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636)
|
|
354
|
+
- [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591)
|
|
355
|
+
- [OAuth Protected Resource Metadata (RFC 9728)](https://datatracker.ietf.org/doc/html/rfc9728)
|