pi-mcp-extension 1.9.9 → 1.9.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-extension",
3
- "version": "1.9.9",
3
+ "version": "1.9.11",
4
4
  "description": "MCP (Model Context Protocol) client extension for the Pi coding agent — connect Pi to any MCP server",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.32.1",
37
- "zod": "^3.25.0"
37
+ "zod": "^4.6.5"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-ai": "*",
@@ -45,6 +45,7 @@
45
45
  "@eslint/js": "^10.0.1",
46
46
  "@types/node": "^26.6.5",
47
47
  "eslint": "^10.13.0",
48
+ "fast-check": "^4.10.2",
48
49
  "tsx": "^4.19.4",
49
50
  "typescript": "^5.7.0",
50
51
  "typescript-eslint": "^8.71.2"
package/src/index.ts CHANGED
@@ -614,14 +614,22 @@ export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string;
614
614
  }
615
615
 
616
616
  manager.setToolRefreshCallback(async (serverName, client, options) => {
617
+ const server = manager.getServer(serverName);
618
+ const requestTimeoutMs =
619
+ options?.requestTimeoutMs ??
620
+ server?.config.requestTimeoutMs ??
621
+ config.settings.requestTimeoutMs;
617
622
  await bridge.refreshTools(
618
623
  serverName,
619
624
  client,
620
625
  () => {
621
- const server = manager.getServer(serverName);
622
- return server?.client === client && server.state !== "stopped";
626
+ const current = manager.getServer(serverName);
627
+ return current?.client === client && current.state !== "stopped";
628
+ },
629
+ {
630
+ ...options,
631
+ requestTimeoutMs,
623
632
  },
624
- options,
625
633
  );
626
634
  });
627
635
  manager.setServerStoppedCallback((name) => bridge.deactivateServer(name));
@@ -29,16 +29,10 @@ import { McpError, AuthCancelledError } from "./errors.js";
29
29
  import type { McpConfig, ServerConfig, Settings } from "./config.js";
30
30
  import { McpOAuthProvider, AuthRequiredError, getAuthStatus, resetAuth, invalidateStoredTokens } from "./oauth-provider.js";
