pi-mcp-client 0.9.0 → 0.10.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.
@@ -200,6 +200,81 @@ sharing a name, URL, and client ID still share credentials. Explicit login renew
200
200
  dynamic registration when its requested options change. `/mcp get example` shows
201
201
  the requested scopes and callback address without connecting.
202
202
 
203
+ ## Use DPoP-bound tokens
204
+
205
+ DPoP binds an access token to a signing key, so possession of the token alone
206
+ isn't enough to use it. Enable it for an OAuth HTTP server whose service supports
207
+ ES256 proofs:
208
+
209
+ ```json
210
+ {
211
+ "mcpServers": {
212
+ "example": {
213
+ "url": "https://mcp.example.com/mcp",
214
+ "oauthDpop": true
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ Run `/mcp reload`, then `/mcp login example`. For a new definition, you can use
221
+ `/mcp add --scope global --oauth-dpop example https://mcp.example.com/mcp`.
222
+
223
+ Only explicit login creates a key. The private key stays in the OS credential
224
+ store alongside the named server's OAuth credentials, bound to its issuer and
225
+ registered client. Connections and token refreshes reuse it across sessions;
226
+ proofs and server nonces aren't persisted. There is no plaintext fallback, and
227
+ keys aren't stored in configuration or session history. This protects keys at
228
+ rest, not against a compromised Pi process or OS account.
229
+
230
+ The SDK signs fresh request proofs and handles bounded nonce challenges. Enabling
231
+ DPoP requests proof support; it doesn't require the service to issue bound tokens.
232
+ If the service issues Bearer access tokens, resource requests still use Bearer
233
+ authentication. Refresh tokens can be DPoP-bound independently; their binding is
234
+ retained even if the access token is Bearer or the signing key is lost.
235
+ `/mcp get example` shows whether DPoP is configured, not whether a grant is bound.
236
+
237
+ If a bound access or refresh token's key is missing, sign in again at the same
238
+ issuer. If the key is corrupt or belongs to a different client, use
239
+ `/mcp logout example` before signing in. Login checks the signing key before
240
+ opening the browser. Disabling DPoP doesn't convert bound tokens to Bearer
241
+ tokens: enable it again or log out and obtain a new grant.
242
+ Logout removes the key and tokens locally and attempts remote token revocation.
243
+ A changed issuer requires explicit logout before trusting its replacement.
244
+
245
+ ## Approve additional permissions
246
+
247
+ A server can reject a request with an OAuth scope challenge—for example, when
248
+ searching works but creating an issue requires write access. The operation stops
249
+ with `oauth_scope_required`; it doesn't open a browser or retry automatically.
250
+
251
+ Run the indicated command yourself:
252
+
253
+ ```text
254
+ /mcp login example
255
+ ```
256
+
257
+ Page through the server's requested scope names and the combined set of configured,
258
+ previously granted, and requested scopes. In the terminal, press `a` on the last
259
+ page to approve; in RPC, select **Approve sign-in**. Approving starts a fresh login.
260
+ You can also use `--no-browser`. Declining, cancelling sign-in, or a failed token
261
+ exchange leaves your existing grant unchanged. Reloading configuration cancels
262
+ an active login. After signing in, explicitly retry the operation; sign-in never
263
+ replays it. Successful grants retain their requested scopes even when the service
264
+ omits the optional scope field from its token response. If previously granted
265
+ scopes are unknown, the review warns that they might not be retained. Configure
266
+ `oauthScopes` with the permissions you need rather than relying on unspecified
267
+ service defaults. After review, the sign-in dialog shows a scope count instead
268
+ of repeating the full list.
269
+
270
+ Scope names are untrusted service data, not instructions. Requests are bounded to
271
+ 100 scope names and kept only in this Pi session for ten minutes. Reloading
272
+ configuration, ending the session, or logging out discards them. Login uses the
273
+ configured server's OAuth discovery, not URLs supplied in a scope challenge.
274
+ Invalid or missing scope names require checking `oauthScopes` against the service's
275
+ requirements instead. Headless operation reports the challenge but cannot approve
276
+ additional permissions.
277
+
203
278
  ## Sign in remotely or without launching a browser
204
279
 
205
280
  When Pi runs over SSH, or you don't want it to launch a browser, use:
package/docs/commands.md CHANGED
@@ -20,7 +20,7 @@ The model's separate interface is documented in the [tool reference](tool-refere
20
20
  | `/mcp reload` | Apply configuration changes without restarting Pi. |
21
21
  | `/mcp enable <server>` | Enable a server in its effective configuration file. |
22
22
  | `/mcp disable <server>` | Disable a server, close its connection, and deactivate its tools. |
23
- | `/mcp login <server> [--no-browser]` | Authenticate an HTTP server without changing its configuration; optionally paste the callback URL in an interactive dialog. |
23
+ | `/mcp login <server> [--no-browser]` | Authenticate an HTTP server or review additional permissions it requested; optionally paste the callback URL in an interactive dialog. Configuration stays unchanged. |
24
24
  | `/mcp logout <server>` | Remove local OAuth credentials and attempt remote revocation, including for disabled servers. |
25
25
  | `/mcp reconnect <server>` | Replace a connection and refresh its catalog. |
26
26
  | `/mcp refresh <server>` | Refresh tool, resource, and prompt metadata without fetching content or loading additional tools. |
