@geohar/pi-mcp-combiner 0.14.3 → 0.16.0-dev.1

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.
Files changed (98) hide show
  1. package/README.md +71 -48
  2. package/dist/client/config-ladder.d.ts +1 -1
  3. package/dist/client/config-ladder.d.ts.map +1 -1
  4. package/dist/client/control.d.ts +1 -1
  5. package/dist/client/control.d.ts.map +1 -1
  6. package/dist/client/elicitation.d.ts +4 -2
  7. package/dist/client/elicitation.d.ts.map +1 -1
  8. package/dist/client/elicitation.js +8 -2
  9. package/dist/client/elicitation.js.map +1 -1
  10. package/dist/client/footer.d.ts +1 -1
  11. package/dist/client/footer.d.ts.map +1 -1
  12. package/dist/client/panel.d.ts +1 -1
  13. package/dist/client/panel.d.ts.map +1 -1
  14. package/dist/client/panel.js +1 -1
  15. package/dist/client/panel.js.map +1 -1
  16. package/dist/client/prompts.d.ts +1 -1
  17. package/dist/client/prompts.d.ts.map +1 -1
  18. package/dist/client/proxy-tool.d.ts +1 -1
  19. package/dist/client/proxy-tool.d.ts.map +1 -1
  20. package/dist/client/resource-naming.d.ts +8 -0
  21. package/dist/client/resource-naming.d.ts.map +1 -0
  22. package/dist/client/resource-naming.js +33 -0
  23. package/dist/client/resource-naming.js.map +1 -0
  24. package/dist/client/settings.d.ts +5 -4
  25. package/dist/client/settings.d.ts.map +1 -1
  26. package/dist/client/settings.js +3 -3
  27. package/dist/client/settings.js.map +1 -1
  28. package/dist/client/tool-matching.d.ts +32 -0
  29. package/dist/client/tool-matching.d.ts.map +1 -0
  30. package/dist/client/tool-matching.js +43 -0
  31. package/dist/client/tool-matching.js.map +1 -0
  32. package/dist/client/types.d.ts +71 -0
  33. package/dist/client/types.d.ts.map +1 -0
  34. package/dist/client/types.js +16 -0
  35. package/dist/client/types.js.map +1 -0
  36. package/dist/client/widget-support.d.ts +17 -0
  37. package/dist/client/widget-support.d.ts.map +1 -0
  38. package/dist/client/widget-support.js +89 -0
  39. package/dist/client/widget-support.js.map +1 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +109 -53
  42. package/dist/index.js.map +1 -1
  43. package/dist/{client/connection.d.ts → native/combiner-connection.d.ts} +46 -38
  44. package/dist/native/combiner-connection.d.ts.map +1 -0
  45. package/dist/native/combiner-connection.js +412 -0
  46. package/dist/native/combiner-connection.js.map +1 -0
  47. package/dist/native/index.d.ts +30 -0
  48. package/dist/native/index.d.ts.map +1 -0
  49. package/dist/native/index.js +61 -0
  50. package/dist/native/index.js.map +1 -0
  51. package/dist/native/resources.d.ts +18 -0
  52. package/dist/native/resources.d.ts.map +1 -0
  53. package/dist/native/resources.js +97 -0
  54. package/dist/native/resources.js.map +1 -0
  55. package/dist/native/tool-surface.d.ts +26 -0
  56. package/dist/native/tool-surface.d.ts.map +1 -0
  57. package/dist/native/tool-surface.js +113 -0
  58. package/dist/native/tool-surface.js.map +1 -0
  59. package/dist/pi.d.ts +19 -0
  60. package/dist/pi.d.ts.map +1 -1
  61. package/package.json +3 -5
  62. package/src/client/config-ladder.ts +1 -1
  63. package/src/client/control.ts +1 -1
  64. package/src/client/elicitation.ts +10 -2
  65. package/src/client/footer.ts +1 -1
  66. package/src/client/panel.ts +2 -2
  67. package/src/client/prompts.ts +1 -1
  68. package/src/client/proxy-tool.ts +1 -1
  69. package/src/client/resource-naming.ts +36 -0
  70. package/src/client/settings.ts +7 -6
  71. package/src/client/tool-matching.ts +69 -0
  72. package/src/client/types.ts +80 -0
  73. package/src/client/widget-support.ts +86 -0
  74. package/src/index.ts +109 -65
  75. package/src/native/combiner-connection.ts +483 -0
  76. package/src/native/index.ts +100 -0
  77. package/src/native/resources.ts +112 -0
  78. package/src/native/tool-surface.ts +147 -0
  79. package/src/pi.ts +16 -0
  80. package/dist/client/connection.d.ts.map +0 -1
  81. package/dist/client/connection.js +0 -332
  82. package/dist/client/connection.js.map +0 -1
  83. package/dist/client/direct-tools.d.ts +0 -41
  84. package/dist/client/direct-tools.d.ts.map +0 -1
  85. package/dist/client/direct-tools.js +0 -122
  86. package/dist/client/direct-tools.js.map +0 -1
  87. package/dist/client/resources.d.ts +0 -20
  88. package/dist/client/resources.d.ts.map +0 -1
  89. package/dist/client/resources.js +0 -119
  90. package/dist/client/resources.js.map +0 -1
  91. package/dist/client/script.d.ts +0 -10
  92. package/dist/client/script.d.ts.map +0 -1
  93. package/dist/client/script.js +0 -130
  94. package/dist/client/script.js.map +0 -1
  95. package/src/client/connection.ts +0 -373
  96. package/src/client/direct-tools.ts +0 -146
  97. package/src/client/resources.ts +0 -137
  98. package/src/client/script.ts +0 -150
