@geohar/pi-mcp-combiner 0.14.2 → 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 +4 -6
  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,112 @@
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
+
22
+ import type { ExtensionAPI, ToolDefinition } from "../pi.js"
23
+ import type { NativeCombinerConnection, ResourceSummary } from "./combiner-connection.js"
24
+ import { isInteractiveResource, openInBrowser, renderResourceResult } from "../client/widget-support.js"
25
+ import { resourceNameToToolName, resourceServer } from "../client/resource-naming.js"
26
+ import type { ServerFilter } from "../client/config-ladder.js"
27
+
28
+ export type NativeResourceOptions = {
29
+ connection: NativeCombinerConnection
30
+ /** Per-project server filter, same semantics as legacy filterResources. */
31
+ serverFilter?: ServerFilter
32
+ /** Auto-open the widget URL on read (index.ts's uiAutoOpen setting). */
33
+ uiAutoOpen?: boolean
34
+ log: (level: "info" | "warn" | "error", message: string) => void
35
+ }
36
+
37
+ /** Project server filter for resources — mirrors legacy filterResources
38
+ * attribution semantics (uri host first, `<server>_` name-prefix fallback). */
39
+ export function filterInteractiveResources(
40
+ resources: ResourceSummary[],
41
+ filter: ServerFilter | undefined,
42
+ ): ResourceSummary[] {
43
+ const interactive = resources.filter(isInteractiveResource)
44
+ if (!filter) return interactive
45
+ const matches = (r: ResourceSummary, entry: string): boolean =>
46
+ resourceServer(r) === entry ||
47
+ (r.name ?? "").startsWith(`${entry}_`) ||
48
+ (r.name ?? "").startsWith(`${entry}-`)
49
+ if (filter.allow?.length) return interactive.filter((r) => filter.allow!.some((e) => matches(r, e)))
50
+ if (filter.deny?.length) return interactive.filter((r) => !filter.deny!.some((e) => matches(r, e)))
51
+ return interactive
52
+ }
53
+
54
+ function toReadTool(toolName: string, resource: ResourceSummary, opts: NativeResourceOptions): ToolDefinition {
55
+ const label = resource.name ?? resource.uri
56
+ const url = opts.connection.uiUrlFor(resource.uri)
57
+ return {
58
+ name: toolName,
59
+ label: `MCP resource: ${label}`,
60
+ description: [
61
+ resource.description ?? `Read MCP resource ${resource.uri}`,
62
+ `uri: ${resource.uri}`,
63
+ resource.mimeType ? `type: ${resource.mimeType}` : undefined,
64
+ "interactive app resource — markup as text; the interactive widget opens from the URL appended to results",
65
+ ]
66
+ .filter(Boolean)
67
+ .join(" | "),
68
+ promptSnippet: `Read MCP resource ${label}.`,
69
+ parameters: { type: "object", properties: {} },
70
+ exposure: "direct",
71
+ namespace: { name: "mcp_combiner", description: "mcp-combiner aggregated tools" },
72
+ execute: async (_toolCallId, _params, _signal, _onUpdate, ctx) => {
73
+ try {
74
+ const result = await opts.connection.readResource(resource.uri)
75
+ let text = renderResourceResult(result)
76
+ if (url) {
77
+ text = `${text}\n\ninteractive: ${url}`
78
+ if (opts.uiAutoOpen !== false && ctx?.hasUI) openInBrowser(url)
79
+ }
80
+ return { content: [{ type: "text", text }], details: { mode: "resource", uri: resource.uri, url: url || undefined } }
81
+ } catch (e) {
82
+ throw new Error(`${toolName}: resource read failed (${e instanceof Error ? e.message : String(e)})`)
83
+ }
84
+ },
85
+ }
86
+ }
87
+
88
+ /** Register read_* tools for the interactive resources (idempotent, like legacy).
89
+ * Returns the tool names registered this pass. */
90
+ export async function activateNativeResources(
91
+ pi: ExtensionAPI,
92
+ opts: NativeResourceOptions,
93
+ registered: Set<string>,
94
+ ): Promise<string[]> {
95
+ let resources: ResourceSummary[] = []
96
+ try {
97
+ resources = filterInteractiveResources(await opts.connection.listResources(true), opts.serverFilter)
98
+ } catch (e) {
99
+ opts.log("warn", `interactive resource discovery failed (${e instanceof Error ? e.message : String(e)})`)
100
+ return []
101
+ }
102
+ const added: string[] = []
103
+ for (const resource of resources) {
104
+ const toolName = `read_${resourceNameToToolName(resource.name ?? resource.uri)}`
105
+ if (registered.has(toolName)) continue
106
+ registered.add(toolName)
107
+ pi.registerTool(toReadTool(toolName, resource, opts))
108
+ added.push(toolName)
109
+ }
110
+ if (added.length) opts.log("info", `native resources: ${added.length} interactive resource tool(s) registered`)
111
+ return added
112
+ }
@@ -0,0 +1,147 @@
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
+
27
+ import type { ExtensionAPI, ToolDefinition, ToolParameters } from "../pi.js"
28
+ import type { NativeCombinerConnection, ToolSummary } from "./combiner-connection.js"
29
+ import { applyServerFilter, matchesGlob, RESERVED_TOOL_NAMES } from "../client/tool-matching.js"
30
+ import type { ServerFilter } from "../client/config-ladder.js"
31
+
32
+ function isRecord(v: unknown): v is Record<string, unknown> {
33
+ return typeof v === "object" && v !== null && !Array.isArray(v)
34
+ }
35
+
36
+ export type NativeToolSurfaceOptions = {
37
+ /** The resolved combiner connection (owns elicitation, filters, caches). */
38
+ connection: NativeCombinerConnection
39
+ /** The per-project server filter (same object index.ts passes to applyFilter);
40
+ * applied defensively here too so a filter race never widens surface exposure. */
41
+ serverFilter?: ServerFilter
42
+ /** The combiner block's directTools spec, as resolved from the ladder: a glob
43
+ * allowlist → "direct" exposure for matches; "search" → everything codemode. */
44
+ directSpec?: string[] | "search"
45
+ /** Warn when the direct-exposure set grows large (every declared schema rides in
46
+ * every request). Settings kill-switch: warnLargeDirectExposure (default true). */
47
+ warnLargeDirectExposure?: boolean
48
+ /** Namespace shown in codemode's grouped listing (pi normalizes -/­_ alike). */
49
+ namespaceName?: string
50
+ /** Names that must never be shadowed by a combiner tool (pi builtins + ours). */
51
+ reserved?: readonly string[]
52
+ log: (level: "info" | "warn" | "error", message: string) => void
53
+ }
54
+
55
+ /** Map a CallToolResult content block to pi's text-block contract. pi tool results
56
+ * are arrays of text blocks today; image/other blocks are summarized to text and the
57
+ * full result rides `details` for renderers (same shape the SDK client half uses). */
58
+ function textBlockOf(b: unknown): { type: "text"; text: string } {
59
+ const block = isRecord(b) ? b : undefined
60
+ if (block && block.type === "text") {
61
+ return { type: "text", text: String(block.text) }
62
+ }
63
+ const kind = block?.type ?? (block ? "content" : b)
64
+ return { type: "text", text: `[${String(kind)} block]` }
65
+ }
66
+
67
+ function toToolResult(call: { content?: unknown; structuredContent?: unknown }): {
68
+ content: { type: "text"; text: string }[]
69
+ } {
70
+ const blocks = Array.isArray(call.content) ? call.content : []
71
+ return { content: blocks.map((b) => textBlockOf(b)) }
72
+ }
73
+
74
+ /** Register the combiner's tools natively. */
75
+ const DIRECT_EXPOSURE_WARN_THRESHOLD = 50
76
+
77
+ /** Turn one combiner ToolSummary into pi's ToolDefinition, plus the native-only fields
78
+ * pi 1.0 accepts on registerTool (exposure/namespace/annotations). */
79
+ function toNativeTool(sum: ToolSummary, conn: NativeCombinerConnection, exposure: ToolDefinition["exposure"]): ToolDefinition {
80
+ const name = sum.name
81
+ return {
82
+ name,
83
+ label: name,
84
+ description: sum.description ?? name,
85
+ parameters: (sum.inputSchema as ToolParameters) ?? { type: "object", properties: {} },
86
+ // NATIVE-ONLY fields (pi ≥ 1.0 host contract): exposure decides how the tool is
87
+ // declared; namespace groups it in codemode's listing; annotations feed
88
+ // pi-permission-system's classification. Kept optional on ToolDefinition so the
89
+ // extension stays load-compatible with pi 0.99.x where these fields are ignored.
90
+ exposure,
91
+ namespace: { name: "mcp_combiner", description: "mcp-combiner aggregated tools" },
92
+ annotations: sum.annotations,
93
+ execute: async (_toolCallId, params, signal) => {
94
+ // The abort signal rides into the transport; the combiner can cancel
95
+ // upstream work, and widget holds respect it (notifications/cancelled).
96
+ void signal
97
+ try {
98
+ const result = await conn.callTool(name, params)
99
+ return { ...toToolResult(result), details: result }
100
+ } catch (e) {
101
+ // pi's error convention: THROWING marks the call an error (no isError
102
+ // property on successful results).
103
+ throw e instanceof Error ? e : new Error(String(e))
104
+ }
105
+ },
106
+ }
107
+ }
108
+
109
+ /** Register the combiner's tools natively. Returns the names it registered (for
110
+ * status/panel surfaces). Idempotent by name — pi.registerTool replaces by name with
111
+ * a warning, so re-registration on list_changed must pass the same names. */
112
+ export async function activateNativeTools(pi: ExtensionAPI, opts: NativeToolSurfaceOptions): Promise<string[]> {
113
+ const registered = [] as string[]
114
+ const cachedNames: string[] = registered
115
+ const tools = applyServerFilter(await opts.connection.listTools(true), opts.serverFilter)
116
+ const allowlist = Array.isArray(opts.directSpec) ? opts.directSpec : []
117
+ const reserved = new Set<string>([...(opts.reserved ?? []), ...RESERVED_TOOL_NAMES])
118
+
119
+ const directNames: string[] = []
120
+ for (const sum of tools) {
121
+ if (reserved.has(sum.name)) {
122
+ opts.log("warn", `skipping combiner tool "${sum.name}" — collides with a reserved name`)
123
+ continue
124
+ }
125
+ // Allowlist glob match → declared verbatim ("direct"); everything else rides
126
+ // codemode: callable from scripts, discoverable via tool_search/searchTools(),
127
+ // near-zero declaration cost. "search" degrades to the same codemode mapping
128
+ // (pi's tool_search replaces the adapter's search-promote semantics).
129
+ const exposure = allowlist.some((p) => matchesGlob(sum.name, p)) ? "direct" : "codemode"
130
+ if (exposure === "direct") directNames.push(sum.name)
131
+ pi.registerTool(toNativeTool(sum, opts.connection, exposure))
132
+ cachedNames.push(sum.name)
133
+ }
134
+ if (
135
+ directNames.length > DIRECT_EXPOSURE_WARN_THRESHOLD &&
136
+ opts.warnLargeDirectExposure !== false
137
+ ) {
138
+ opts.log(
139
+ "warn",
140
+ `native mode: ${directNames.length} tools declared "direct" — every schema rides in every ` +
141
+ `request. Consider trimming the allowlist (directTools) or disabling this warning ` +
142
+ `with "warnLargeDirectExposure": false in extensions/mcp-combiner.json.`,
143
+ )
144
+ }
145
+ opts.log("info", `native tool surface: ${cachedNames.length} tools registered`)
146
+ return cachedNames
147
+ }
package/src/pi.ts CHANGED
@@ -118,6 +118,22 @@ export type ToolDefinition = {
118
118
  theme: RenderTheme,
119
119
  context: { [key: string]: unknown },
120
120
  ) => TuiComponent