@@ -145,6 +145,7 @@ Windows paths with backslashes, single quotes preserve the path verbatim.
145
145
  | `--oauth-client-id ID` | Use a pre-registered public client. |
146
146
  | `--oauth-scope SCOPE` | Request an OAuth scope. Repeat for additional scopes. |
147
147
  | `--oauth-callback-port PORT` | Set the loopback callback port. |
148
+ | `--oauth-dpop` | Request DPoP-bound OAuth tokens using an OS-backed signing key. |
148
149
 
149
150
  Retain environment references rather than typing tokens:
150
151
 
@@ -137,6 +137,7 @@ server definition:
137
137
  | `oauthClientId` | Optional pre-registered public client ID. Supports `${ENV_VAR}` interpolation, not secret commands. |
138
138
  | `oauthScopes` | Optional array of 1–100 unique OAuth scope tokens to request at login. Omitted scopes use SDK/server defaults. Values are literal, without interpolation. |
139
139
  | `oauthCallbackPort` | Optional loopback callback port, from 1 to 65535. Defaults to `19847`. |
140
+ | `oauthDpop` | Opt in to DPoP proofs with ES256 keys stored in the OS keyring. Defaults to `false`. Run a fresh login after enabling it. |
140
141
  | `disabled` | Prevent this server from connecting or exposing tools and resources. |
141
142
  | `includeTools` | Optional allowlist of original MCP tool names; `*` matches any sequence. An empty list exposes no tools. |
142
143
  | `excludeTools` | Denylist applied after `includeTools`. |
@@ -145,7 +146,7 @@ server definition:
145
146
  | `toolTimeoutMs` | Optional timeout for tool calls only, from 100 to 600000 ms. Overrides `timeoutMs` for calls without changing metadata or resource deadlines. Time spent answering [server requests](behavior.md#server-requests-for-input) doesn't count. |
146
147
  | `protocol` | `auto` (default) for SDK protocol-version negotiation, or `legacy` for an explicit legacy handshake. |
147
148
 
148
- OAuth client IDs, scopes, and callback ports require HTTP without an Authorization
149
+ OAuth client IDs, scopes, callback ports, and DPoP require HTTP without an Authorization
149
150
  header. HTTP authentication is automatic; remove the obsolete `oauth` field from
150
151
  existing definitions. See [authentication](authentication.md).
151
152
 
@@ -45,6 +45,8 @@ unavailable server isn't an empty catalog.
45
45
  | `oauth_registration_rejected` | The server rejected dynamic registration. Check public/native client eligibility and the callback URL, or configure an approved public client ID. |
46
46
  | `oauth_client_rejected` | Check the client ID, app approval, and public-client authentication (token endpoint method `none`). A rejected client doesn't necessarily mean a client secret is required. |
47
47
  | `oauth_pkce_unsupported` | The authorization server must support S256 PKCE. Login without PKCE isn't supported. |
48
+ | `oauth_scope_required` | Run `/mcp login <server>` to review additional permissions, then explicitly retry the operation. |
49
+ | `oauth_dpop_unavailable` | Enable DPoP again, or log out before signing in if you disabled it deliberately or its stored key is corrupt or mismatched. |
48
50
  | `oauth_scope_rejected` | Check `oauthScopes` against the service's allowed scopes and app permissions. Reload after changes, then log in again. |
49
51
  | `oauth_grant_rejected` | Log in again for a fresh code. If it still fails, check the client and exact callback URL. |
50
52
  | `oauth_redirect_rejected` | Register the exact callback host, port, and `/callback` path shown by `/mcp get <server>`. Manual login uses the same callback URL. |
@@ -72,6 +74,14 @@ remain visible as content, even when the tool reports an error; they aren't
72
74
  sanitized transport diagnostics. Tool-call failures aren't replayed automatically;
73
75
  verify the outcome before retrying.
74
76
 
77
+ ## OAuth warnings in the terminal
78
+
79
+ The MCP SDK can print a sanitized warning when a refresh token expires or is
80
+ revoked. In an interactive terminal, this can temporarily disrupt the display
81
+ until Pi redraws it. Remote error details are removed, but these SDK warnings
82
+ can't currently be routed through Pi's UI. Follow the accompanying diagnostic
83
+ if another login is needed; the warning alone doesn't mean sign-in started.
84
+
75
85
  ## Missing project servers
76
86
 
77
87
  A project's `.mcp.json` loads only after an explicit trust decision, even in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-client",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "MCP tools for Pi, discovered on demand and called natively through the official SDK.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,8 +39,9 @@
39
39
  "prepack": "bun run build"
40
40
  },
41
41
  "dependencies": {
42
- "@modelcontextprotocol/client": "2.0.0",
42
+ "@modelcontextprotocol/client": "2.1.0",
43
43
  "@napi-rs/keyring": "^1.3.0",
44
+ "jose": "^6.1.3",
44
45
  "minisearch": "^7.2.0",
45
46
  "smol-toml": "^1.8.0"
46
47
  },
@@ -48,7 +49,7 @@
48
49
  "@earendil-works/pi-ai": "^0.85.1",
49
50
  "@earendil-works/pi-coding-agent": "^0.85.1",
50
51
  "@earendil-works/pi-tui": "^0.85.1",
51
- "@modelcontextprotocol/server": "2.0.0",
52
+ "@modelcontextprotocol/server": "2.1.0",
52
53
  "@types/bun": "^1.3.0",
53
54
  "@types/node": "^24.0.0",
54
55
  "esbuild": "^0.25.0",