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 +3 -2
- package/src/index.ts +11 -3
- package/src/server-manager.ts +5 -17
- package/src/tool-bridge.ts +2 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mcp-extension",
|
|
3
|
-
"version": "1.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": "^
|
|
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
|
|
622
|
-
return
|
|
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));
|
package/src/server-manager.ts
CHANGED
|
@@ -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
|
|
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
|
|
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,
|
package/src/tool-bridge.ts
CHANGED
|
@@ -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
|
|
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
|
|
770
|
+
const effectiveSignal = options?.signal;
|
|
792
771
|
if (explicitTimeoutMs !== undefined) {
|
|
793
772
|
this.serverTimeouts.set(serverName, explicitTimeoutMs);
|
|
794
773
|
} else {
|