@@ -0,0 +1,97 @@
1
+ // NATIVE RESOURCE TOOLS : read_* tools for
2
+ // INTERACTIVE (mcp-app / ui://) resources — the parity half of legacy-client/
3
+ // resources.ts that pi's built-in read_mcp_resource deliberately does NOT cover
4
+ // (it skips ui:// listings and renders nothing).
5
+ //
6
+ // Parity contract with legacy read_<resource> tools:
7
+ // - SAME naming: read_<resourceNameToToolName(resource.name ?? uri)>
8
+ // - SAME attribution + project filtering: resourceServer() + `<server>_`/
9
+ // `<server>-` prefix matching against the per-project ServerFilter
10
+ // - SAME read behavior: connection.readResource(uri) → renderResourceResult
11
+ // (markup as text, json prettified, blobs as byte notes, 16KB guard)
12
+ // - SAME surfacing: append the combiner UI-host URL (this session's token),
13
+ // auto-open in the browser when a dialog-capable UI exists (uiAutoOpen)
14
+ // - idempotent registration; re-syncs on resources/list_changed (the
15
+ // connection's onToolsChanged hook covers both list-change notifications)
16
+ //
17
+ // TEXT/image resources are NOT re-registered here — pi's built-in
18
+ // read_mcp_resource + tool_search discovery covers them in native mode; that is
19
+ // the deliberate (documented) divergence from legacy, which registered one
20
+ // zero-arg tool per resource of every kind.
21
+ import { isInteractiveResource, openInBrowser, renderResourceResult } from "../client/widget-support.js";
22
+ import { resourceNameToToolName, resourceServer } from "../client/resource-naming.js";
23
+ /** Project server filter for resources — mirrors legacy filterResources
24
+ * attribution semantics (uri host first, `<server>_` name-prefix fallback). */
25
+ export function filterInteractiveResources(resources, filter) {
26
+ const interactive = resources.filter(isInteractiveResource);
27
+ if (!filter)
28
+ return interactive;
29
+ const matches = (r, entry) => resourceServer(r) === entry ||
30
+ (r.name ?? "").startsWith(`${entry}_`) ||
31
+ (r.name ?? "").startsWith(`${entry}-`);
32
+ if (filter.allow?.length)
33
+ return interactive.filter((r) => filter.allow.some((e) => matches(r, e)));
34
+ if (filter.deny?.length)
35
+ return interactive.filter((r) => !filter.deny.some((e) => matches(r, e)));
36
+ return interactive;
37
+ }
38
+ function toReadTool(toolName, resource, opts) {
39
+ const label = resource.name ?? resource.uri;
40
+ const url = opts.connection.uiUrlFor(resource.uri);
41
+ return {
42
+ name: toolName,
43
+ label: `MCP resource: ${label}`,
44
+ description: [
45
+ resource.description ?? `Read MCP resource ${resource.uri}`,
46
+ `uri: ${resource.uri}`,
47
+ resource.mimeType ? `type: ${resource.mimeType}` : undefined,
48
+ "interactive app resource — markup as text; the interactive widget opens from the URL appended to results",
49
+ ]
50
+ .filter(Boolean)
51
+ .join(" | "),
52
+ promptSnippet: `Read MCP resource ${label}.`,
53
+ parameters: { type: "object", properties: {} },
54
+ exposure: "direct",
55
+ namespace: { name: "mcp_combiner", description: "mcp-combiner aggregated tools" },
56
+ execute: async (_toolCallId, _params, _signal, _onUpdate, ctx) => {
57
+ try {
58
+ const result = await opts.connection.readResource(resource.uri);
59
+ let text = renderResourceResult(result);
60
+ if (url) {
61
+ text = `${text}\n\ninteractive: ${url}`;
62
+ if (opts.uiAutoOpen !== false && ctx?.hasUI)
63
+ openInBrowser(url);
64
+ }
65
+ return { content: [{ type: "text", text }], details: { mode: "resource", uri: resource.uri, url: url || undefined } };
66
+ }
67
+ catch (e) {
68
+ throw new Error(`${toolName}: resource read failed (${e instanceof Error ? e.message : String(e)})`);
69
+ }
70
+ },
71
+ };
72
+ }
73
+ /** Register read_* tools for the interactive resources (idempotent, like legacy).
74
+ * Returns the tool names registered this pass. */
75
+ export async function activateNativeResources(pi, opts, registered) {
76
+ let resources = [];
77
+ try {
78
+ resources = filterInteractiveResources(await opts.connection.listResources(true), opts.serverFilter);
79
+ }
80
+ catch (e) {
81
+ opts.log("warn", `interactive resource discovery failed (${e instanceof Error ? e.message : String(e)})`);
82
+ return [];
83
+ }
84
+ const added = [];
85
+ for (const resource of resources) {
86
+ const toolName = `read_${resourceNameToToolName(resource.name ?? resource.uri)}`;
87
+ if (registered.has(toolName))
88
+ continue;
89
+ registered.add(toolName);
90
+ pi.registerTool(toReadTool(toolName, resource, opts));
91
+ added.push(toolName);
92
+ }
93
+ if (added.length)
94
+ opts.log("info", `native resources: ${added.length} interactive resource tool(s) registered`);
95
+ return added;
96
+ }
97
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/native/resources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ExtensionAPI } from "../pi.js";
2
+ import type { NativeCombinerConnection } from "./combiner-connection.js";
3
+ import type { ServerFilter } from "../client/config-ladder.js";
4
+ export type NativeToolSurfaceOptions = {
5
+ /** The resolved combiner connection (owns elicitation, filters, caches). */
6
+ connection: NativeCombinerConnection;
7
+ /** The per-project server filter (same object index.ts passes to applyFilter);
8
+ * applied defensively here too so a filter race never widens surface exposure. */
9
+ serverFilter?: ServerFilter;
10
+ /** The combiner block's directTools spec, as resolved from the ladder: a glob
11
+ * allowlist → "direct" exposure for matches; "search" → everything codemode. */
12
+ directSpec?: string[] | "search";
13
+ /** Warn when the direct-exposure set grows large (every declared schema rides in
14
+ * every request). Settings kill-switch: warnLargeDirectExposure (default true). */
15
+ warnLargeDirectExposure?: boolean;
16
+ /** Namespace shown in codemode's grouped listing (pi normalizes -/­_ alike). */
17
+ namespaceName?: string;
18
+ /** Names that must never be shadowed by a combiner tool (pi builtins + ours). */
19
+ reserved?: readonly string[];
20
+ log: (level: "info" | "warn" | "error", message: string) => void;
21
+ };
22
+ /** Register the combiner's tools natively. Returns the names it registered (for
23
+ * status/panel surfaces). Idempotent by name — pi.registerTool replaces by name with
24
+ * a warning, so re-registration on list_changed must pass the same names. */
25
+ export declare function activateNativeTools(pi: ExtensionAPI, opts: NativeToolSurfaceOptions): Promise<string[]>;
26
+ //# sourceMappingURL=tool-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-surface.d.ts","sourceRoot":"","sources":["../../src/native/tool-surface.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAkC,MAAM,UAAU,CAAA;AAC5E,OAAO,KAAK,EAAE,wBAAwB,EAAe,MAAM,0BAA0B,CAAA;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAM9D,MAAM,MAAM,wBAAwB,GAAG;IACnC,4EAA4E;IAC5E,UAAU,EAAE,wBAAwB,CAAA;IACpC;uFACmF;IACnF,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;qFACiF;IACjF,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAA;IAChC;wFACoF;IACpF,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACnE,CAAA;AAwDD;;8EAE8E;AAC9E,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmC7G"}
@@ -0,0 +1,113 @@
1
+ // NATIVE TOOL SURFACE : declares the combiner's tools
2
+ // to Pi FIRST-CLASS, via pi.registerTool with exposure/namespace/annotations — the
3
+ // replacement for client/proxy-tool.ts + client/script.ts + direct-tools.ts promotion.
4
+ //
5
+ // What this buys (the whole point of the native path):
6
+ //
7
+ // - every tool is declared under its own name (`github_search_code`), one hop,
8
+ // no mcp() wrapper and no mcpScript
9
+ // - exposure IS pi's native mechanism: `direct` for the daily drivers (declared to
10
+ // the model verbatim), `codemode` for the rest (callable from codemode scripts,
11
+ // listed only as a namespace — 4%-token cost since pi 1.0), so the combiner's
12
+ // 671 tools stay out of the declaration budget by default
13
+ // - annotations (readOnly/destructive hints) ride along, so pi-permission-system
14
+ // and other tool_call gates classify combiner calls without any adapter glue
15
+ // - tool_search / codemode searchTools() find undeclared ones by BM25, replacing
16
+ // directTools:"search" promotion with pi's own discovery
17
+ //
18
+ // Exposure mapping reuses the existing config vocabulary: the `combiner.directTools`
19
+ // allowlist in the mcp.json ladder means "these are direct" here; everything else is
20
+ // `codemode`. `directTools: "search"` degenerates to plain codemode (pi's tool_search
21
+ // replaces it). Per-project allow/deny keeps flowing combiner-side via applyFilter.
22
+ //
23
+ // Wire shape: register once at session_start (tools list is stable modulo
24
+ // list_changed, which re-registers on the same names). Tools the combiner offers
25
+ // during a session surface via the connection's onToolsChanged hook → activate again.
26
+ import { applyServerFilter, matchesGlob, RESERVED_TOOL_NAMES } from "../client/tool-matching.js";
27
+ function isRecord(v) {
28
+ return typeof v === "object" && v !== null && !Array.isArray(v);
29
+ }
30
+ /** Map a CallToolResult content block to pi's text-block contract. pi tool results
31
+ * are arrays of text blocks today; image/other blocks are summarized to text and the
32
+ * full result rides `details` for renderers (same shape the SDK client half uses). */
33
+ function textBlockOf(b) {
34
+ const block = isRecord(b) ? b : undefined;
35
+ if (block && block.type === "text") {
36
+ return { type: "text", text: String(block.text) };
37
+ }
38
+ const kind = block?.type ?? (block ? "content" : b);
39
+ return { type: "text", text: `[${String(kind)} block]` };
40
+ }
41
+ function toToolResult(call) {
42
+ const blocks = Array.isArray(call.content) ? call.content : [];
43
+ return { content: blocks.map((b) => textBlockOf(b)) };
44
+ }
45
+ /** Register the combiner's tools natively. */
46
+ const DIRECT_EXPOSURE_WARN_THRESHOLD = 50;
47
+ /** Turn one combiner ToolSummary into pi's ToolDefinition, plus the native-only fields
48
+ * pi 1.0 accepts on registerTool (exposure/namespace/annotations). */
49
+ function toNativeTool(sum, conn, exposure) {
50
+ const name = sum.name;
51
+ return {
52
+ name,
53
+ label: name,
54
+ description: sum.description ?? name,
55
+ parameters: sum.inputSchema ?? { type: "object", properties: {} },
56
+ // NATIVE-ONLY fields (pi ≥ 1.0 host contract): exposure decides how the tool is
57
+ // declared; namespace groups it in codemode's listing; annotations feed
58
+ // pi-permission-system's classification. Kept optional on ToolDefinition so the
59
+ // extension stays load-compatible with pi 0.99.x where these fields are ignored.
60
+ exposure,
61
+ namespace: { name: "mcp_combiner", description: "mcp-combiner aggregated tools" },
62
+ annotations: sum.annotations,
63
+ execute: async (_toolCallId, params, signal) => {
64
+ // The abort signal rides into the transport; the combiner can cancel
65
+ // upstream work, and widget holds respect it (notifications/cancelled).
66
+ void signal;
67
+ try {
68
+ const result = await conn.callTool(name, params);
69
+ return { ...toToolResult(result), details: result };
70
+ }
71
+ catch (e) {
72
+ // pi's error convention: THROWING marks the call an error (no isError
73
+ // property on successful results).
74
+ throw e instanceof Error ? e : new Error(String(e));
75
+ }
76
+ },
77
+ };
78
+ }
79
+ /** Register the combiner's tools natively. Returns the names it registered (for
80
+ * status/panel surfaces). Idempotent by name — pi.registerTool replaces by name with
81
+ * a warning, so re-registration on list_changed must pass the same names. */
82
+ export async function activateNativeTools(pi, opts) {
83
+ const registered = [];
84
+ const cachedNames = registered;
85
+ const tools = applyServerFilter(await opts.connection.listTools(true), opts.serverFilter);
86
+ const allowlist = Array.isArray(opts.directSpec) ? opts.directSpec : [];
87
+ const reserved = new Set([...(opts.reserved ?? []), ...RESERVED_TOOL_NAMES]);
88
+ const directNames = [];
89
+ for (const sum of tools) {
90
+ if (reserved.has(sum.name)) {
91
+ opts.log("warn", `skipping combiner tool "${sum.name}" — collides with a reserved name`);
92
+ continue;
93
+ }
94
+ // Allowlist glob match → declared verbatim ("direct"); everything else rides
95
+ // codemode: callable from scripts, discoverable via tool_search/searchTools(),
96
+ // near-zero declaration cost. "search" degrades to the same codemode mapping
97
+ // (pi's tool_search replaces the adapter's search-promote semantics).
98
+ const exposure = allowlist.some((p) => matchesGlob(sum.name, p)) ? "direct" : "codemode";
99
+ if (exposure === "direct")
100
+ directNames.push(sum.name);
101
+ pi.registerTool(toNativeTool(sum, opts.connection, exposure));
102
+ cachedNames.push(sum.name);
103
+ }
104
+ if (directNames.length > DIRECT_EXPOSURE_WARN_THRESHOLD &&
105
+ opts.warnLargeDirectExposure !== false) {
106
+ opts.log("warn", `native mode: ${directNames.length} tools declared "direct" — every schema rides in every ` +
107
+ `request. Consider trimming the allowlist (directTools) or disabling this warning ` +
108
+ `with "warnLargeDirectExposure": false in extensions/mcp-combiner.json.`);
109
+ }
110
+ opts.log("info", `native tool surface: ${cachedNames.length} tools registered`);
111
+ return cachedNames;
112
+ }
113
+ //# sourceMappingURL=tool-surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-surface.js","sourceRoot":"","sources":["../../src/native/tool-surface.ts"],"names":[],"mappings":"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"}
package/dist/pi.d.ts CHANGED
@@ -128,6 +128,25 @@ export type ToolDefinition = {
128
128
  }, options: ToolRenderResultOptions, theme: RenderTheme, context: {
129
129
  [key: string]: unknown;
130
130
  }) => TuiComponent;
