@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
@@ -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
- }