31
31
  import { UnauthorizedError, type OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
32
- import {
33
- setClientDefaultOptions,
34
- getClientDefaultOptions,
35
- type ToolRefreshOptions,
36
- } from "./tool-bridge.js";
32
+ import type { ToolRefreshOptions } from "./tool-bridge.js";
37
33
  import { sanitizeTerminalText, type SanitizeTerminalTextOptions } from "./terminal-text.js";
38
34
 
39
35
  export {
40
- setClientDefaultOptions,
41
- getClientDefaultOptions,
42
36
  type ToolRefreshOptions,
43
37
  AuthCancelledError,
44
38
  sanitizeTerminalText,
@@ -99,7 +93,7 @@ export function isOAuthUnauthorizedError(error: unknown, seen = new Set<unknown>
99
93
 
100
94
  // Reject messages that explicitly report a non-401/403 HTTP status code
101
95
  const httpStatusMatch = msg.match(
102
- /\b(?:HTTP\s+|status(?:\s+code)?[:\s]+|(?:POSTing|GETting|connecting)\s+to\s+\S+:\s*|(?:Non-200\s+status\s+code\s*\())(\d{3})\b/i,
96
+ /\b(?:HTTP(?:\/\d(?:\.\d)?)?\s+|status(?:\s+code)?[:\s]+|(?:POSTing|GETting|connecting)\s+to\s+\S+:\s*|(?:Non-200\s+status\s+code\s*\())(\d{3})\b/i,
103
97
  );
104
98
  if (httpStatusMatch) {
105
99
  const status = Number(httpStatusMatch[1]);
@@ -112,18 +106,17 @@ export function isOAuthUnauthorizedError(error: unknown, seen = new Set<unknown>
112
106
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
113
107
  /^\s*401\s*$/.test(msg) ||
114
108
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
115
- /\bHTTP\s+401\b/i.test(msg) ||
109
+ /\bHTTP(?:\/\d(?:\.\d)?)?[:\s]+401\b/i.test(msg) ||
116
110
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
117
111
  /\bstatus(?:\s+code)?[:\s(]+401\b/i.test(msg) ||
118
112
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
113
+ /\b(?:POSTing|GETting|connecting)\s+to\s+\S+:\s*401\b/i.test(msg) ||
114
+ // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
119
115
  /\breturned\s+401\b/i.test(msg) ||
120
116
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
121
117
  /\b401\s+Unauthorized\b/i.test(msg) ||
122
118
  // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
123
- /:\s*401\b/.test(msg) ||
124
- // nosemgrep: pi-mcp.no-401-substring -- WHY: intentional message-text fallback after structured checks for SDK paths that only surface text
125
119
  /\(401\)/.test(msg) ||
126
- /\bUnauthorized\b/i.test(msg) ||
127
120
  /\binvalid_token\b/i.test(msg)
128
121
  ) {
129
122
  return true;
@@ -820,10 +813,6 @@ export class ServerManager {
820
813
  if (!signal.aborted && this.onToolRefresh && server.state === "ready" && server.client === client) {
821
814
  try {
822
815
  const refreshTimeoutMs = this._resolveTimeoutMs(server);
823
- setClientDefaultOptions(server.client, {
824
- requestTimeoutMs: refreshTimeoutMs,
825
- signal,
826
- });
827
816
  await this.onToolRefresh(server.name, server.client, {
828
817
  requestTimeoutMs: refreshTimeoutMs,
829
818
  signal,
@@ -860,7 +849,6 @@ export class ServerManager {
860
849
  server.childPid = transport.pid;
861
850
  }
862
851
  // Readiness includes tool discovery, not just the MCP handshake.
863
- setClientDefaultOptions(client, { requestTimeoutMs: timeoutMs, signal });
864
852
  if (this.onToolRefresh) {
865
853
  await this.onToolRefresh(server.name, client, {
866
854
  requestTimeoutMs: timeoutMs,
@@ -32,26 +32,6 @@ export type ToolRefreshOptions = {
32
32
  signal?: AbortSignal | undefined;
33
33
  };
34
34
 
35
- // WHY: Store per-client default { requestTimeoutMs, signal } in a WeakMap so 2-argument
36
- // onToolRefresh callbacks ((name, client) => bridge.refreshTools(name, client)) still
37
- // inherit the server's configured requestTimeoutMs and handshake AbortSignal.
38
- const clientRefreshDefaults = new WeakMap<Client, ToolRefreshOptions>();
39
-
40
- export function setClientDefaultOptions(client: Client, options: ToolRefreshOptions): void {
41
- if (client && typeof client === "object") {
42
- clientRefreshDefaults.set(client, options);
43
- }
44
- }
45
-
46
- export function getClientDefaultOptions(client: Client): ToolRefreshOptions | undefined {
47
- if (client && typeof client === "object") {
48
- return clientRefreshDefaults.get(client);
49
- }
50
- return undefined;
51
- }
52
-
53
-
54
-
55
35
  // ─── Schema Conversion ────────────────────────────────────────────────────────
56
36
 
57
37
  const KIND_SYMBOL = Symbol.for("TypeBox.Kind");
@@ -785,10 +765,9 @@ export class ToolBridge {
785
765
  ): Promise<void> {
786
766
  const isCurrent = typeof isCurrentOrOptions === "function" ? isCurrentOrOptions : () => true;
787
767
  const options = typeof isCurrentOrOptions === "object" ? isCurrentOrOptions : maybeOptions;
788
- const defaults = getClientDefaultOptions(client);
789
- const explicitTimeoutMs = options?.requestTimeoutMs ?? defaults?.requestTimeoutMs;
768
+ const explicitTimeoutMs = options?.requestTimeoutMs;
790
769
  const effectiveTimeoutMs = explicitTimeoutMs ?? this.settings.requestTimeoutMs;
791
- const effectiveSignal = options?.signal ?? defaults?.signal;
770
+ const effectiveSignal = options?.signal;
792
771
  if (explicitTimeoutMs !== undefined) {
793
772
  this.serverTimeouts.set(serverName, explicitTimeoutMs);
794
773
  } else {