121
+ /** NATIVE-exposure fields (pi ≥ 1.0 host contract; optional so 0.99.x hosts ignore
122
+ * them instead of erroring — unknown fields are dropped by the 0.99 loader):
123
+ * exposure — "direct" | "model-only" | "codemode" | "deferred" | "hidden":
124
+ * how the tool is declared to the model (codemode: callable from
125
+ * codemode scripts, listed as a namespace in the codemode tool's
126
+ * description — near-zero token cost as of pi 1.0)
127
+ * namespace — groups the tool in codemode's listing, like an MCP server
128
+ * annotations — MCP tool hints; reach pi.getAllTools() for permission gates */
129
+ exposure?: "direct" | "model-only" | "codemode" | "deferred" | "hidden"
130
+ namespace?: { name: string; description: string }
131
+ annotations?: {
132
+ readOnlyHint?: boolean
133
+ destructiveHint?: boolean
134
+ idempotentHint?: boolean
135
+ openWorldHint?: boolean
136
+ }
121
137
  }
122
138
 
123
139
  export type CommandSpec = {
@@ -1 +0,0 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/client/connection.ts"],"names":[],"mappings":"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"}
@@ -1,332 +0,0 @@
1
- // The combiner MCP client: one streamable-HTTP connection, one token, reconnect.
2
- //
3
- // This is the piece that makes the extension the adapter: it owns the Client +
4
- // StreamableHTTPClientTransport pair against the combiner's /mcp endpoint, mints the
5
- // per-Pi-session grouping token into the URL path (URL form beats headers combiner-
6
- // side, so the token rides the path), sends the optional inbound bearer, bridges
7
- // elicitation to Pi's UI, and keeps a tools/list cache fresh via tools/list_changed.
8
- //
9
- // Lifecycle: lazy by default (connect on first mcp() call), `eager` connects at
10
- // session_start. A session reset (new/resume/fork/reload) rebinds the token and
11
- // drops the client; a transport close or stale-session 404 triggers a one-shot
12
- // reconnect-and-retry on the next call. The combiner's handover means the same token
13
- // resumes its parked isolated upstream sessions after a combiner restart.
14
- import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
15
- import { handleElicitation } from "./elicitation.js";
16
- const CLIENT_INFO = { name: "pi-mcp-combiner", version: "0.1.0" };
17
- const CONNECT_TIMEOUT_MS = 8_000;
18
- function isRecord(v) {
19
- return typeof v === "object" && v !== null && !Array.isArray(v);
20
- }
21
- /** Pull {message, requestedSchema} out of an elicitation/create request params,
22
- * tolerating both form-mode and legacy shapes. Returns undefined when unusable. */
23
- function toElicitParams(raw) {
24
- const p = isRecord(raw) ? raw : undefined;
25
- if (!p)
26
- return undefined;
27
- const out = {};
28
- if (typeof p.message === "string")
29
- out.message = p.message;
30
- if (isRecord(p.requestedSchema))
31
- out.requestedSchema = p.requestedSchema;
32
- return out;
33
- }
34
- /** Build the tokened endpoint URL: <origin>/mcp/<token> (combiner's URL form). */
35
- export function tokenedUrl(baseUrl, token) {
36
- try {
37
- const u = new URL(baseUrl);
38
- // Normalize to bare /mcp (strip any existing path segments below it), append token.
39
- const path = u.pathname.replace(/\/mcp(?:\/[^/]*)*\/?$/, "/mcp");
40
- u.pathname = `${path.replace(/\/$/, "")}/${encodeURIComponent(token)}`;
41
- return u.toString();
42
- }
43
- catch {
44
- return baseUrl;
45
- }
46
- }
47
- export class CombinerConnection {
48
- conn;
49
- log;
50
- /** Latest dialog-capable UI, refreshed each session_start. */
51
- elicitUi = { hasUI: false };
52
- prompts;
53
- promptsFetchedAt = 0;
54
- resources;
55
- resourcesFetchedAt = 0;
56
- token;
57
- client;
58
- connecting;
59
- tools;
60
- toolsFetchedAt = 0;
61
- hooks = {};
62
- state = "disconnected";
63
- lastError;
64
- constructor(conn, log) {
65
- this.conn = conn;
66
- this.log = log;
67
- }
68
- /** Bind lifecycle hooks (tools changed / state transitions). */
69
- setHooks(hooks) {
70
- this.hooks = hooks;
71
- }
72
- /** Effective grouping token for this session (explicit URL token wins). */
73
- get sessionToken() {
74
- return this.effectiveToken();
75
- }
76
- /** Bind the UI slice used by elicitation (called from session_start). */
77
- bindUi(ui) {
78
- this.elicitUi = ui;
79
- }
80
- /** Swap connection inputs (base URL / bearer env) when a session re-resolves a
81
- * different combiner URL (e.g. a worktree branch pinning its own combiner).
82
- * No-op when nothing changed; otherwise drops the client for a fresh connect. */
83
- rebind(conn) {
84
- if (conn.baseUrl === this.conn.baseUrl &&
85
- conn.bearerTokenEnv === this.conn.bearerTokenEnv &&
86
- conn.urlToken === this.conn.urlToken) {
87
- return false;
88
- }
89
- this.conn = conn;
90
- void this.reset("connection rebound (session config changed)");
91
- return true;
92
- }
93
- /** Set the grouping token for this session; resets any open connection. */
94
- setToken(token) {
95
- if (this.token === token)
96
- return;
97
- this.token = token;
98
- void this.reset("token changed");
99
- }
100
- /** Drop the connection + caches (session reset / shutdown). Never throws. */
101
- async reset(reason) {
102
- const client = this.client;
103
- this.client = undefined;
104
- this.connecting = undefined;
105
- this.tools = undefined;
106
- this.state = "disconnected";
107
- try {
108
- await client?.close();
109
- }
110
- catch {
111
- // best-effort
112
- }
113
- if (reason)
114
- this.log("info", `connection reset (${reason})`);
115
- }
116
- /** Effective token: explicit URL token wins over the minted session token. */
117
- effectiveToken() {
118
- return this.conn.urlToken ?? this.token;
119
- }
120
- /** Connect (idempotent, single-flight). Throws with a human message on failure. */
121
- async ensureConnected() {
122
- if (this.client)
123
- return this.client;
124
- if (this.connecting)
125
- return this.connecting;
126
- const token = this.effectiveToken();
127
- if (!token)
128
- throw new Error("no grouping token set (session not started)");
129
- const url = tokenedUrl(this.conn.baseUrl, token);
130
- let endpoint;
131
- try {
132
- endpoint = new URL(url);
133
- }
134
- catch {
135
- throw new Error(`combiner URL "${url}" is not parseable; set a valid url (…/mcp) in settings or mcp.json`);
136
- }
137
- const bearer = this.conn.bearerTokenEnv ? process.env[this.conn.bearerTokenEnv] : undefined;
138
- this.state = "connecting";
139
- this.connecting = (async () => {
140
- const client = new Client(CLIENT_INFO, { capabilities: { elicitation: {} } });
141
- const transport = new StreamableHTTPClientTransport(endpoint, {
142
- authProvider: bearer ? { token: async () => bearer } : undefined,
143
- });
144
- // Elicitation bridge — the combiner's permission gate depends on it.
145
- client.setRequestHandler("elicitation/create", (req) => {
146
- const params = toElicitParams(isRecord(req) ? req.params : undefined);
147
- if (!params)
148
- return { action: "decline" };
149
- return handleElicitation(params, this.elicitUi);
150
- });
151
- client.setNotificationHandler("notifications/tools/list_changed", () => {
152
- this.tools = undefined; // refetch on next list
153
- this.prompts = undefined;
154
- this.resources = undefined;
155
- try {
156
- this.hooks.onToolsChanged?.();
157
- }
158
- catch {
159
- // hook errors must never break the notification path
160
- }
161
- });
162
- client.setNotificationHandler("notifications/resources/list_changed", () => {
163
- this.resources = undefined;
164
- try {
165
- this.hooks.onToolsChanged?.();
166
- }
167
- catch {
168
- // hook errors must never break the notification path
169
- }
170
- });
171
- // Stage 2: the widget hold announces its UI URL via a log
172
- // notification ("Interactive UI ready: <url>") while the agent's
173
- // call is in flight — open it so the user sees the widget at once.
174
- client.setNotificationHandler("notifications/message", (n) => {
175
- const data = n.params?.data;
176
- if (typeof data !== "string")
177
- return;
178
- const m = data.match(/Interactive UI ready: (\S+)/);
179
- if (m) {
180
- try {
181
- this.hooks.onWidgetUrl?.(m[1]);
182
- }
183
- catch {
184
- // hook errors must never break the notification path
185
- }
186
- }
187
- });
188
- client.onclose = () => {
189
- if (this.client === client) {
190
- this.client = undefined;
191
- this.state = "disconnected";
192
- }
193
- };
194
- await Promise.race([
195
- client.connect(transport),
196
- new Promise((_, reject) => setTimeout(() => reject(new Error("connect timeout")), CONNECT_TIMEOUT_MS)),
197
- ]);
198
- this.client = client;
199
- this.state = "connected";
200
- this.lastError = undefined;
201
- this.hooks.onStateChange?.(this.state);
202
- return client;
203
- })();
204
- try {
205
- return await this.connecting;
206
- }
207
- catch (e) {
208
- this.state = "failed";
209
- this.lastError = e instanceof Error ? e.message : String(e);
210
- this.log("warn", `combiner connect failed (${this.lastError})`);
211
- this.hooks.onStateChange?.(this.state);
212
- throw new Error(`cannot reach the combiner at ${this.conn.baseUrl}: ${this.lastError}`);
213
- }
214
- finally {
215
- this.connecting = undefined;
216
- }
217
- }
218
- /** tools/list, cached until invalidated (list_changed / reset / reconnect). */
219
- async listTools(force = false) {
220
- if (!force && this.tools && Date.now() - this.toolsFetchedAt < 5_000)
221
- return this.tools;
222
- const client = await this.ensureConnected();
223
- const res = await client.listTools();
224
- this.tools = (res.tools ?? []);
225
- this.toolsFetchedAt = Date.now();
226
- return this.tools;
227
- }
228
- /** prompts/list, cached alongside tools (combiner namespaces `<server>_<name>`). */
229
- async listPrompts(force = false) {
230
- if (!force && this.prompts && Date.now() - this.promptsFetchedAt < 5_000)
231
- return this.prompts;
232
- const client = await this.ensureConnected();
233
- const res = await client.listPrompts();
234
- this.prompts = (res.prompts ?? []);
235
- this.promptsFetchedAt = Date.now();
236
- return this.prompts;
237
- }
238
- /** prompts/get through the combiner. */
239
- async getPrompt(name, args) {
240
- const client = await this.ensureConnected();
241
- return client.getPrompt({ name, arguments: args });
242
- }
243
- /** resources/list, cached alongside tools/prompts. */
244
- async listResources(force = false) {
245
- if (!force && this.resources && Date.now() - this.resourcesFetchedAt < 5_000)
246
- return this.resources;
247
- const client = await this.ensureConnected();
248
- const res = await client.listResources();
249
- this.resources = (res.resources ?? []);
250
- this.resourcesFetchedAt = Date.now();
251
- return this.resources;
252
- }
253
- /** resources/read through the combiner. */
254
- async readResource(uri) {
255
- const client = await this.ensureConnected();
256
- return client.readResource({ uri });
257
- }
258
- /** callTool with a single stale-session retry: on transport/stale errors we reset
259
- * and reconnect once (the combiner may have bounced; handover keeps our token). */
260
- async callTool(name, args) {
261
- const attempt = async () => {
262
- const client = await this.ensureConnected();
263
- return client.callTool({ name, arguments: args ?? {} });
264
- };
265
- try {
266
- return await attempt();
267
- }
268
- catch (e) {
269
- const msg = e instanceof Error ? e.message : String(e);
270
- const stale = /404|stale|session|not found|closed|fetch failed/i.test(msg);
271
- if (!stale)
272
- throw e;
273
- this.log("info", `call "${name}" went stale (${msg}); reconnecting once`);
274
- await this.reset("stale session");
275
- return await attempt();
276
- }
277
- }
278
- /** Control-plane origin for the combiner's REST routes (/health, /sessions/*). */
279
- controlOrigin() {
280
- try {
281
- return new URL(this.conn.baseUrl).origin;
282
- }
283
- catch {
284
- return this.conn.baseUrl;
285
- }
286
- }
287
- /** The interactive-UI URL for a resource (the combiner's UI host serves the
288
- * widget for THIS session's token), or empty when no token is set. */
289
- uiUrlFor(resourceUri) {
290
- const token = this.effectiveToken();
291
- if (!token || !resourceUri)
292
- return "";
293
- return `${this.controlOrigin()}/ui/${encodeURIComponent(token)}/?resource=${encodeURIComponent(resourceUri)}`;
294
- }
295
- /** Headers for control-plane calls: the same inbound bearer as /mcp (the combiner
296
- * gates its mutating routes — /sessions*, /handover* — with it; /health is open
297
- * but presenting anyway is harmless). */
298
- controlHeaders() {
299
- const token = this.conn.bearerTokenEnv ? process.env[this.conn.bearerTokenEnv] : undefined;
300
- return token ? { authorization: `Bearer ${token}` } : {};
301
- }
302
- /** POST the project-derived server filter for our token. The combiner accepts
303
- * pending filters pre-connect, so this is safe before the first connect. */
304
- async applyFilter(filter) {
305
- const token = this.effectiveToken();
306
- if (!token)
307
- throw new Error("no grouping token set (session not started)");
308
- const body = {};
309
- if (filter.allow?.length)
310
- body.allowed_servers = filter.allow;
311
- else if (filter.deny?.length)
312
- body.disabled_servers = filter.deny;
313
- else
314
- return;
315
- const res = await fetch(`${this.controlOrigin()}/sessions/token/${encodeURIComponent(token)}/filter`, {
316
- method: "POST",
317
- headers: { "content-type": "application/json", ...this.controlHeaders() },
318
- body: JSON.stringify(body),
319
- });
320
- if (!res.ok)
321
- throw new Error(`filter apply failed: ${res.status} ${await res.text().catch(() => "")}`);
322
- this.log("info", `session server filter applied (${filter.allow ? "allow" : "deny"}-list)`);
323
- }
324
- /** GET /health — the control-plane health snapshot. */
325
- async health() {
326
- const res = await fetch(`${this.controlOrigin()}/health`, { headers: this.controlHeaders() });
327
- if (!res.ok)
328
- throw new Error(`/health returned ${res.status}`);
329
- return res.json();
330
- }
331
- }
332
- //# sourceMappingURL=connection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/client/connection.ts"],"names":[],"mappings":"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"}