@geohar/pi-mcp-combiner 0.14.3 → 0.16.0-dev.2
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 +71 -48
- package/dist/client/config-ladder.d.ts +1 -1
- package/dist/client/config-ladder.d.ts.map +1 -1
- package/dist/client/control.d.ts +1 -1
- package/dist/client/control.d.ts.map +1 -1
- package/dist/client/elicitation.d.ts +4 -2
- package/dist/client/elicitation.d.ts.map +1 -1
- package/dist/client/elicitation.js +8 -2
- package/dist/client/elicitation.js.map +1 -1
- package/dist/client/footer.d.ts +1 -1
- package/dist/client/footer.d.ts.map +1 -1
- package/dist/client/panel.d.ts +1 -1
- package/dist/client/panel.d.ts.map +1 -1
- package/dist/client/panel.js +1 -1
- package/dist/client/panel.js.map +1 -1
- package/dist/client/prompts.d.ts +1 -1
- package/dist/client/prompts.d.ts.map +1 -1
- package/dist/client/proxy-tool.d.ts +1 -1
- package/dist/client/proxy-tool.d.ts.map +1 -1
- package/dist/client/resource-naming.d.ts +8 -0
- package/dist/client/resource-naming.d.ts.map +1 -0
- package/dist/client/resource-naming.js +33 -0
- package/dist/client/resource-naming.js.map +1 -0
- package/dist/client/settings.d.ts +5 -4
- package/dist/client/settings.d.ts.map +1 -1
- package/dist/client/settings.js +3 -3
- package/dist/client/settings.js.map +1 -1
- package/dist/client/tool-matching.d.ts +32 -0
- package/dist/client/tool-matching.d.ts.map +1 -0
- package/dist/client/tool-matching.js +43 -0
- package/dist/client/tool-matching.js.map +1 -0
- package/dist/client/types.d.ts +71 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +16 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/widget-support.d.ts +17 -0
- package/dist/client/widget-support.d.ts.map +1 -0
- package/dist/client/widget-support.js +89 -0
- package/dist/client/widget-support.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +109 -53
- package/dist/index.js.map +1 -1
- package/dist/{client/connection.d.ts → native/combiner-connection.d.ts} +46 -38
- package/dist/native/combiner-connection.d.ts.map +1 -0
- package/dist/native/combiner-connection.js +412 -0
- package/dist/native/combiner-connection.js.map +1 -0
- package/dist/native/index.d.ts +30 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +61 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/resources.d.ts +18 -0
- package/dist/native/resources.d.ts.map +1 -0
- package/dist/native/resources.js +97 -0
- package/dist/native/resources.js.map +1 -0
- package/dist/native/tool-surface.d.ts +26 -0
- package/dist/native/tool-surface.d.ts.map +1 -0
- package/dist/native/tool-surface.js +113 -0
- package/dist/native/tool-surface.js.map +1 -0
- package/dist/pi.d.ts +19 -0
- package/dist/pi.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/client/config-ladder.ts +1 -1
- package/src/client/control.ts +1 -1
- package/src/client/elicitation.ts +10 -2
- package/src/client/footer.ts +1 -1
- package/src/client/panel.ts +2 -2
- package/src/client/prompts.ts +1 -1
- package/src/client/proxy-tool.ts +1 -1
- package/src/client/resource-naming.ts +36 -0
- package/src/client/settings.ts +7 -6
- package/src/client/tool-matching.ts +69 -0
- package/src/client/types.ts +80 -0
- package/src/client/widget-support.ts +86 -0
- package/src/index.ts +109 -65
- package/src/native/combiner-connection.ts +483 -0
- package/src/native/index.ts +100 -0
- package/src/native/resources.ts +112 -0
- package/src/native/tool-surface.ts +147 -0
- package/src/pi.ts +16 -0
- package/dist/client/connection.d.ts.map +0 -1
- package/dist/client/connection.js +0 -332
- package/dist/client/connection.js.map +0 -1
- package/dist/client/direct-tools.d.ts +0 -41
- package/dist/client/direct-tools.d.ts.map +0 -1
- package/dist/client/direct-tools.js +0 -122
- package/dist/client/direct-tools.js.map +0 -1
- package/dist/client/resources.d.ts +0 -20
- package/dist/client/resources.d.ts.map +0 -1
- package/dist/client/resources.js +0 -119
- package/dist/client/resources.js.map +0 -1
- package/dist/client/script.d.ts +0 -10
- package/dist/client/script.d.ts.map +0 -1
- package/dist/client/script.js +0 -130
- package/dist/client/script.js.map +0 -1
- package/src/client/connection.ts +0 -373
- package/src/client/direct-tools.ts +0 -146
- package/src/client/resources.ts +0 -137
- package/src/client/script.ts +0 -150
package/src/client/script.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// The <toolName>Script batching tool — API parity with pi-mcp-adapter's
|
|
2
|
-
// mcpScript (MIT, © 2026 Nico Bailon; mcp-code.ts), reimplemented lean:
|
|
3
|
-
// scriptMode: `{code, timeoutMs?}` executing trusted JavaScript with
|
|
4
|
-
// `await tools.search/describe/call()` and `emit(value)` for user-visible output.
|
|
5
|
-
//
|
|
6
|
-
// Lean port: pi-mcp-adapter runs the code in a worker thread (mcp-code.ts) for hard
|
|
7
|
-
// timeout termination; we evaluate in-process with an AsyncFunction and race a timer.
|
|
8
|
-
// The trade-off is deliberate — trusted code, one loopback server — an abandoned
|
|
9
|
-
// call finishes harmlessly combiner-side, and the API surface (what agents write)
|
|
10
|
-
// is identical. Default timeout 30s, matching the adapter.
|
|
11
|
-
|
|
12
|
-
import type { ToolDefinition } from "../pi.js"
|
|
13
|
-
import type { CombinerConnection, ToolSummary } from "./connection.js"
|
|
14
|
-
import { rankTools, regexMatches, compileSafeRegex } from "./ranking.js"
|
|
15
|
-
import { renderDescribe, renderSearchHit, renderToolResult, textResult } from "./render.js"
|
|
16
|
-
import { callRenderer, resultRenderer } from "./renderers.js"
|
|
17
|
-
|
|
18
|
-
export const DEFAULT_SCRIPT_TIMEOUT_MS = 30_000
|
|
19
|
-
const MAX_SCRIPT_TIMEOUT_MS = 120_000
|
|
20
|
-
const MAX_EMITTED_BLOCKS = 200
|
|
21
|
-
const MAX_EMIT_CHARS = 16 * 1024
|
|
22
|
-
|
|
23
|
-
export type ScriptToolDeps = {
|
|
24
|
-
connection: CombinerConnection
|
|
25
|
-
/** Tool name for the batching tool — `${toolName}Script` ("mcpScript" in parity). */
|
|
26
|
-
name: string
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const AsyncFunction = Object.getPrototypeOf(async () => {
|
|
30
|
-
/* capture */
|
|
31
|
-
}).constructor as new (...args: string[]) => (tools: unknown, emit: (v: unknown) => void) => Promise<unknown>
|
|
32
|
-
|
|
33
|
-
type ToolsApi = {
|
|
34
|
-
search: (query?: string, opts?: { server?: string; limit?: number; offset?: number; regex?: boolean }) => unknown
|
|
35
|
-
describe: (name: string) => unknown
|
|
36
|
-
call: (name: string, args?: Record<string, unknown>) => unknown
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function serverOf(name: string): string {
|
|
40
|
-
return name.split("_", 1)[0] ?? ""
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function formatValue(value: unknown): string {
|
|
44
|
-
if (typeof value === "string") return value
|
|
45
|
-
try {
|
|
46
|
-
return JSON.stringify(value, null, 2) ?? String(value)
|
|
47
|
-
} catch {
|
|
48
|
-
return "[unserializable value]"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function createScriptTool(deps: ScriptToolDeps): ToolDefinition {
|
|
53
|
-
const { connection, name } = deps
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
name,
|
|
57
|
-
label: "MCP script (combiner)",
|
|
58
|
-
description: [
|
|
59
|
-
"Run trusted JavaScript that makes multiple MCP calls in one request.",
|
|
60
|
-
"API: const r = await tools.search('query', {limit}); await tools.describe('name');",
|
|
61
|
-
"await tools.call('name', {args}); emit(value) for user-visible output.",
|
|
62
|
-
"The final return value (or the last emitted value) becomes the tool result.",
|
|
63
|
-
].join(" "),
|
|
64
|
-
promptSnippet: "Batch multiple MCP calls with trusted JavaScript (tools.search/describe/call + emit).",
|
|
65
|
-
parameters: {
|
|
66
|
-
type: "object",
|
|
67
|
-
properties: {
|
|
68
|
-
code: { type: "string", description: "JavaScript to run (await allowed)" },
|
|
69
|
-
timeoutMs: { type: "number", description: `Timeout in ms (default ${DEFAULT_SCRIPT_TIMEOUT_MS})` },
|
|
70
|
-
},
|
|
71
|
-
required: ["code"],
|
|
72
|
-
},
|
|
73
|
-
renderCall: callRenderer(name, (args) => {
|
|
74
|
-
const code = typeof args.code === "string" ? (args.code.trim().split("\n", 1)[0] ?? "") : ""
|
|
75
|
-
return code.length > 60 ? `${code.slice(0, 59)}…` : code
|
|
76
|
-
}),
|
|
77
|
-
renderResult: resultRenderer(() => name, name),
|
|
78
|
-
execute: async (_toolCallId, params) => {
|
|
79
|
-
const p = params as { code?: unknown; timeoutMs?: unknown }
|
|
80
|
-
if (typeof p.code !== "string" || !p.code.trim()) {
|
|
81
|
-
throw new Error(`${name}: code must be a non-empty string`)
|
|
82
|
-
}
|
|
83
|
-
const timeoutMs =
|
|
84
|
-
typeof p.timeoutMs === "number" && p.timeoutMs > 0
|
|
85
|
-
? Math.min(MAX_SCRIPT_TIMEOUT_MS, p.timeoutMs)
|
|
86
|
-
: DEFAULT_SCRIPT_TIMEOUT_MS
|
|
87
|
-
|
|
88
|
-
const emitted: string[] = []
|
|
89
|
-
let emittedChars = 0
|
|
90
|
-
const emit = (value: unknown): void => {
|
|
91
|
-
if (emitted.length >= MAX_EMITTED_BLOCKS) return
|
|
92
|
-
const text = formatValue(value).slice(0, MAX_EMIT_CHARS)
|
|
93
|
-
emittedChars += text.length
|
|
94
|
-
emitted.push(text)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const tools: ToolsApi = {
|
|
98
|
-
search: (query, opts) => {
|
|
99
|
-
const q = typeof query === "string" ? query : ""
|
|
100
|
-
const server = opts?.server
|
|
101
|
-
return connection
|
|
102
|
-
.listTools()
|
|
103
|
-
.then((all) => {
|
|
104
|
-
const pool = server ? all.filter((t) => serverOf(t.name) === server) : all
|
|
105
|
-
if (opts?.regex) {
|
|
106
|
-
const { re, error } = compileSafeRegex(q)
|
|
107
|
-
if (!re) throw new Error(`bad regex: ${error}`)
|
|
108
|
-
return regexMatches(pool, re, opts?.limit ?? 12, opts?.offset ?? 0).map((m) => m.tool)
|
|
109
|
-
}
|
|
110
|
-
return rankTools(pool, q, opts?.limit ?? 12, opts?.offset ?? 0).map((m) => m.tool)
|
|
111
|
-
})
|
|
112
|
-
.then((tools) => tools.map((t) => renderSearchHit(t)))
|
|
113
|
-
.then((lines) => lines.join("\n"))
|
|
114
|
-
},
|
|
115
|
-
describe: (toolName) =>
|
|
116
|
-
connection
|
|
117
|
-
.listTools()
|
|
118
|
-
.then((all) => all.find((t) => t.name === toolName))
|
|
119
|
-
.then((t: ToolSummary | undefined) => {
|
|
120
|
-
if (!t) throw new Error(`no tool "${toolName}" — use tools.search first`)
|
|
121
|
-
return renderDescribe(t, serverOf(t.name))
|
|
122
|
-
}),
|
|
123
|
-
call: (toolName, args) => connection.callTool(toolName, args).then((r) => renderToolResult(r)),
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const fn = new AsyncFunction("tools", "emit", p.code)
|
|
128
|
-
const timer = new Promise<never>((_, reject) =>
|
|
129
|
-
setTimeout(() => reject(new Error(`${name} timed out after ${timeoutMs}ms`)), timeoutMs),
|
|
130
|
-
)
|
|
131
|
-
let result: unknown
|
|
132
|
-
try {
|
|
133
|
-
result = await Promise.race([fn(tools as unknown, emit), timer])
|
|
134
|
-
} finally {
|
|
135
|
-
// nothing to clean — the timer's reject is inert once the race settled
|
|
136
|
-
}
|
|
137
|
-
const resultText = result === undefined ? "" : formatValue(result)
|
|
138
|
-
const text = [...emitted, resultText].filter((s) => s.length > 0).join("\n\n")
|
|
139
|
-
return textResult(text || "(no output — emit(value) or return a value)", {
|
|
140
|
-
mode: "script",
|
|
141
|
-
emitted: emitted.length,
|
|
142
|
-
})
|
|
143
|
-
} catch (e) {
|
|
144
|
-
const msg = e instanceof Error ? e.message : String(e)
|
|
145
|
-
const text = emitted.length ? `${emitted.join("\n\n")}\n\n${name}: ${msg}` : `${name}: ${msg}`
|
|
146
|
-
throw new Error(text)
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
}
|
|
150
|
-
}
|