131
+ /** NATIVE-exposure fields (pi ≥ 1.0 host contract; optional so 0.99.x hosts ignore
132
+ * them instead of erroring — unknown fields are dropped by the 0.99 loader):
133
+ * exposure — "direct" | "model-only" | "codemode" | "deferred" | "hidden":
134
+ * how the tool is declared to the model (codemode: callable from
135
+ * codemode scripts, listed as a namespace in the codemode tool's
136
+ * description — near-zero token cost as of pi 1.0)
137
+ * namespace — groups the tool in codemode's listing, like an MCP server
138
+ * annotations — MCP tool hints; reach pi.getAllTools() for permission gates */
139
+ exposure?: "direct" | "model-only" | "codemode" | "deferred" | "hidden";
140
+ namespace?: {
141
+ name: string;
142
+ description: string;
143
+ };
144
+ annotations?: {
145
+ readOnlyHint?: boolean;
146
+ destructiveHint?: boolean;
147
+ idempotentHint?: boolean;
148
+ openWorldHint?: boolean;
149
+ };
131
150
  };
132
151
  export type CommandSpec = {
133
152
  description: string;
package/dist/pi.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geohar/pi-mcp-combiner",
3
- "version": "0.14.3",
3
+ "version": "0.16.0-dev.1",
4
4
  "description": "Pi extension: run the mcp-combiner MCP aggregator via sharedserver and inject its tool-discovery directive. Pairs with pi-mcp-adapter, which makes the combiner's tools reachable from Pi.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,6 +54,7 @@
54
54
  "access": "public"
55
55
  },
