@herbertgao/pi-extensions 2026.9.16 → 2026.9.18
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/README.md +14 -15
- package/node_modules/@herbertgao/pi-bark/package.json +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +75 -75
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/resume-from/package.json +4 -2
- package/node_modules/@herbertgao/sol-pi/package.json +6 -6
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +11 -53
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +99 -99
- package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
- package/node_modules/@tifan/pi-handoff/package.json +1 -1
- package/node_modules/@tifan/pi-handoff/src/index.ts +56 -26
- package/node_modules/@tifan/pi-mermaid-open/package.json +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
- package/node_modules/@tifan/pi-recap/package.json +1 -1
- package/node_modules/@tifan/pi-rename/package.json +1 -1
- package/node_modules/pi-antigravity/package.json +2 -2
- package/node_modules/pi-lens/package.json +158 -158
- package/node_modules/pi-multi-account/package.json +2 -2
- package/node_modules/pi-typesafe/README.md +1 -1
- package/node_modules/pi-typesafe/dist/auth.d.ts +9 -5
- package/node_modules/pi-typesafe/dist/auth.js +7 -3
- package/node_modules/pi-typesafe/dist/backends.d.ts +33 -3
- package/node_modules/pi-typesafe/dist/backends.js +95 -1
- package/node_modules/pi-typesafe/dist/client.d.ts +5 -5
- package/node_modules/pi-typesafe/dist/client.js +12 -9
- package/node_modules/pi-typesafe/dist/credentials.d.ts +14 -8
- package/node_modules/pi-typesafe/dist/credentials.js +10 -7
- package/node_modules/pi-typesafe/dist/errors.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/dist/login.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/login.js +2 -2
- package/node_modules/pi-typesafe/package.json +5 -5
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/remote-pi/package.json +5 -1
- package/package.json +16 -28
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +0 -1013
- package/node_modules/pi-mcp-adapter/LICENSE +0 -21
- package/node_modules/pi-mcp-adapter/OAUTH.md +0 -369
- package/node_modules/pi-mcp-adapter/README.md +0 -1021
- package/node_modules/pi-mcp-adapter/abort.ts +0 -34
- package/node_modules/pi-mcp-adapter/agent-dir.ts +0 -68
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +0 -416
- package/node_modules/pi-mcp-adapter/agent-plugin-provenance.ts +0 -36
- package/node_modules/pi-mcp-adapter/app-bridge.bundle.js +0 -67
- package/node_modules/pi-mcp-adapter/banner.png +0 -0
- package/node_modules/pi-mcp-adapter/bearer-command-resolver.ts +0 -193
- package/node_modules/pi-mcp-adapter/claude-plugin-loader.ts +0 -365
- package/node_modules/pi-mcp-adapter/cli.js +0 -454
- package/node_modules/pi-mcp-adapter/commands.ts +0 -861
- package/node_modules/pi-mcp-adapter/config.ts +0 -1565
- package/node_modules/pi-mcp-adapter/consent-manager.ts +0 -95
- package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +0 -244
- package/node_modules/pi-mcp-adapter/direct-tools.ts +0 -428
- package/node_modules/pi-mcp-adapter/dist/abort.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/abort.js +0 -36
- package/node_modules/pi-mcp-adapter/dist/abort.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +0 -10
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js +0 -63
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +0 -8
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +0 -398
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.d.ts +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.js +0 -31
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js +0 -194
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.d.ts +0 -9
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.js +0 -362
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +0 -134
- package/node_modules/pi-mcp-adapter/dist/config.js +0 -1361
- package/node_modules/pi-mcp-adapter/dist/config.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/consent-manager.d.ts +0 -18
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js +0 -88
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js +0 -316
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/errors.d.ts +0 -131
- package/node_modules/pi-mcp-adapter/dist/errors.js +0 -278
- package/node_modules/pi-mcp-adapter/dist/errors.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/http-ca.d.ts +0 -8
- package/node_modules/pi-mcp-adapter/dist/http-ca.js +0 -88
- package/node_modules/pi-mcp-adapter/dist/http-ca.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-client.d.ts +0 -14
- package/node_modules/pi-mcp-adapter/dist/jev-client.js +0 -367
- package/node_modules/pi-mcp-adapter/dist/jev-client.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.d.ts +0 -77
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js +0 -2
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.d.ts +0 -51
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js +0 -205
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +0 -68
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/lifecycle.d.ts +0 -56
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js +0 -410
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/logger.d.ts +0 -51
- package/node_modules/pi-mcp-adapter/dist/logger.js +0 -131
- package/node_modules/pi-mcp-adapter/dist/logger.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.d.ts +0 -26
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js +0 -103
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.d.ts +0 -118
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js +0 -1097
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +0 -170
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +0 -1142
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +0 -24
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +0 -203
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.d.ts +0 -53
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js +0 -441
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.d.ts +0 -148
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js +0 -689
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js +0 -166
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.d.ts +0 -102
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js +0 -369
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.d.ts +0 -95
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js +0 -242
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +0 -19
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +0 -301
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js +0 -505
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +0 -144
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.d.ts +0 -10
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js +0 -312
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +0 -1
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js +0 -15
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.d.ts +0 -13
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js +0 -97
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js +0 -203
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.d.ts +0 -11
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js +0 -172
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/server-manager.d.ts +0 -157
- package/node_modules/pi-mcp-adapter/dist/server-manager.js +0 -1745
- package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/session-approvals.d.ts +0 -34
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js +0 -140
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/session-recovery.d.ts +0 -26
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js +0 -142
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/state.d.ts +0 -73
- package/node_modules/pi-mcp-adapter/dist/state.js +0 -2
- package/node_modules/pi-mcp-adapter/dist/state.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +0 -642
- package/node_modules/pi-mcp-adapter/dist/types.js +0 -331
- package/node_modules/pi-mcp-adapter/dist/types.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -37
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js +0 -219
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +0 -104
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +0 -59
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +0 -4
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +0 -27
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.d.ts +0 -15
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js +0 -85
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +0 -41
- package/node_modules/pi-mcp-adapter/dist/utils.js +0 -417
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +0 -1
- package/node_modules/pi-mcp-adapter/elicitation-handler.ts +0 -358
- package/node_modules/pi-mcp-adapter/error-signal.ts +0 -32
- package/node_modules/pi-mcp-adapter/errors.ts +0 -364
- package/node_modules/pi-mcp-adapter/examples/jev-accessibility-loop.mjs +0 -85
- package/node_modules/pi-mcp-adapter/examples/jev-semantic-filter.mjs +0 -34
- package/node_modules/pi-mcp-adapter/failure-backoff.ts +0 -23
- package/node_modules/pi-mcp-adapter/glimpse-ui.ts +0 -80
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -515
- package/node_modules/pi-mcp-adapter/http-ca.ts +0 -84
- package/node_modules/pi-mcp-adapter/index.ts +0 -2106
- package/node_modules/pi-mcp-adapter/init.ts +0 -721
- package/node_modules/pi-mcp-adapter/jev-client.ts +0 -300
- package/node_modules/pi-mcp-adapter/jev-contracts.ts +0 -53
- package/node_modules/pi-mcp-adapter/jev-key-store.ts +0 -192
- package/node_modules/pi-mcp-adapter/json-schema-validator.ts +0 -83
- package/node_modules/pi-mcp-adapter/lazy-loader.ts +0 -7
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -457
- package/node_modules/pi-mcp-adapter/logger.ts +0 -169
- package/node_modules/pi-mcp-adapter/mcp-auth-fetch.ts +0 -113
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +0 -1296
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +0 -1287
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -250
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +0 -534
- package/node_modules/pi-mcp-adapter/mcp-code.ts +0 -495
- package/node_modules/pi-mcp-adapter/mcp-install.ts +0 -60
- package/node_modules/pi-mcp-adapter/mcp-keyring-helper.cjs +0 -89
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +0 -819
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +0 -511
- package/node_modules/pi-mcp-adapter/mcp-panel-theme.ts +0 -104
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +0 -1099
- package/node_modules/pi-mcp-adapter/mcp-probe.ts +0 -201
- package/node_modules/pi-mcp-adapter/mcp-references.ts +0 -373
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +0 -137
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +0 -747
- package/node_modules/pi-mcp-adapter/mcp-status.ts +0 -105
- package/node_modules/pi-mcp-adapter/mcp-tasks.ts +0 -467
- package/node_modules/pi-mcp-adapter/mcp-trace.ts +0 -306
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +0 -362
- package/node_modules/pi-mcp-adapter/namespace-tools.ts +0 -334
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +0 -543
- package/node_modules/pi-mcp-adapter/oauth-handler.ts +0 -30
- package/node_modules/pi-mcp-adapter/oauth.ts +0 -52
- package/node_modules/pi-mcp-adapter/onboarding-state.ts +0 -76
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +0 -156
- package/node_modules/pi-mcp-adapter/package.json +0 -219
- package/node_modules/pi-mcp-adapter/panel-keys.ts +0 -53
- package/node_modules/pi-mcp-adapter/prompts.ts +0 -385
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +0 -1735
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +0 -333
- package/node_modules/pi-mcp-adapter/resource-tools.ts +0 -17
- package/node_modules/pi-mcp-adapter/runtime-owner.ts +0 -106
- package/node_modules/pi-mcp-adapter/sampling-handler.ts +0 -261
- package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +0 -144
- package/node_modules/pi-mcp-adapter/search-ranking.ts +0 -309
- package/node_modules/pi-mcp-adapter/secure-keyring.ts +0 -170
- package/node_modules/pi-mcp-adapter/semantic-search.ts +0 -186
- package/node_modules/pi-mcp-adapter/server-manager.ts +0 -2088
- package/node_modules/pi-mcp-adapter/session-approvals.ts +0 -201
- package/node_modules/pi-mcp-adapter/session-recovery.ts +0 -158
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +0 -49
- package/node_modules/pi-mcp-adapter/state.ts +0 -71
- package/node_modules/pi-mcp-adapter/tool-approval.ts +0 -205
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +0 -302
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +0 -255
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +0 -573
- package/node_modules/pi-mcp-adapter/ts-shape.ts +0 -149
- package/node_modules/pi-mcp-adapter/types.ts +0 -1120
- package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +0 -40
- package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +0 -266
- package/node_modules/pi-mcp-adapter/ui-server.ts +0 -993
- package/node_modules/pi-mcp-adapter/ui-session.ts +0 -624
- package/node_modules/pi-mcp-adapter/ui-stream-types.ts +0 -89
- package/node_modules/pi-mcp-adapter/ui-tool-visibility.ts +0 -25
- package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +0 -85
- package/node_modules/pi-mcp-adapter/utils.ts +0 -464
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import { getToolUiResourceUri } from "./ui-app-bridge-helpers.ts";
|
|
2
|
-
import type { McpExtensionState } from "./state.ts";
|
|
3
|
-
import type { ToolMetadata, McpTool, McpResource, ServerEntry, ToolPrefix } from "./types.ts";
|
|
4
|
-
import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates, isToolAllowed, resolveToolPrefix, resolveUniqueNameOwnership } from "./types.ts";
|
|
5
|
-
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
6
|
-
import { extractToolUiStreamMode } from "./utils.ts";
|
|
7
|
-
import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
8
|
-
|
|
9
|
-
export function buildToolMetadata(
|
|
10
|
-
tools: McpTool[],
|
|
11
|
-
resources: McpResource[],
|
|
12
|
-
definition: ServerEntry,
|
|
13
|
-
serverName: string,
|
|
14
|
-
prefix: ToolPrefix,
|
|
15
|
-
configuredServers?: Record<string, ServerEntry>,
|
|
16
|
-
knownMetadata?: Map<string, ToolMetadata[]>,
|
|
17
|
-
includeMissingConfiguredCandidates = false,
|
|
18
|
-
): { metadata: ToolMetadata[]; failedTools: string[] } {
|
|
19
|
-
const metadata: ToolMetadata[] = [];
|
|
20
|
-
const failedTools: string[] = [];
|
|
21
|
-
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
22
|
-
const hasToolFilters =
|
|
23
|
-
(Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||
|
|
24
|
-
(Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0);
|
|
25
|
-
const selectorCandidateIndex = hasToolFilters && configuredServers ? (() => {
|
|
26
|
-
const candidates = new Set<string>();
|
|
27
|
-
const additionalCandidatesByToolName = new Map<string, Set<string>>();
|
|
28
|
-
const evaluatedToolNames = new Set<string>();
|
|
29
|
-
const addCandidates = (
|
|
30
|
-
target: Set<string>,
|
|
31
|
-
originalName: string,
|
|
32
|
-
candidateServerName: string,
|
|
33
|
-
candidatePrefix: ToolPrefix,
|
|
34
|
-
) => {
|
|
35
|
-
for (const candidate of getToolNameCandidates(originalName, candidateServerName, candidatePrefix, false)) target.add(candidate);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
for (const tool of tools) {
|
|
39
|
-
if (!tool?.name) continue;
|
|
40
|
-
evaluatedToolNames.add(tool.name);
|
|
41
|
-
addCandidates(candidates, tool.name, serverName, effectivePrefix);
|
|
42
|
-
}
|
|
43
|
-
if (definition.exposeResources !== false) {
|
|
44
|
-
for (const resource of resources) {
|
|
45
|
-
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
46
|
-
evaluatedToolNames.add(baseName);
|
|
47
|
-
if (resource?.name && resource?.uri) addCandidates(candidates, baseName, serverName, effectivePrefix);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
for (const [otherServerName, otherDefinition] of Object.entries(configuredServers)) {
|
|
51
|
-
if (otherServerName === serverName) continue;
|
|
52
|
-
const otherPrefix = resolveToolPrefix(otherDefinition, prefix);
|
|
53
|
-
const knownTools = knownMetadata?.get(otherServerName);
|
|
54
|
-
if (knownTools) {
|
|
55
|
-
for (const tool of knownTools) {
|
|
56
|
-
candidates.add(tool.name);
|
|
57
|
-
addCandidates(candidates, tool.originalName, otherServerName, otherPrefix);
|
|
58
|
-
}
|
|
59
|
-
} else if (!knownMetadata || includeMissingConfiguredCandidates) {
|
|
60
|
-
for (const toolName of evaluatedToolNames) {
|
|
61
|
-
let additionalCandidates = additionalCandidatesByToolName.get(toolName);
|
|
62
|
-
if (!additionalCandidates) {
|
|
63
|
-
additionalCandidates = new Set<string>();
|
|
64
|
-
additionalCandidatesByToolName.set(toolName, additionalCandidates);
|
|
65
|
-
}
|
|
66
|
-
addCandidates(additionalCandidates, toolName, otherServerName, otherPrefix);
|
|
67
|
-
if (includeMissingConfiguredCandidates) {
|
|
68
|
-
for (const candidate of getToolNameCandidates(toolName, otherServerName, otherPrefix, false)) {
|
|
69
|
-
additionalCandidates.add(candidate.replace(/-/g, "_"));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return createToolSelectorCandidateIndex(candidates, additionalCandidatesByToolName);
|
|
76
|
-
})() : undefined;
|
|
77
|
-
|
|
78
|
-
for (const tool of tools) {
|
|
79
|
-
if (!tool?.name) {
|
|
80
|
-
failedTools.push("(unnamed)");
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const name = formatToolName(tool.name, serverName, effectivePrefix);
|
|
88
|
-
const uiVisibility = extractUiToolVisibility(tool._meta);
|
|
89
|
-
if (!isUiToolVisibleToModel(uiVisibility)) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
let uiResourceUri: string | undefined;
|
|
93
|
-
try {
|
|
94
|
-
uiResourceUri = getToolUiResourceUri({ _meta: tool._meta });
|
|
95
|
-
} catch {
|
|
96
|
-
failedTools.push(tool.name);
|
|
97
|
-
}
|
|
98
|
-
const uiStreamMode = extractToolUiStreamMode(tool._meta);
|
|
99
|
-
metadata.push({
|
|
100
|
-
name,
|
|
101
|
-
originalName: tool.name,
|
|
102
|
-
description: tool.description ?? "",
|
|
103
|
-
...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
|
|
104
|
-
...(tool.outputSchema !== undefined ? { outputSchema: tool.outputSchema } : {}),
|
|
105
|
-
...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
|
|
106
|
-
...(uiVisibility !== undefined ? { uiVisibility } : {}),
|
|
107
|
-
...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (definition.exposeResources !== false) {
|
|
112
|
-
for (const resource of resources) {
|
|
113
|
-
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
114
|
-
if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const name = formatToolName(baseName, serverName, effectivePrefix);
|
|
119
|
-
metadata.push({
|
|
120
|
-
name,
|
|
121
|
-
originalName: baseName,
|
|
122
|
-
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
123
|
-
resourceUri: resource.uri,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const ownership = resolveUniqueNameOwnership(metadata, (tool) => tool.name);
|
|
129
|
-
for (const colliding of ownership.collisions.values()) {
|
|
130
|
-
failedTools.push(...colliding.map((tool) => tool.originalName));
|
|
131
|
-
}
|
|
132
|
-
return { metadata: ownership.unique, failedTools };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function getToolNames(state: McpExtensionState, serverName: string): string[] {
|
|
136
|
-
return state.toolMetadata.get(serverName)?.map(m => m.name) ?? [];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function totalToolCount(state: McpExtensionState): number {
|
|
140
|
-
let count = 0;
|
|
141
|
-
for (const metadata of state.toolMetadata.values()) {
|
|
142
|
-
count += metadata.length;
|
|
143
|
-
}
|
|
144
|
-
return count;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function findToolByName(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | undefined {
|
|
148
|
-
if (!metadata) return undefined;
|
|
149
|
-
const exact = metadata.find(m => m.name === toolName);
|
|
150
|
-
if (exact) return exact;
|
|
151
|
-
const normalized = toolName.replace(/-/g, "_");
|
|
152
|
-
return metadata.find(m => m.name.replace(/-/g, "_") === normalized);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/** Whether the schema formatter has field descriptions worth showing beside a compact shape. */
|
|
156
|
-
export function hasSchemaDescriptions(schema: unknown, includeRoot = false): boolean {
|
|
157
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return false;
|
|
158
|
-
const s = schema as Record<string, unknown>;
|
|
159
|
-
if (includeRoot && typeof s.description === "string" && s.description.length > 0) return true;
|
|
160
|
-
const hasNestedDescription = (child: unknown) => hasSchemaDescriptions(child, true);
|
|
161
|
-
if (s.properties && typeof s.properties === "object" && !Array.isArray(s.properties)
|
|
162
|
-
&& Object.values(s.properties).some(hasNestedDescription)) return true;
|
|
163
|
-
return hasNestedDescription(s.items)
|
|
164
|
-
|| (Array.isArray(s.anyOf) && s.anyOf.some(hasNestedDescription))
|
|
165
|
-
|| (Array.isArray(s.oneOf) && s.oneOf.some(hasNestedDescription));
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function formatSchema(schema: unknown, indent = " "): string {
|
|
169
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
170
|
-
return `${indent}(no schema)`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const s = schema as Record<string, unknown>;
|
|
174
|
-
|
|
175
|
-
if (s.type === "object" && s.properties && typeof s.properties === "object" && !Array.isArray(s.properties)) {
|
|
176
|
-
const props = s.properties as Record<string, unknown>;
|
|
177
|
-
const required = Array.isArray(s.required) ? s.required.filter((name): name is string => typeof name === "string") : [];
|
|
178
|
-
|
|
179
|
-
if (Object.keys(props).length === 0) {
|
|
180
|
-
return `${indent}(no parameters)`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const lines: string[] = [];
|
|
184
|
-
for (const [name, propSchema] of Object.entries(props)) {
|
|
185
|
-
lines.push(...formatProperty(name, propSchema, required.includes(name), indent));
|
|
186
|
-
}
|
|
187
|
-
return lines.join("\n");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const lines = formatNestedSchema(s, indent);
|
|
191
|
-
if (lines.length > 0) {
|
|
192
|
-
return lines.join("\n");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const typeStr = formatType(s);
|
|
196
|
-
if (typeStr) {
|
|
197
|
-
return `${indent}(${typeStr})`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return `${indent}(complex schema)`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function formatProperty(name: string, schema: unknown, required: boolean, indent: string): string[] {
|
|
204
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
205
|
-
return [`${indent}${name}${required ? " *required*" : ""}`];
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const s = schema as Record<string, unknown>;
|
|
209
|
-
const parts = [`${indent}${name}`];
|
|
210
|
-
const typeStr = formatType(s);
|
|
211
|
-
if (typeStr) parts.push(`(${typeStr})`);
|
|
212
|
-
if (required) parts.push("*required*");
|
|
213
|
-
appendSchemaAnnotations(parts, s);
|
|
214
|
-
|
|
215
|
-
return [parts.join(" "), ...formatNestedSchema(s, `${indent} `)];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function formatNestedSchema(schema: Record<string, unknown>, indent: string): string[] {
|
|
219
|
-
const lines: string[] = [];
|
|
220
|
-
|
|
221
|
-
if (Array.isArray(schema.anyOf)) {
|
|
222
|
-
lines.push(...formatVariants("anyOf", schema.anyOf, indent));
|
|
223
|
-
}
|
|
224
|
-
if (Array.isArray(schema.oneOf)) {
|
|
225
|
-
lines.push(...formatVariants("oneOf", schema.oneOf, indent));
|
|
226
|
-
}
|
|
227
|
-
if (schema.items !== undefined) {
|
|
228
|
-
lines.push(...formatProperty("items", schema.items, false, indent));
|
|
229
|
-
}
|
|
230
|
-
if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
|
|
231
|
-
const required = Array.isArray(schema.required) ? schema.required.filter((name): name is string => typeof name === "string") : [];
|
|
232
|
-
for (const [name, propSchema] of Object.entries(schema.properties as Record<string, unknown>)) {
|
|
233
|
-
lines.push(...formatProperty(name, propSchema, required.includes(name), indent));
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return lines;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function formatVariants(keyword: "anyOf" | "oneOf", variants: unknown[], indent: string): string[] {
|
|
241
|
-
const lines = [`${indent}${keyword}:`];
|
|
242
|
-
|
|
243
|
-
for (const variant of variants) {
|
|
244
|
-
if (!variant || typeof variant !== "object" || Array.isArray(variant)) {
|
|
245
|
-
lines.push(`${indent} - ${JSON.stringify(variant)}`);
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const s = variant as Record<string, unknown>;
|
|
250
|
-
const typeStr = formatType(s) || "schema";
|
|
251
|
-
const parts = [`${indent} - ${typeStr}`];
|
|
252
|
-
appendSchemaAnnotations(parts, s);
|
|
253
|
-
lines.push(parts.join(" "));
|
|
254
|
-
lines.push(...formatNestedSchema(s, `${indent} `));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return lines;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function formatType(schema: Record<string, unknown>): string {
|
|
261
|
-
if (Object.hasOwn(schema, "const")) {
|
|
262
|
-
return `const ${JSON.stringify(schema.const)}`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (Array.isArray(schema.enum)) {
|
|
266
|
-
return `enum: ${schema.enum.map(v => JSON.stringify(v)).join(", ")}`;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (Array.isArray(schema.type)) {
|
|
270
|
-
return schema.type.map(type => String(type)).join(" | ");
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (schema.type) {
|
|
274
|
-
return String(schema.type);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
|
|
278
|
-
return "object";
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (schema.items !== undefined) {
|
|
282
|
-
return "array";
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return "";
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function appendSchemaAnnotations(parts: string[], schema: Record<string, unknown>): void {
|
|
289
|
-
if (schema.description && typeof schema.description === "string") {
|
|
290
|
-
parts.push(`- ${schema.description}`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
for (const key of ["minLength", "maxLength", "minimum", "maximum", "minItems", "maxItems", "format", "pattern"] as const) {
|
|
294
|
-
if (schema[key] !== undefined) {
|
|
295
|
-
parts.push(`[${key}: ${JSON.stringify(schema[key])}]`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (schema.default !== undefined) {
|
|
300
|
-
parts.push(`[default: ${JSON.stringify(schema.default)}]`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
// tool-registrar.ts - MCP content transformation
|
|
2
|
-
// NOTE: Tools are NOT registered with Pi - only the unified `mcp` proxy tool is registered.
|
|
3
|
-
// This keeps the LLM context small (1 tool instead of 100s).
|
|
4
|
-
|
|
5
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import type { McpContent, ContentBlock } from "./types.ts";
|
|
9
|
-
|
|
10
|
-
const MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;
|
|
11
|
-
const MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;
|
|
12
|
-
const MAX_SESSION_RESOURCE_FILES = 10_000; // Bounds metadata from empty or tiny resources.
|
|
13
|
-
const CLEANUP_RETRY_DELAY_MS = 30_000;
|
|
14
|
-
const MAX_CLEANUP_RETRY_ATTEMPTS = 3;
|
|
15
|
-
type MaterializedResourceSession = {
|
|
16
|
-
directory: string | undefined;
|
|
17
|
-
bytes: number;
|
|
18
|
-
files: number;
|
|
19
|
-
sequence: number;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function createMaterializedResourceSession(): MaterializedResourceSession {
|
|
23
|
-
return {
|
|
24
|
-
directory: undefined,
|
|
25
|
-
bytes: 0,
|
|
26
|
-
files: 0,
|
|
27
|
-
sequence: 0,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const defaultMaterializedResourceSession = createMaterializedResourceSession();
|
|
32
|
-
const scopedMaterializedResourceSessions = new WeakMap<object, MaterializedResourceSession>();
|
|
33
|
-
const pendingCleanupDirectories = new Set<string>();
|
|
34
|
-
const cleanupRetryAttempts = new Map<string, number>();
|
|
35
|
-
let pendingCleanupRetry: ReturnType<typeof setTimeout> | undefined;
|
|
36
|
-
|
|
37
|
-
function isAbortedScope(scope: object | undefined): boolean {
|
|
38
|
-
return !!scope && "aborted" in scope && (scope as { aborted?: unknown }).aborted === true;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getMaterializedResourceSession(scope?: object): MaterializedResourceSession | undefined {
|
|
42
|
-
if (isAbortedScope(scope)) return undefined;
|
|
43
|
-
if (!scope) return defaultMaterializedResourceSession;
|
|
44
|
-
let session = scopedMaterializedResourceSessions.get(scope);
|
|
45
|
-
if (!session) {
|
|
46
|
-
session = createMaterializedResourceSession();
|
|
47
|
-
scopedMaterializedResourceSessions.set(scope, session);
|
|
48
|
-
}
|
|
49
|
-
return session;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type BinaryResource = { uri?: string | undefined; text?: string | undefined; mimeType?: string | undefined; blob: string };
|
|
53
|
-
type McpResourceContent = {
|
|
54
|
-
uri: string;
|
|
55
|
-
text?: string | undefined;
|
|
56
|
-
blob?: string | undefined;
|
|
57
|
-
mimeType?: string | undefined;
|
|
58
|
-
[key: string]: unknown;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function hasRetryableCleanupDirectory(): boolean {
|
|
62
|
-
for (const directory of pendingCleanupDirectories) {
|
|
63
|
-
if ((cleanupRetryAttempts.get(directory) ?? 0) < MAX_CLEANUP_RETRY_ATTEMPTS) return true;
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function schedulePendingCleanupRetry(): void {
|
|
69
|
-
if (pendingCleanupRetry || !hasRetryableCleanupDirectory()) return;
|
|
70
|
-
for (const directory of pendingCleanupDirectories) {
|
|
71
|
-
const attempts = cleanupRetryAttempts.get(directory) ?? 0;
|
|
72
|
-
if (attempts < MAX_CLEANUP_RETRY_ATTEMPTS) cleanupRetryAttempts.set(directory, attempts + 1);
|
|
73
|
-
}
|
|
74
|
-
pendingCleanupRetry = setTimeout(() => {
|
|
75
|
-
pendingCleanupRetry = undefined;
|
|
76
|
-
try {
|
|
77
|
-
drainPendingCleanupDirectories();
|
|
78
|
-
} catch {
|
|
79
|
-
// drainPendingCleanupDirectories already retained the paths and rescheduled another retry.
|
|
80
|
-
}
|
|
81
|
-
}, CLEANUP_RETRY_DELAY_MS);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function drainPendingCleanupDirectories(): void {
|
|
85
|
-
const failures: unknown[] = [];
|
|
86
|
-
for (const directory of Array.from(pendingCleanupDirectories)) {
|
|
87
|
-
try {
|
|
88
|
-
rmSync(directory, { recursive: true, force: true });
|
|
89
|
-
pendingCleanupDirectories.delete(directory);
|
|
90
|
-
cleanupRetryAttempts.delete(directory);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
failures.push(error);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (pendingCleanupDirectories.size === 0 && pendingCleanupRetry) {
|
|
96
|
-
clearTimeout(pendingCleanupRetry);
|
|
97
|
-
pendingCleanupRetry = undefined;
|
|
98
|
-
}
|
|
99
|
-
if (failures.length > 0) {
|
|
100
|
-
schedulePendingCleanupRetry();
|
|
101
|
-
throw new AggregateError(failures, "Failed to clean materialized MCP resources");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function cleanupMaterializedBinaryResources(scope?: object): void {
|
|
106
|
-
const session = scope ? scopedMaterializedResourceSessions.get(scope) : defaultMaterializedResourceSession;
|
|
107
|
-
if (session?.directory) pendingCleanupDirectories.add(session.directory);
|
|
108
|
-
if (session) {
|
|
109
|
-
session.directory = undefined;
|
|
110
|
-
session.bytes = 0;
|
|
111
|
-
session.files = 0;
|
|
112
|
-
session.sequence = 0;
|
|
113
|
-
if (scope) scopedMaterializedResourceSessions.delete(scope);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
drainPendingCleanupDirectories();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function replaceBlob(resource: BinaryResource, text: string): string {
|
|
120
|
-
delete (resource as Partial<BinaryResource>).blob;
|
|
121
|
-
resource.text = text;
|
|
122
|
-
return text;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function omitBinaryResource(resource: BinaryResource, reason: string): string {
|
|
126
|
-
return replaceBlob(resource, [
|
|
127
|
-
`[Resource: ${resource.uri ?? "(no URI)"}]`,
|
|
128
|
-
`Binary content omitted: ${reason}`,
|
|
129
|
-
`MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
|
|
130
|
-
].join("\n"));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function materializeBinaryResource(resource: BinaryResource, scope?: object): string {
|
|
134
|
-
const session = getMaterializedResourceSession(scope);
|
|
135
|
-
if (!session) return omitBinaryResource(resource, "runtime stopped");
|
|
136
|
-
const decodedBytes = Buffer.byteLength(resource.blob, "base64");
|
|
137
|
-
if (decodedBytes > MAX_BINARY_RESOURCE_BYTES) {
|
|
138
|
-
return omitBinaryResource(resource, "decoded size exceeds 10 MiB");
|
|
139
|
-
}
|
|
140
|
-
if (
|
|
141
|
-
session.bytes + decodedBytes > MAX_SESSION_RESOURCE_BYTES ||
|
|
142
|
-
session.files >= MAX_SESSION_RESOURCE_FILES
|
|
143
|
-
) {
|
|
144
|
-
return omitBinaryResource(resource, "session resource limit reached");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
session.directory ??= mkdtempSync(join(tmpdir(), "pi-mcp-resource-"));
|
|
149
|
-
} catch {
|
|
150
|
-
return omitBinaryResource(resource, "could not be saved");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const filePath = join(session.directory, `resource-${++session.sequence}.bin`);
|
|
154
|
-
session.bytes += decodedBytes;
|
|
155
|
-
session.files += 1;
|
|
156
|
-
try {
|
|
157
|
-
writeFileSync(filePath, Buffer.from(resource.blob, "base64"), { flag: "wx", mode: 0o600 });
|
|
158
|
-
} catch {
|
|
159
|
-
try {
|
|
160
|
-
rmSync(filePath, { force: true });
|
|
161
|
-
session.bytes -= decodedBytes;
|
|
162
|
-
session.files -= 1;
|
|
163
|
-
} catch {
|
|
164
|
-
// Keep the reservation when a partial file cannot be removed.
|
|
165
|
-
}
|
|
166
|
-
return omitBinaryResource(resource, "could not be saved");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return replaceBlob(resource, [
|
|
170
|
-
`[Resource: ${resource.uri ?? "(no URI)"}]`,
|
|
171
|
-
`Binary content saved to ${filePath}`,
|
|
172
|
-
`MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
|
|
173
|
-
].join("\n"));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Transform MCP content types to Pi content blocks.
|
|
178
|
-
*/
|
|
179
|
-
export function transformMcpResourceContents(contents: McpResourceContent[], scope?: object): ContentBlock[] {
|
|
180
|
-
return contents.map(resource => {
|
|
181
|
-
if (typeof resource.text === "string") return { type: "text" as const, text: resource.text };
|
|
182
|
-
if (typeof resource.blob === "string") return { type: "text" as const, text: materializeBinaryResource(resource as BinaryResource, scope) };
|
|
183
|
-
return { type: "text" as const, text: JSON.stringify(resource) };
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function transformMcpContent(content: McpContent[], scope?: object): ContentBlock[] {
|
|
188
|
-
return content.map(c => {
|
|
189
|
-
if (c.type === "text") {
|
|
190
|
-
return { type: "text" as const, text: c.text ?? "" };
|
|
191
|
-
}
|
|
192
|
-
if (c.type === "image") {
|
|
193
|
-
return {
|
|
194
|
-
type: "image" as const,
|
|
195
|
-
data: c.data ?? "",
|
|
196
|
-
mimeType: c.mimeType ?? "image/png",
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (c.type === "resource") {
|
|
200
|
-
const resourceUri = c.resource?.uri ?? "(no URI)";
|
|
201
|
-
if (c.resource && "blob" in c.resource && typeof c.resource.blob === "string") {
|
|
202
|
-
const binaryResource = c.resource as typeof c.resource & { mimeType?: string; blob: string };
|
|
203
|
-
return {
|
|
204
|
-
type: "text" as const,
|
|
205
|
-
text: materializeBinaryResource(binaryResource, scope),
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : "(no content)");
|
|
209
|
-
return {
|
|
210
|
-
type: "text" as const,
|
|
211
|
-
text: `[Resource: ${resourceUri}]\n${resourceContent}`,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
if (c.type === "resource_link") {
|
|
215
|
-
const linkName = c.name ?? c.uri ?? "unknown";
|
|
216
|
-
const linkUri = c.uri ?? "(no URI)";
|
|
217
|
-
return {
|
|
218
|
-
type: "text" as const,
|
|
219
|
-
text: `[Resource Link: ${linkName}]\nURI: ${linkUri}`,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
if (c.type === "audio") {
|
|
223
|
-
return {
|
|
224
|
-
type: "text" as const,
|
|
225
|
-
text: `[Audio content: ${c.mimeType ?? "audio/*"}]`,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
return { type: "text" as const, text: JSON.stringify(c) };
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Resolve a tool result's content blocks, appending structuredContent when it
|
|
234
|
-
* coexists with ordinary content and falling back to it when content is empty.
|
|
235
|
-
*/
|
|
236
|
-
export function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[] {
|
|
237
|
-
const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[], scope);
|
|
238
|
-
if (result.structuredContent !== undefined && result.structuredContent !== null) {
|
|
239
|
-
const structured = stringifyStructuredContent(result.structuredContent);
|
|
240
|
-
if (blocks.length > 0) {
|
|
241
|
-
return [...blocks, { type: "text" as const, text: `structuredContent:\n${structured}` }];
|
|
242
|
-
}
|
|
243
|
-
return [{ type: "text" as const, text: structured }];
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return blocks;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function stringifyStructuredContent(value: unknown): string {
|
|
250
|
-
try {
|
|
251
|
-
return JSON.stringify(value, null, 2) ?? String(value);
|
|
252
|
-
} catch {
|
|
253
|
-
return String(value);
|
|
254
|
-
}
|
|
255
|
-
}
|