56
56
  "devDependencies": {
57
+ "@earendil-works/pi-mcp": "^1.0.0",
57
58
  "@earendil-works/pi-tui": "^0.85.1",
58
59
  "@types/node": "^22.0.0",
59
60
  "typescript": "^5.6.0",
@@ -62,11 +63,8 @@
62
63
  "engines": {
63
64
  "node": ">=18.0.0"
64
65
  },
65
- "dependencies": {
66
- "@modelcontextprotocol/client": "^2.0.0",
67
- "@modelcontextprotocol/core": "^2.0.0"
68
- },
69
66
  "peerDependencies": {
67
+ "@earendil-works/pi-mcp": "^1.0.2",
70
68
  "@earendil-works/pi-tui": "^0.85.1"
71
69
  }
72
70
  }
@@ -19,7 +19,7 @@ import { existsSync, readFileSync } from "node:fs"
19
19
  import { homedir } from "node:os"
20
20
  import { join } from "node:path"
21
21
  import { agentDir } from "./settings.js"
22
- import type { DirectToolsSpec } from "./direct-tools.js"
22
+ import type { DirectToolsSpec } from "./tool-matching.js"
23
23
 
24
24
  export type ServerFilter = { allow?: string[]; deny?: string[] }
25
25
 
@@ -4,7 +4,7 @@
4
4
  // filter routes); the write side drives the combiner__* meta-tools through our own
5
5
  // MCP client — one auth path, no second protocol.
6
6
 
7
- import type { CombinerConnection } from "./connection.js"
7
+ import type { CombinerConnection } from "./types.js"
8
8
 
9
9
  export type HealthServer = { name?: string; status?: string; state?: string; transport?: string }
10
10
 
@@ -84,8 +84,15 @@ async function askProperty(
84
84
  }
85
85
 
86
86
  /** Handle an elicitation request against Pi's UI. Never throws — a UI that can't
87
- * answer declines, and the combiner applies its elicitUnavailable policy. */
88
- export async function handleElicitation(req: ElicitRequest, elicitUi: ElicitUi): Promise<ElicitResponse> {
87
+ * answer declines, and the combiner applies its elicitUnavailable policy.
88
+ * `abort` (wired from the connection's elicitation handler signal) cancels between
89
+ * dialogs: a cancelled pending request must not keep prompting the user. */
90
+ export async function handleElicitation(
91
+ req: ElicitRequest,
92
+ elicitUi: ElicitUi,
93
+ abort?: AbortSignal,
94
+ ): Promise<ElicitResponse> {
95
+ if (abort?.aborted) return { action: "cancel" }
89
96
  if (!elicitUi.hasUI || !elicitUi.ui?.select || !elicitUi.ui.input) return REFUSE
90
97
 
91
98
  const schema = isRecord(req.requestedSchema) ? req.requestedSchema : undefined
@@ -106,6 +113,7 @@ export async function handleElicitation(req: ElicitRequest, elicitUi: ElicitUi):
106
113
  const entries = Object.entries(props).filter(([, v]) => isRecord(v))
107
114
  const content: Record<string, ElicitValue> = {}
108
115
  for (const [name, raw] of entries) {
116
+ if (abort?.aborted) return { action: "cancel" }
109
117
  const label = entries.length === 1 ? header : `${header}: ${name}`
110
118
  const value = await askProperty(elicitUi.ui, label, raw as Record<string, unknown>)
111
119
  if (value === undefined) return REFUSE
@@ -4,7 +4,7 @@
4
4
  // `mcp` (default, overridable via the mcpFooterKey setting) — the slot
5
5
  // pi-mcp-adapter wrote and oh-my-posh-style footers aggregate into PI_STATUS.
6
6
 
7
- import type { CombinerConnection } from "./connection.js"
7
+ import type { CombinerConnection } from "./types.js"
8
8
  import type { ExtensionUIContext } from "../pi.js"
9
9
  import type { FooterStatus } from "./settings.js"
10
10
 
@@ -9,9 +9,9 @@
9
9
 
10
10
  import { spawn } from "node:child_process"
11
11
  import { Container, Text, matchesKey, truncateToWidth, visibleWidth, type Component } from "@earendil-works/pi-tui"
12
- import type { CombinerConnection, PromptSummary, ResourceSummary, ToolSummary } from "./connection.js"
12
+ import type { CombinerConnection, PromptSummary, ResourceSummary, ToolSummary } from "./types.js"
13
13
  import { metaToolCall } from "./control.js"
14
- import { resourceServer } from "./resources.js"
14
+ import { resourceServer } from "./resource-naming.js"
15
15
 
16
16
  type RenderTheme = { fg: (color: string, text: string) => string }
17
17
 
@@ -14,7 +14,7 @@
14
14
  // passthrough) are ported from pi-mcp-adapter's prompts.ts (MIT, © 2026 Nico Bailon).
15
15
 
16
16
  import type { ExtensionAPI } from "../pi.js"
17
- import type { CombinerConnection, PromptSummary } from "./connection.js"
17
+ import type { CombinerConnection, PromptSummary } from "./types.js"
18
18
 
19
19
  // ── naming ──
20
20
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { spawn } from "node:child_process"
14
14
  import type { ToolCallContext, ToolDefinition } from "../pi.js"
15
- import type { CombinerConnection, ToolSummary } from "./connection.js"
15
+ import type { CombinerConnection, ToolSummary } from "./types.js"
16
16
  import { compileSafeRegex, rankTools, regexMatches, type SearchableTool } from "./ranking.js"
17
17
  import { renderDescribe, renderSearchHit, renderToolResult, textResult, toolUiResourceUri } from "./render.js"
18
18
  import { proxyRenderers } from "./renderers.js"
@@ -0,0 +1,36 @@
1
+ // Resource naming helpers — extracted from legacy-client/resources.ts during the
2
+ // Extracted during the legacy-client factoring so surfaces that survive the native migration (panel, widget
3
+ // resource attribution) don't import the deprecated legacy-client/ directory.
4
+ //
5
+ // These name the SAME way legacy read_<resource> tools always did (ported verbatim
6
+ // from pi-mcp-adapter, MIT © Nico Bailon); native widget resource support reuses the
7
+ // attribution logic, so panel counts and native read_* tools agree on server keys.
8
+
9
+ import type { ResourceSummary } from "./types.js"
10
+
11
+ /** Ported verbatim from pi-mcp-adapter's resource-tools.ts (MIT, © 2026 Nico Bailon). */
12
+ export function resourceNameToToolName(name: string): string {
13
+ let result = name
14
+ .replace(/[^a-zA-Z0-9]/g, "_")
15
+ .replace(/_+/g, "_")
16
+ .replace(/^_+/, "") // Remove leading underscores
17
+ .replace(/_+$/, "") // Remove trailing underscores
18
+ .toLowerCase()
19
+
20
+ // Ensure we have a valid name
21
+ if (!result || /^\d/.test(result)) {
22
+ result = "resource" + (result ? "_" + result : "")
23
+ }
24
+ return result
25
+ }
26
+
27
+ /** Attribute a resource to an upstream server for filtering: ui:// URIs carry the
28
+ * server as the host component (ui://todoist/…); otherwise fall back to the name's
29
+ * leading word before a dash/underscore. */
30
+ export function resourceServer(r: ResourceSummary): string | undefined {
31
+ const m = r.uri.match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]+)/i)
32
+ if (m?.[1]) return m[1].split(".")[0]?.toLowerCase()
33
+ const name = r.name ?? r.uri
34
+ const head = name.split(/[-_]/, 1)[0]
35
+ return head ? head.toLowerCase() : undefined
36
+ }
@@ -21,8 +21,8 @@ export type CombinerSettings = {
21
21
  toolName: string
22
22
  /** "lazy" (default): connect on first mcp() call. "eager": connect at session_start. */
23
23
  lazy: "lazy" | "eager"
24
- /** Client-half gate. `true`: we are the MCP tool. `false`: legacy mode — the
25
- * process + instructions halves only, pi-mcp-adapter (if installed) owns MCP.
24
+ /** Client-half gate. `true`: we are the MCP tool. `false`: the process +
25
+ * instructions halves only, pi-mcp-adapter (if installed) owns MCP.
26
26
  * `"auto"` (default): OFF when pi-mcp-adapter is detected installed, ON otherwise —
27
27
  * so existing adapter users upgrade with zero behaviour change, while fresh
28
28
  * installs get the one-package experience. Env PI_MCP_COMBINER_ADAPTER wins. */
@@ -39,11 +39,12 @@ export type CombinerSettings = {
39
39
  * slot pi-mcp-adapter wrote and footers (oh-my-posh's PI_STATUS aggregate)
40
40
  * already surface; rename only if something else claims it. */
41
41
  mcpFooterKey: string
42
- /** Register the trusted batching tool (<toolName>Script). Default true. */
43
- scriptMode: boolean
44
42
  /** Auto-open interactive widget URLs (Stage 2 holds + resource reads).
45
43
  * Default true. */
46
44
  uiAutoOpen: boolean
45
+ /** Warn when the direct-exposure set grows large (default threshold: 50 tools —
46
+ * every schema rides in every request). Default true. */
47
+ warnLargeDirectExposure: boolean
47
48
  /** Explicit combiner base URL (e.g. "http://127.0.0.1:9741/mcp"). Env wins. */
48
49
  url?: string
49
50
  /** Notify (vs stderr-only) for connection lifecycle messages. */
@@ -58,8 +59,8 @@ export const DEFAULT_SETTINGS: CombinerSettings = {
58
59
  prompts: true,
59
60
  mcpFooterStatus: "full",
60
61
  mcpFooterKey: "mcp",
61
- scriptMode: true,
62
62
  uiAutoOpen: true,
63
+ warnLargeDirectExposure: true,
63
64
  notify: true,
64
65
  }
65
66
 
@@ -102,7 +103,7 @@ export function loadSettings(path = settingsPath()): CombinerSettings {
102
103
  }
103
104
  if (typeof d.mcpFooterKey === "string" && d.mcpFooterKey.trim()) out.mcpFooterKey = d.mcpFooterKey.trim()
104
105
  if (typeof d.uiAutoOpen === "boolean") out.uiAutoOpen = d.uiAutoOpen
105
- if (typeof d.scriptMode === "boolean") out.scriptMode = d.scriptMode
106
+ if (typeof d.warnLargeDirectExposure === "boolean") out.warnLargeDirectExposure = d.warnLargeDirectExposure
106
107
  if (typeof d.url === "string" && d.url.trim()) out.url = d.url.trim()
107
108
  if (typeof d.notify === "boolean") out.notify = d.notify
108
109
  return out
@@ -0,0 +1,69 @@
1
+ // Shared tool-name matching helpers — extracted from legacy-client/direct-tools.ts
2
+ // during the legacy-client factoring so the NATIVE tool surface (src/native/) and the
3
+ // client half (src/client/: config-ladder, panel) can use them without importing the
4
+ // deprecated legacy-client/ directory. When legacy-client/ is deleted, this file
5
+ // survives untouched.
6
+ //
7
+ // These are the stable contract:
8
+ // - tool names arrive with the combiner's baked `<server>_` prefix
9
+ // - server attribution is PREFIX-based (underscore-safe), not string equality
10
+
11
+ /** A combined-tool allowlist spec from the ladder's `combiner.directTools` key:
12
+ * a glob allowlist, or "search" (legacy adapter semantics — deprecated in the
13
+ * native mode, where tool_search replaces it). */
14
+ export type DirectToolsSpec = string[] | "search"
15
+
16
+ /** Names that must never be shadowed by a promoted/registered tool (pi builtins).
17
+ * Callers add their own tool names on top. */
18
+ export const RESERVED_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls", "mcp", "glob"] as const
19
+
20
+ /** Apply a server allow/deny filter to a combined tool list. Server names may
21
+ * contain underscores (gws_georgeharker), so matching is by PREFIX — a tool
22
+ * belongs to entry E iff its name starts `E_` (subsumes first-segment equality). */
23
+ export function toolServerMatches(toolName: string, serverEntry: string): boolean {
24
+ return toolName.startsWith(`${serverEntry}_`)
25
+ }
26
+
27
+ /** Apply a server allow/deny filter to a combined tool list (`<server>_` prefix,
28
+ * underscore-safe via toolServerMatches). */
29
+ export function applyServerFilter(
30
+ tools: ToolSummary[],
31
+ filter: { allow?: string[]; deny?: string[] } | undefined,
32
+ ): ToolSummary[] {
33
+ if (!filter) return tools
34
+ if (filter.allow?.length) {
35
+ return tools.filter((t) => filter.allow!.some((e) => toolServerMatches(t.name, e)))
36
+ }
37
+ if (filter.deny?.length) {
38
+ return tools.filter((t) => !filter.deny!.some((e) => toolServerMatches(t.name, e)))
39
+ }
40
+ return tools
41
+ }
42
+
43
+ /** fnmatch-lite: `*` → any run, `?` → one char, everything else literal. */
44
+ export function globToRegExp(pattern: string): RegExp {
45
+ const escaped = pattern
46
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
47
+ .replace(/\*/g, ".*")
48
+ .replace(/\?/g, ".")
49
+ return new RegExp(`^${escaped}$`, "i")
50
+ }
51
+
52
+ export function matchesGlob(name: string, pattern: string): boolean {
53
+ return globToRegExp(pattern).test(name)
54
+ }
55
+
56
+ // Local shape of a combiner tool listing (kept structural — the two connection
57
+ // implementations share it; defined here so both src/client and src/native can
58
+ // import it without a connection.ts dependency).
59
+ export type ToolSummary = {
60
+ name: string
61
+ description?: string
62
+ inputSchema?: unknown
63
+ annotations?: {
64
+ readOnlyHint?: boolean
65
+ destructiveHint?: boolean
66
+ idempotentHint?: boolean
67
+ openWorldHint?: boolean
68
+ }
69
+ }