@tool-bridge/server 0.22.1 → 0.23.0

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 (110) hide show
  1. package/dist/admin.js +2 -2
  2. package/dist/{airtable-ML2GIGCH.js → airtable-VMQVXLHK.js} +6 -6
  3. package/dist/{alt_text_generator_ai-FGYQAF2A.js → alt_text_generator_ai-FTWY4RU4.js} +5 -5
  4. package/dist/{amap-G7V65NGK.js → amap-OK5VBHYC.js} +5 -5
  5. package/dist/{anthropic-6ZK24TBB.js → anthropic-S67AZFZ3.js} +6 -6
  6. package/dist/{apify-VDGYHP6W.js → apify-ZE6MYQI3.js} +6 -6
  7. package/dist/{brave_search-TAATLZOS.js → brave_search-TG2M2KOD.js} +6 -6
  8. package/dist/{chunk-7ISGFRC6.js → chunk-2JNAI4OC.js} +1 -1
  9. package/dist/{chunk-RMAWSG7E.js → chunk-4XJPN3DY.js} +353 -552
  10. package/dist/{chunk-HA4BGL6C.js → chunk-5ICRY3VU.js} +15 -12
  11. package/dist/{chunk-2IAI3XQM.js → chunk-7CTWGTDU.js} +2 -2
  12. package/dist/{chunk-UY6ANCOY.js → chunk-BADVCN6K.js} +1 -1
  13. package/dist/{chunk-RTW5VRTK.js → chunk-TDAWLMDE.js} +3 -3
  14. package/dist/{chunk-BNMLA2RC.js → chunk-VWRUGUSU.js} +3 -3
  15. package/dist/{chunk-X7ZUTKHR.js → chunk-WJHG6OK7.js} +2 -2
  16. package/dist/{circleci-BQ2UWXL2.js → circleci-3L3WJVSK.js} +6 -6
  17. package/dist/{clerk-2JYWQNOI.js → clerk-EZPF2V7T.js} +6 -6
  18. package/dist/{cohere-6MI7AQM4.js → cohere-U27DPO64.js} +6 -6
  19. package/dist/{confluence-6Y6GDETF.js → confluence-G2KL6ERH.js} +6 -6
  20. package/dist/{convertapi-HBZCSWOX.js → convertapi-L4CLJ2M3.js} +4 -4
  21. package/dist/{currencyapi-337U5TGI.js → currencyapi-YPYXG7YY.js} +6 -6
  22. package/dist/{deepseek-OSPASO7O.js → deepseek-6HT6WF3M.js} +6 -6
  23. package/dist/{docker_hub-3ATQOY5L.js → docker_hub-LPE3PCVB.js} +5 -5
  24. package/dist/{dropbox-LSA4GM2Y.js → dropbox-V56ABHTA.js} +6 -6
  25. package/dist/{dub-VZPJSPI6.js → dub-6OKQ7MHT.js} +6 -6
  26. package/dist/{e2b-6SDYQZL2.js → e2b-JS2U2QFU.js} +6 -6
  27. package/dist/{exa-B6EUYDVP.js → exa-DPWVYXSP.js} +6 -6
  28. package/dist/{fal_ai-K4Q27IME.js → fal_ai-DK6Y27ZF.js} +6 -6
  29. package/dist/{feishu-4TKXG4SU.js → feishu-K7EGSRF3.js} +3 -3
  30. package/dist/{feishu_custom_bot-7CTEAKRB.js → feishu_custom_bot-2JUGFYNG.js} +4 -4
  31. package/dist/{figma-OUKSZLD5.js → figma-FT5I7ITD.js} +6 -6
  32. package/dist/{firehydrant-7MOYL7QH.js → firehydrant-EIKLKHX2.js} +6 -6
  33. package/dist/{fixer-H2BV2NO4.js → fixer-U5QXIAKD.js} +5 -5
  34. package/dist/{fly-PXKY6PNH.js → fly-T3JYFZ2M.js} +6 -6
  35. package/dist/{front-UZYIV5DW.js → front-D72MGVOR.js} +6 -6
  36. package/dist/{geocodio-NSYO7TFP.js → geocodio-BOTD5TI5.js} +5 -5
  37. package/dist/{ghost-QBKKQQMF.js → ghost-KYP7SZGB.js} +5 -5
  38. package/dist/{github-ENLSJ4CL.js → github-AGMZT3VY.js} +6 -6
  39. package/dist/{gmail-KQSDEB47.js → gmail-BLJNJOBG.js} +6 -6
  40. package/dist/{googlecalendar-5YCX4SZF.js → googlecalendar-GTHWNCF6.js} +6 -6
  41. package/dist/{googledocs-SSWIVFKA.js → googledocs-6EEDMEBQ.js} +4 -4
  42. package/dist/{grafana-TF2LXDQA.js → grafana-GMW6EKSQ.js} +6 -6
  43. package/dist/{graphhopper-CURBVHYK.js → graphhopper-6OLGBDGB.js} +5 -5
  44. package/dist/index.js +5 -5
  45. package/dist/{ip2proxy-YNVMTZEM.js → ip2proxy-4QN42E6T.js} +5 -5
  46. package/dist/{ipgeolocation_io-NAU7VJ4J.js → ipgeolocation_io-SK4XKDA2.js} +5 -5
  47. package/dist/{ipqualityscore-OT63L4TI.js → ipqualityscore-6ANBPXKT.js} +5 -5
  48. package/dist/{jina_ai-6WBMBGWM.js → jina_ai-IHPSVRM5.js} +6 -6
  49. package/dist/{jira-DVDVTHNO.js → jira-46KKM4LC.js} +6 -6
  50. package/dist/{langsmith-IM5N4YAU.js → langsmith-JZA345I5.js} +6 -6
  51. package/dist/{lemlist-277SHUQF.js → lemlist-43IV7DK6.js} +6 -6
  52. package/dist/{linear-AEPH7B5V.js → linear-5LFH7XNL.js} +4 -4
  53. package/dist/{logsnag-VEWRAGBG.js → logsnag-5RMYAHIL.js} +6 -6
  54. package/dist/main.js +5 -5
  55. package/dist/{mattermost-Q3A2BIZA.js → mattermost-XFL6SHGG.js} +6 -6
  56. package/dist/{mem0-AXS7GZGI.js → mem0-NYO4DTP6.js} +6 -6
  57. package/dist/{memos-7TXG4ZTU.js → memos-37MPQOLV.js} +6 -6
  58. package/dist/{metabase-NSVACWQM.js → metabase-KYBKZFMG.js} +6 -6
  59. package/dist/{mistral_ai-YZ52RGD4.js → mistral_ai-6PL3K4PM.js} +6 -6
  60. package/dist/{mother_duck-XLLL6FVL.js → mother_duck-2NH7W5M5.js} +6 -6
  61. package/dist/{ngrok-4O4W3WZW.js → ngrok-UO6CDYCA.js} +6 -6
  62. package/dist/{notes-YTB3TJOO.js → notes-I3P32WP4.js} +2 -2
  63. package/dist/{notion-4W4O7J7V.js → notion-BEAJFUOE.js} +6 -6
  64. package/dist/{open_exchange_rates-23QJXVO5.js → open_exchange_rates-CMF62JB4.js} +5 -5
  65. package/dist/{openai-ZS7GLSLH.js → openai-IHOMKVND.js} +6 -6
  66. package/dist/{opengraph_io-PMVH7OYI.js → opengraph_io-ZQ2FGLHX.js} +5 -5
  67. package/dist/{openrouter-ONRGFNB6.js → openrouter-HXLW3NR5.js} +6 -6
  68. package/dist/{outline-UCUYZZ7J.js → outline-ELCP4NEU.js} +6 -6
  69. package/dist/{paddle-35HTKTUL.js → paddle-O77LALVN.js} +6 -6
  70. package/dist/{perplexity-NVOBYN24.js → perplexity-JCU654QV.js} +6 -6
  71. package/dist/{pinecone-55CJY73C.js → pinecone-W5W4LRKL.js} +6 -6
  72. package/dist/{pipedrive-WMCAKS72.js → pipedrive-ITMJLLPZ.js} +6 -6
  73. package/dist/{polar-SM4ZM53B.js → polar-R33XU4L3.js} +6 -6
  74. package/dist/{posthog-NWCXVXNJ.js → posthog-JVNTOLTV.js} +6 -6
  75. package/dist/{postmark-QJV5PHUQ.js → postmark-6SBG7HWV.js} +6 -6
  76. package/dist/{prerender-2W7UT4SC.js → prerender-GPNKTNFC.js} +5 -5
  77. package/dist/{pubmed-VVV7XYLU.js → pubmed-UP6ZEEIH.js} +4 -4
  78. package/dist/{railway-KGI57S3O.js → railway-KWEWXHTY.js} +4 -4
  79. package/dist/{readwise-AGHTQB42.js → readwise-BLATSMC3.js} +6 -6
  80. package/dist/{render-O4HBROCK.js → render-QYRVIXII.js} +6 -6
  81. package/dist/{replicate-KWBOMX7D.js → replicate-JYTWH4NF.js} +6 -6
  82. package/dist/{resend-DRH6Y6I4.js → resend-L5N75SZM.js} +6 -6
  83. package/dist/{rootly-XGCUDW6X.js → rootly-MHCPEQ26.js} +6 -6
  84. package/dist/{scrapfly-OIQ3OY6L.js → scrapfly-PTPGYSJM.js} +5 -5
  85. package/dist/{scrapingbee-OCZO4FHX.js → scrapingbee-JN6UH5PY.js} +5 -5
  86. package/dist/{screenshot_fyi-CUNGFZOC.js → screenshot_fyi-4QRVSV55.js} +5 -5
  87. package/dist/{semantic_scholar-3UJRLKES.js → semantic_scholar-LRSWA2GE.js} +6 -6
  88. package/dist/{sentry-5BWVZFJV.js → sentry-2G6PF5W6.js} +6 -6
  89. package/dist/{shodan-HI37CTEW.js → shodan-WKKPP5C6.js} +5 -5
  90. package/dist/{shopify-LJ2EV2HH.js → shopify-64YDK5T2.js} +6 -6
  91. package/dist/{stripe-3LHMV55K.js → stripe-QAIGGOTF.js} +4 -4
  92. package/dist/{supabase-H2KPX3Q2.js → supabase-WHTGR6ND.js} +6 -6
  93. package/dist/{tavily-FBEWGH3Z.js → tavily-QIXCDDWO.js} +6 -6
  94. package/dist/{telegram-MYYO3JND.js → telegram-E6G7CWI6.js} +5 -5
  95. package/dist/{telnyx-H4ALEGAX.js → telnyx-ZDVVS7PK.js} +6 -6
  96. package/dist/{todoist-OBWICA5Q.js → todoist-DMYF3G7J.js} +6 -6
  97. package/dist/{trello-GUFUJRWO.js → trello-HY2RYIRP.js} +5 -5
  98. package/dist/{turso-NIAK4UMS.js → turso-HAQWMGRP.js} +6 -6
  99. package/dist/{twilio-K6SLK2CX.js → twilio-MLXYPHJE.js} +4 -4
  100. package/dist/{umami-42RS2EPS.js → umami-DZHBLPVG.js} +6 -6
  101. package/dist/{unsplash-V7TOTETT.js → unsplash-GS2OPXKE.js} +6 -6
  102. package/dist/{upstash_redis-AWQMB6Y3.js → upstash_redis-XMXWTNAJ.js} +6 -6
  103. package/dist/{uptimerobot-AZC2DHOT.js → uptimerobot-TF5WRZ4G.js} +4 -4
  104. package/dist/{v2ex-4TVRNMLS.js → v2ex-2BVXAPDF.js} +6 -6
  105. package/dist/{vercel-DPHTJ3XR.js → vercel-YMJGQYJE.js} +6 -6
  106. package/dist/{wolfram_alpha_api-XAG7IXBW.js → wolfram_alpha_api-I56SXWFZ.js} +5 -5
  107. package/dist/{wordpress-KMPKVGUX.js → wordpress-R7TDFZ2P.js} +6 -6
  108. package/dist/{workos-PKB2YR7S.js → workos-HDNHWVMS.js} +6 -6
  109. package/dist/{zhihu-Y4H4XYPB.js → zhihu-LX6AGGDS.js} +6 -6
  110. package/package.json +2 -2
@@ -10,15 +10,14 @@ import {
10
10
  import {
11
11
  BootstrapStateStore,
12
12
  package_default
13
- } from "./chunk-2IAI3XQM.js";
13
+ } from "./chunk-7CTWGTDU.js";
14
14
  import {
15
15
  createGuardedFetch
16
- } from "./chunk-7ISGFRC6.js";
16
+ } from "./chunk-2JNAI4OC.js";
17
17
  import {
18
18
  AnnotationStore,
19
19
  CONTEXT_CAPABILITIES,
20
20
  CONTEXT_DIRECT_UPLOAD_CAPABILITY,
21
- DEFAULT_MAX_NODES,
22
21
  DEVICE_CALL_TIMEOUT_MS,
23
22
  DeviceGatewaySession,
24
23
  DeviceMailboxService,
@@ -35,6 +34,7 @@ import {
35
34
  MAX_TREE_DEPTH,
36
35
  NodeRegistryStore,
37
36
  OAUTH_CLIENT_FIELDS,
37
+ OperationRegistry,
38
38
  PG_SEARCH_INSERT_ROWS_MAX,
39
39
  PG_SEARCH_SCHEMA_STATEMENTS,
40
40
  PG_SEARCH_SNAPSHOTS_COLUMNS,
@@ -72,6 +72,7 @@ import {
72
72
  buildHttpRequest,
73
73
  buildTokenRequest,
74
74
  buildTree,
75
+ canonicalizePath,
75
76
  catalogSetDigest,
76
77
  check,
77
78
  checkAllowlist,
@@ -109,6 +110,7 @@ import {
109
110
  deviceOperationListResponseSchema,
110
111
  deviceOperationRenewRequestSchema,
111
112
  deviceOperationRenewResponseSchema,
113
+ deviceOperationStateSchema,
112
114
  deviceOperationSummarySchema,
113
115
  deviceShellHelpModel,
114
116
  dispatchContextCmd,
@@ -176,6 +178,7 @@ import {
176
178
  storeUri,
177
179
  tbErrorBodySchema,
178
180
  toolHelpModel,
181
+ toolSearchOptionsSchema,
179
182
  toolSearchPageSchema,
180
183
  toolSearchRelevanceSchema,
181
184
  toolSearchRequestSchema,
@@ -188,7 +191,7 @@ import {
188
191
  validatePath,
189
192
  validateStoreTokenKeyring,
190
193
  virtualizeTools
191
- } from "./chunk-HA4BGL6C.js";
194
+ } from "./chunk-5ICRY3VU.js";
192
195
 
193
196
  // src/config.ts
194
197
  function runtimeAppSettings(settings, instanceId) {
@@ -216,7 +219,7 @@ function runtimeAppSettings(settings, instanceId) {
216
219
  // ../app/package.json
217
220
  var package_default2 = {
218
221
  name: "@tool-bridge/app",
219
- version: "0.21.0",
222
+ version: "0.22.0",
220
223
  description: "Host-neutral HTBP application: assemble the tool-bridge tree on any runtime by injecting state, object, secret, device-channel and search adapters",
221
224
  type: "module",
222
225
  license: "MIT",
@@ -254,7 +257,8 @@ var package_default2 = {
254
257
  "@modelcontextprotocol/client": "catalog:",
255
258
  "@modelcontextprotocol/core": "catalog:",
256
259
  "@modelcontextprotocol/server": "catalog:",
257
- hono: "catalog:"
260
+ hono: "catalog:",
261
+ zod: "catalog:"
258
262
  },
259
263
  devDependencies: {
260
264
  "@modelcontextprotocol/sdk": "catalog:",
@@ -262,8 +266,7 @@ var package_default2 = {
262
266
  "@tool-bridge/plugins": "workspace:*",
263
267
  tsup: "catalog:",
264
268
  typescript: "catalog:",
265
- vitest: "catalog:",
266
- zod: "catalog:"
269
+ vitest: "catalog:"
267
270
  },
268
271
  repository: {
269
272
  type: "git",
@@ -13607,8 +13610,8 @@ var require_utils = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
13607
13610
  }
13608
13611
  function consumeHextets(buffer, address, output) {
13609
13612
  if (buffer.length) {
13610
- const hex2 = stringArrayToHexStripped(buffer);
13611
- if (hex2 !== "") address.push(hex2);
13613
+ const hex = stringArrayToHexStripped(buffer);
13614
+ if (hex !== "") address.push(hex);
13612
13615
  else {
13613
13616
  output.error = true;
13614
13617
  return false;
@@ -24524,8 +24527,8 @@ function syntheticElicitationId() {
24524
24527
  webCrypto.getRandomValues(bytes);
24525
24528
  bytes[6] = bytes[6] & 15 | 64;
24526
24529
  bytes[8] = bytes[8] & 63 | 128;
24527
- const hex2 = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
24528
- return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
24530
+ const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
24531
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
24529
24532
  }
24530
24533
  function legacyShimFailure(method, message) {
24531
24534
  if (method === "tools/call") return {
@@ -27003,24 +27006,6 @@ function createMcpHandler(factory, options = {}) {
27003
27006
  }
27004
27007
 
27005
27008
  // ../app/src/mcpServer.ts
27006
- function hex(bytes) {
27007
- return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
27008
- }
27009
- function mcpToolIdentity(invokePath, toolName) {
27010
- return JSON.stringify([invokePath, toolName]);
27011
- }
27012
- async function mcpToolName(identity2) {
27013
- const encoded = [...new TextEncoder().encode(identity2)].map((byte) => {
27014
- const char = String.fromCharCode(byte);
27015
- if (/[A-Za-z0-9.-]/.test(char)) return char;
27016
- if (char === "_") return "__";
27017
- return `_${byte.toString(16).padStart(2, "0")}`;
27018
- }).join("");
27019
- const prefixed = `tb_${encoded}`;
27020
- if (prefixed.length <= 128) return prefixed;
27021
- const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(identity2)));
27022
- return `tb_${encoded.slice(0, 60)}_${hex(digest)}`;
27023
- }
27024
27009
  var SUBSCHEMA_KEYS = [
27025
27010
  "additionalProperties",
27026
27011
  "contains",
@@ -27050,7 +27035,10 @@ function assertSchemaShape(value, path, depth = 0) {
27050
27035
  if (depth >= MAX_SCHEMA_DEPTH) throw new Error(`JSON Schema nested too deeply at '${path}'`);
27051
27036
  const schema = value;
27052
27037
  for (const key of SUBSCHEMA_KEYS) {
27053
- if (schema[key] !== void 0) assertSchemaShape(schema[key], `${path}/${key}`, depth + 1);
27038
+ const sub = schema[key];
27039
+ if (key === "items" && Array.isArray(sub)) {
27040
+ sub.forEach((item, i) => assertSchemaShape(item, `${path}/items/${i}`, depth + 1));
27041
+ } else if (sub !== void 0) assertSchemaShape(sub, `${path}/${key}`, depth + 1);
27054
27042
  }
27055
27043
  for (const key of SUBSCHEMA_MAP_KEYS) {
27056
27044
  const map = schema[key];
@@ -27069,9 +27057,9 @@ function assertSchemaShape(value, path, depth = 0) {
27069
27057
  list.forEach((sub, i) => assertSchemaShape(sub, `${path}/${key}/${i}`, depth + 1));
27070
27058
  }
27071
27059
  }
27072
- function inputSchemaOf(raw, delivery) {
27073
- if (raw === void 0 && delivery === void 0) return { type: "object", properties: {} };
27074
- const source = raw ?? { type: "object", properties: {} };
27060
+ function inputSchemaOf(raw) {
27061
+ if (raw === void 0) return { type: "object", properties: {} };
27062
+ const source = raw;
27075
27063
  if (source === null || typeof source !== "object" || Array.isArray(source)) {
27076
27064
  throw new Error("MCP tool inputSchema must be a JSON Schema object");
27077
27065
  }
@@ -27080,23 +27068,21 @@ function inputSchemaOf(raw, delivery) {
27080
27068
  throw new Error("MCP tool inputSchema root type must be object");
27081
27069
  }
27082
27070
  assertSchemaShape(schema, "inputSchema");
27083
- if (delivery !== "mailbox" && delivery !== "both") {
27084
- return { ...schema, type: "object" };
27071
+ return { ...schema, type: "object" };
27072
+ }
27073
+ function inputValidator(raw, path) {
27074
+ try {
27075
+ return new ToolJsonSchemaValidator().getValidator(
27076
+ inputSchemaOf(raw)
27077
+ );
27078
+ } catch {
27079
+ throw new ProtocolError3(ProtocolErrorCode2.InternalError, `invalid input schema for '${path}'`);
27085
27080
  }
27086
- const properties = schema.properties;
27087
- if (properties !== void 0 && (properties === null || typeof properties !== "object" || Array.isArray(properties))) throw new Error("MCP tool inputSchema properties must be an object");
27088
- return {
27089
- ...schema,
27090
- type: "object",
27091
- properties: {
27092
- ...properties ?? {},
27093
- "~delivery": {
27094
- type: "string",
27095
- enum: delivery === "both" ? ["realtime", "mailbox", "fallback"] : ["mailbox", "fallback"],
27096
- description: "Tool Bridge delivery policy; fallback prefers realtime and queues only when dispatch definitely did not occur."
27097
- }
27098
- }
27099
- };
27081
+ }
27082
+ function validateMcpArguments(raw, args, path) {
27083
+ const validate = inputValidator(raw, path);
27084
+ const result = validate(args);
27085
+ if (!result.valid) throw new ProtocolError3(ProtocolErrorCode2.InvalidParams, result.errorMessage);
27100
27086
  }
27101
27087
  function annotationsOf(tool) {
27102
27088
  const annotations = {};
@@ -27105,69 +27091,26 @@ function annotationsOf(tool) {
27105
27091
  if (tool.effect === "destructive" || tool.confirm === true) annotations.destructiveHint = true;
27106
27092
  return Object.keys(annotations).length > 0 ? annotations : void 0;
27107
27093
  }
27108
- async function projectTools(source, validator) {
27109
- const unique = /* @__PURE__ */ new Map();
27110
- for (const tool of source) {
27111
- if (unique.has(tool.identity)) {
27112
- throw new ProtocolError3(ProtocolErrorCode2.InternalError, "duplicate HTBP tool identity");
27113
- }
27114
- unique.set(tool.identity, tool);
27115
- }
27116
- const projected = await Promise.all(
27117
- [...unique.values()].map(async (bridge) => {
27118
- const name = bridge.mcpName ?? await mcpToolName(bridge.identity);
27119
- const annotations = annotationsOf(bridge);
27120
- const description = bridge.description === void 0 ? `HTBP ${bridge.invokePath}` : `${bridge.description}
27121
-
27122
- HTBP ${bridge.invokePath}`;
27123
- let inputSchema;
27124
- try {
27125
- inputSchema = inputSchemaOf(bridge.inputSchema, bridge.delivery);
27126
- } catch {
27127
- throw new ProtocolError3(
27128
- ProtocolErrorCode2.InternalError,
27129
- `invalid input schema for '${bridge.sourcePath}/${bridge.toolName}'`
27130
- );
27131
- }
27132
- const candidate = {
27133
- name,
27134
- description,
27135
- inputSchema,
27136
- _meta: {
27137
- "io.tool-bridge/path": bridge.sourcePath,
27138
- "io.tool-bridge/command": bridge.toolName
27139
- },
27140
- ...annotations !== void 0 ? { annotations } : {}
27141
- };
27142
- const parsed = ToolSchema.safeParse(candidate);
27143
- if (!parsed.success) {
27144
- throw new ProtocolError3(
27145
- ProtocolErrorCode2.InternalError,
27146
- `invalid tool metadata for '${bridge.sourcePath}/${bridge.toolName}'`
27147
- );
27148
- }
27149
- let validateInput;
27150
- try {
27151
- validateInput = validator.getValidator(
27152
- parsed.data.inputSchema
27153
- );
27154
- } catch {
27155
- throw new ProtocolError3(
27156
- ProtocolErrorCode2.InternalError,
27157
- `invalid input schema for '${bridge.sourcePath}/${bridge.toolName}'`
27158
- );
27159
- }
27160
- return { bridge, tool: parsed.data, validateInput };
27161
- })
27162
- );
27094
+ function projectTools(source) {
27163
27095
  const names = /* @__PURE__ */ new Set();
27164
- for (const item of projected) {
27165
- if (names.has(item.tool.name)) {
27096
+ return source.map((bridge) => {
27097
+ if (names.has(bridge.name)) {
27166
27098
  throw new ProtocolError3(ProtocolErrorCode2.InternalError, "MCP tool name collision");
27167
27099
  }
27168
- names.add(item.tool.name);
27169
- }
27170
- return projected.sort((a, b) => a.tool.name.localeCompare(b.tool.name));
27100
+ names.add(bridge.name);
27101
+ const annotations = annotationsOf(bridge);
27102
+ const validateInput = inputValidator(bridge.inputSchema, bridge.name);
27103
+ const parsed = ToolSchema.safeParse({
27104
+ name: bridge.name,
27105
+ description: bridge.description,
27106
+ inputSchema: inputSchemaOf(bridge.inputSchema),
27107
+ ...annotations === void 0 ? {} : { annotations }
27108
+ });
27109
+ if (!parsed.success) {
27110
+ throw new ProtocolError3(ProtocolErrorCode2.InternalError, `invalid tool metadata for '${bridge.name}'`);
27111
+ }
27112
+ return { bridge, tool: parsed.data, validateInput };
27113
+ }).sort((a, b) => a.tool.name.localeCompare(b.tool.name));
27171
27114
  }
27172
27115
  function contentOf(value) {
27173
27116
  if (Array.isArray(value)) {
@@ -27188,12 +27131,13 @@ function buildMcpServer(version, bridge, listTtlMs) {
27188
27131
  cacheHints: { "tools/list": { ttlMs: listTtlMs, cacheScope: "private" } }
27189
27132
  }
27190
27133
  );
27134
+ let projected;
27135
+ const tools = () => projected ??= bridge.list().then(projectTools);
27191
27136
  server.setRequestHandler("tools/list", async () => ({
27192
- tools: (await projectTools(await bridge.list(), validator)).map((item) => item.tool)
27137
+ tools: (await tools()).map((item) => item.tool)
27193
27138
  }));
27194
27139
  server.setRequestHandler("tools/call", async (rpc) => {
27195
- const projected = await projectTools(await bridge.list(), validator);
27196
- const selected = projected.find((item) => item.tool.name === rpc.params.name);
27140
+ const selected = (await tools()).find((item) => item.tool.name === rpc.params.name);
27197
27141
  if (selected === void 0) {
27198
27142
  throw new ProtocolError3(ProtocolErrorCode2.InvalidParams, "tool not found");
27199
27143
  }
@@ -27202,9 +27146,15 @@ function buildMcpServer(version, bridge, listTtlMs) {
27202
27146
  if (!validation.valid) {
27203
27147
  throw new ProtocolError3(ProtocolErrorCode2.InvalidParams, validation.errorMessage);
27204
27148
  }
27205
- const result = await bridge.call(selected.bridge, args);
27149
+ let result;
27150
+ try {
27151
+ result = await bridge.call(selected.bridge, args);
27152
+ } catch (error) {
27153
+ if (!isTBError(error)) throw error;
27154
+ result = { content: error.toJSON(), isError: true };
27155
+ }
27206
27156
  const candidate = {
27207
- content: contentOf(result.content),
27157
+ content: contentOf(result.contentBlocks ?? result.content),
27208
27158
  ...result.isError === true ? { isError: true } : {},
27209
27159
  ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : result.content !== null && typeof result.content === "object" && !Array.isArray(result.content) ? { structuredContent: result.content } : {}
27210
27160
  };
@@ -30676,6 +30626,9 @@ function registerSearchRoute(app, env) {
30676
30626
  );
30677
30627
  }
30678
30628
 
30629
+ // ../app/src/routes/mcp.ts
30630
+ import { z as z5 } from "zod/v4";
30631
+
30679
30632
  // ../app/src/helpModel.ts
30680
30633
  async function helpModelFor(node, registry, ctx, builtins, deps, opts) {
30681
30634
  const index = opts.schemas !== true;
@@ -30799,362 +30752,210 @@ async function commandHelpModelFor(registry, ctx, builtins, deps, path, opts) {
30799
30752
  };
30800
30753
  }
30801
30754
 
30802
- // ../app/src/routes/mcp.ts
30803
- var MCP_REMOTE_MAX_REQUESTS = 32;
30804
- var mcpCommand = (nodePath, nodeDescription, command) => {
30805
- const modelPath = nodePath.replace(/^\/+|\/+$/g, "");
30806
- const commandPath = command.path.replace(/^\/+|\/+$/g, "");
30807
- if (!commandPath.startsWith(`${modelPath}/`)) {
30808
- throw new TBError("internal", `command path '${command.path}' escapes node '${nodePath}'`);
30809
- }
30810
- const invokePath = `/${commandPath}`;
30811
- return {
30812
- identity: mcpToolIdentity(invokePath, command.name),
30813
- sourcePath: nodePath,
30814
- toolName: command.name,
30815
- invokePath,
30816
- description: command.h ?? nodeDescription,
30817
- ...command.inputSchema !== void 0 ? { inputSchema: command.inputSchema } : {},
30818
- ...command.delivery !== void 0 ? { delivery: command.delivery } : {},
30819
- ...command.effect !== void 0 ? { effect: command.effect } : {},
30820
- ...command.confirm === true ? { confirm: true } : {}
30821
- };
30822
- };
30823
- var toolSpecCommand = (node, tool, providerBacked = false) => {
30824
- const invokePath = `/${node.path}/${tool.name}`;
30825
- return {
30826
- identity: mcpToolIdentity(invokePath, tool.name),
30827
- sourcePath: node.path,
30828
- toolName: tool.name,
30829
- invokePath,
30830
- ...providerBacked ? { providerBacked: true } : {},
30831
- description: tool.description ?? node.description,
30832
- ...tool.inputSchema !== void 0 ? { inputSchema: tool.inputSchema } : {},
30833
- ...tool.delivery !== void 0 ? { delivery: tool.delivery } : {},
30834
- ...tool.effect !== void 0 ? { effect: tool.effect } : {},
30835
- ...tool.confirm === true ? { confirm: true } : {}
30836
- };
30837
- };
30838
- function mcpBridgeFor(c, env, app) {
30839
- const { builtinsOf, deps, globalSearchCapabilities, searchSync } = env;
30755
+ // ../app/src/mcpInvoke.ts
30756
+ function mcpPath(path, allowRoot = false) {
30757
+ const error = validatePath(path, { allowRoot });
30758
+ if (error !== null) throw error;
30759
+ return canonicalizePath(path).split("/").map(encodeURIComponent).join("/");
30760
+ }
30761
+ function mcpRequest(c, app, path, body, extraHeaders) {
30762
+ return Promise.resolve(app.request(new Request(new URL(path, c.req.url), {
30763
+ method: body === void 0 ? "GET" : "POST",
30764
+ headers: {
30765
+ "authorization": c.req.header("authorization") ?? "",
30766
+ "accept": "application/json",
30767
+ "content-type": "application/json",
30768
+ ...extraHeaders
30769
+ },
30770
+ ...body === void 0 ? {} : { body: JSON.stringify(body) },
30771
+ signal: c.req.raw.signal
30772
+ })));
30773
+ }
30774
+ async function mcpResponse(response) {
30775
+ const text = await response.text();
30776
+ let content = text;
30777
+ try {
30778
+ content = JSON.parse(text);
30779
+ } catch {
30780
+ }
30781
+ return { content, ...response.ok ? {} : { isError: true } };
30782
+ }
30783
+ async function invokeMcpTarget(c, env, app, input) {
30784
+ const encoded = mcpPath(input.path);
30785
+ const path = canonicalizePath(input.path);
30786
+ const args = { ...input.args };
30787
+ if (input.delivery !== void 0 && Object.hasOwn(args, "~delivery")) {
30788
+ throw new TBError("invalid_argument", "delivery and args.~delivery cannot both be provided");
30789
+ }
30790
+ const delivery = input.delivery ?? args["~delivery"];
30791
+ delete args["~delivery"];
30792
+ if (Object.keys(args).some((key) => key.startsWith("~"))) {
30793
+ throw new TBError("invalid_argument", "unknown invocation control in args");
30794
+ }
30795
+ if (delivery !== void 0 && delivery !== "realtime" && delivery !== "mailbox" && delivery !== "fallback") {
30796
+ throw new TBError("invalid_argument", "delivery must be realtime, mailbox or fallback");
30797
+ }
30798
+ if ((input.ttlSeconds !== void 0 || input.idempotencyKey !== void 0) && input.delivery !== "mailbox" && input.delivery !== "fallback") {
30799
+ throw new TBError("invalid_argument", "ttlSeconds and idempotencyKey require mailbox or fallback delivery");
30800
+ }
30801
+ const { deps, searchSync } = env;
30840
30802
  const ctx = c.get("ctx");
30841
30803
  const registry = new NodeRegistryStore(c.get("store"));
30842
- let remoteRequests = 0;
30843
- const controlTools = () => [
30844
- ...globalSearchCapabilities().includes("search") ? [{
30845
- identity: JSON.stringify(["control", "search"]),
30846
- sourcePath: "",
30847
- toolName: "Search",
30848
- invokePath: "/~search",
30849
- mcpName: "tb_search",
30850
- operation: "search",
30851
- description: "Search visible tools across the Tool Bridge tree (compact by default).",
30852
- effect: "read",
30853
- inputSchema: {
30854
- type: "object",
30855
- additionalProperties: false,
30856
- properties: {
30857
- query: { type: "string", minLength: 1 },
30858
- mode: { type: "string", enum: ["keyword"] },
30859
- limit: { type: "integer", minimum: 1, maximum: 200 },
30860
- cursor: { type: "string", minLength: 1 },
30861
- detail: { type: "string", enum: ["compact", "full"], default: "compact" },
30862
- effects: {
30863
- type: "array",
30864
- minItems: 1,
30865
- items: {
30866
- type: "string",
30867
- enum: ["read", "write", "destructive", "unknown"]
30868
- }
30869
- },
30870
- federation: { type: "string", enum: ["local", "recursive"] },
30871
- matching: { type: "string", enum: ["best", "all"] },
30872
- minCoverage: { type: "number", exclusiveMinimum: 0, maximum: 1 },
30873
- pathPrefix: { type: "string", minLength: 1 }
30874
- },
30875
- required: ["query"]
30876
- }
30877
- }] : [],
30878
- {
30879
- identity: JSON.stringify(["control", "help"]),
30880
- sourcePath: "",
30881
- toolName: "Help",
30882
- invokePath: "/~help",
30883
- mcpName: "tb_help",
30884
- operation: "help",
30885
- description: "Describe a visible Tool Bridge node or one of its tools.",
30886
- effect: "read",
30887
- inputSchema: {
30888
- type: "object",
30889
- additionalProperties: false,
30890
- properties: {
30891
- path: { type: "string" },
30892
- tool: { type: "string", minLength: 1, pattern: "^[^/]+$" },
30893
- format: { type: "string", enum: ["json", "markdown", "dsl"] }
30894
- }
30895
- }
30896
- },
30897
- {
30898
- identity: JSON.stringify(["control", "list-nodes"]),
30899
- sourcePath: "",
30900
- toolName: "List",
30901
- invokePath: "/~tree",
30902
- mcpName: "tb_list_nodes",
30903
- operation: "listNodes",
30904
- description: "List the visible Tool Bridge node tree from a path.",
30905
- effect: "read",
30906
- inputSchema: {
30907
- type: "object",
30908
- additionalProperties: false,
30909
- properties: {
30910
- path: { type: "string" },
30911
- depth: { type: "integer", minimum: 0, maximum: MAX_TREE_DEPTH }
30912
- }
30913
- }
30914
- }
30915
- ];
30916
- const takeRemoteRequest = () => {
30917
- remoteRequests += 1;
30918
- if (remoteRequests > MCP_REMOTE_MAX_REQUESTS) {
30919
- throw new TBError("unavailable", "remote MCP discovery request budget exceeded", {
30920
- retryable: false
30921
- });
30922
- }
30923
- };
30924
- const remoteHelp = async (path) => {
30925
- takeRemoteRequest();
30926
- const projector = await remotePathProjectorIfMatch(registry, path);
30927
- if (projector === null) {
30928
- throw remoteProtocolError(`remote ~help path '${path}' lost its mount owner`);
30929
- }
30930
- const headers = new Headers(c.req.raw.headers);
30931
- headers.set("accept", "application/json");
30932
- const response = await remotePassthroughIfMatch(
30933
- c,
30934
- ctx,
30935
- registry,
30936
- path,
30937
- "~help",
30938
- deps,
30939
- headers
30940
- );
30941
- if (response === null || !response.ok) {
30942
- throw new TBError("unavailable", `remote ~help failed for '${path}'`, { retryable: true });
30943
- }
30944
- const parsed = helpJsonSchema.safeParse(await response.json().catch(() => null));
30945
- if (!parsed.success) {
30946
- throw new TBError("unavailable", `remote ~help returned invalid JSON for '${path}'`);
30804
+ const resolved = await registry.resolve(path).catch(() => null);
30805
+ if (resolved === null) throw TBError.notFound("not found");
30806
+ const { node, rest: command } = resolved;
30807
+ if (node.kind === "remote") {
30808
+ if (!check(ctx, path, "read").allow) throw TBError.notFound("not found");
30809
+ if (!check(ctx, path, "call").allow) {
30810
+ throw new TBError("permission_denied", `no scope grants 'call' on '${path}'`);
30811
+ }
30812
+ const response2 = await mcpRequest(c, app, `/${encoded}/~help`);
30813
+ if (!response2.ok) return mcpResponse(response2);
30814
+ const model = helpJsonSchema.safeParse(await response2.json().catch(() => null));
30815
+ if (!model.success) throw new TBError("unavailable", "remote command help returned invalid JSON");
30816
+ const selected = model.data.cmds.find((cmd) => canonicalizePath(cmd.path) === path);
30817
+ if (selected === void 0) throw TBError.notFound("not found");
30818
+ validateMcpArguments(selected.inputSchema, args, path);
30819
+ } else {
30820
+ if (command === "" || command.includes("/") || !check(ctx, node.path, "read").allow) {
30821
+ throw TBError.notFound("not found");
30947
30822
  }
30948
- return projector.projectHelp(parsed.data, path, ctx);
30949
- };
30950
- const remotePaths = async (root) => {
30951
- const found = [];
30952
- const seen = /* @__PURE__ */ new Set();
30953
- const pending = [{ depth: 0, path: root }];
30954
- while (pending.length > 0) {
30955
- const current = pending.shift();
30956
- if (current === void 0 || seen.has(current.path)) continue;
30957
- const { depth, path } = current;
30958
- seen.add(path);
30959
- if (!check(ctx, path, "read").allow || !check(ctx, path, "call").allow) continue;
30960
- const owner = await registry.resolve(path).catch(() => null);
30961
- if (owner?.node.path !== root || owner.node.kind !== "remote") continue;
30962
- if (found.length >= DEFAULT_MAX_NODES) {
30963
- throw new TBError("unavailable", "remote MCP discovery node budget exceeded", {
30964
- retryable: false
30965
- });
30966
- }
30967
- found.push(path);
30968
- takeRemoteRequest();
30969
- const children = await remoteTreeChildren(c, ctx, registry, path, deps);
30970
- if (children.length > 0 && depth >= MAX_TREE_DEPTH) {
30971
- throw new TBError("unavailable", "remote MCP discovery depth exceeded", {
30972
- retryable: false
30973
- });
30823
+ const marker = deviceToolMarker(node);
30824
+ if (marker === null && (node.kind === "mcp" || node.kind === "http" || node.kind === "tool")) {
30825
+ if (!check(ctx, node.path, "call").allow) {
30826
+ throw new TBError("permission_denied", `no scope grants 'call' on '${node.path}'`);
30974
30827
  }
30975
- for (const child of children) {
30976
- if (child.path.startsWith(`${root}/`) && !seen.has(child.path)) {
30977
- pending.push({ path: child.path, depth: depth + 1 });
30978
- }
30828
+ if (delivery !== void 0) {
30829
+ throw new TBError("invalid_argument", "delivery is only supported by device-backed tool commands");
30979
30830
  }
30831
+ const provider = await providerFor(node, ctx, deps);
30832
+ const raw = await upstreamTools(node, provider, deps, false, (/* @__PURE__ */ new Date()).toISOString(), searchSync);
30833
+ const selected = virtualizeTools(node.virtualize, raw).exposed.find((tool) => tool.name === command);
30834
+ if (selected === void 0) throw TBError.notFound("not found");
30835
+ validateMcpArguments(selected.inputSchema, args, path);
30836
+ return provider.call(resolveUpstreamTool(node.virtualize, raw, command), args);
30980
30837
  }
30981
- return found;
30982
- };
30983
- const list = async () => {
30984
- const now = (/* @__PURE__ */ new Date()).toISOString();
30985
- const nodes = await pruneExpiredContext(await registry.subtree(""), registry);
30986
- const result = controlTools();
30987
- for (const node of nodes) {
30988
- if (node.kind === "directory") continue;
30989
- if (!check(ctx, node.path, "read").allow) continue;
30990
- if (node.kind === "remote") {
30991
- for (const path of await remotePaths(node.path)) {
30992
- const model2 = await remoteHelp(path);
30993
- for (const command of model2.cmds) {
30994
- if (check(ctx, path, command.scope).allow) {
30995
- let detailed = command;
30996
- if (command.inputSchema === void 0 && command.path !== `/${model2.node.path}`) {
30997
- const detail = await remoteHelp(command.path.replace(/^\/+/, ""));
30998
- detailed = detail.cmds.find((item) => item.name === command.name) ?? command;
30999
- }
31000
- result.push(mcpCommand(path, model2.node.description, detailed));
31001
- }
31002
- }
31003
- }
31004
- continue;
30838
+ if (marker !== null && marker.cmds === void 0) {
30839
+ if (!check(ctx, node.path, "call").allow) {
30840
+ throw new TBError("permission_denied", `no scope grants 'call' on '${node.path}'`);
31005
30841
  }
31006
- const marker = deviceToolMarker(node);
31007
- if (marker !== null) {
31008
- if (check(ctx, node.path, "call").allow) {
31009
- result.push(...(marker.cmds ?? []).map((tool) => toolSpecCommand(node, tool)));
31010
- }
31011
- continue;
31012
- }
31013
- if ((node.kind === "mcp" || node.kind === "http" || node.kind === "tool") && node.config !== void 0) {
31014
- if (!check(ctx, node.path, "call").allow) continue;
31015
- const provider = await providerFor(node, ctx, deps);
31016
- const raw = await upstreamTools(node, provider, deps, false, now, searchSync);
31017
- const { exposed } = virtualizeTools(node.virtualize, raw);
31018
- result.push(...exposed.map((tool) => toolSpecCommand(node, tool, true)));
31019
- continue;
31020
- }
31021
- const model = await helpModelFor(node, registry, ctx, builtinsOf(c.get("store")), deps, {
30842
+ } else {
30843
+ const model = await helpModelFor(node, registry, ctx, env.builtinsOf(c.get("store")), deps, {
31022
30844
  includeDirectUpload: check(ctx, node.path, "write").allow,
30845
+ now: (/* @__PURE__ */ new Date()).toISOString(),
31023
30846
  refresh: false,
31024
- now,
30847
+ schemas: true,
31025
30848
  searchSync
31026
30849
  });
31027
- for (const command of model.cmds) {
31028
- if (check(ctx, node.path, command.scope).allow) {
31029
- result.push(mcpCommand(node.path, node.description, command));
31030
- }
30850
+ const selected = model.cmds.find((cmd) => canonicalizePath(cmd.path) === path);
30851
+ if (selected === void 0) throw TBError.notFound("not found");
30852
+ if (!check(ctx, node.path, selected.scope).allow) {
30853
+ throw new TBError("permission_denied", `no scope grants '${selected.scope}' on '${node.path}'`);
31031
30854
  }
30855
+ validateMcpArguments(selected.inputSchema, args, path);
31032
30856
  }
31033
- return result;
31034
- };
30857
+ }
30858
+ const query = input.ttlSeconds === void 0 ? "" : `?ttlSeconds=${input.ttlSeconds}`;
30859
+ const response = await mcpRequest(c, app, `/${encoded}${query}`, {
30860
+ ...args,
30861
+ ...delivery === void 0 ? {} : { "~delivery": delivery }
30862
+ }, input.idempotencyKey === void 0 ? void 0 : { "x-tb-idempotency-key": input.idempotencyKey });
30863
+ const result = await mcpResponse(response);
30864
+ if (delivery !== void 0 && response.ok) {
30865
+ return {
30866
+ content: response.status === 202 ? { delivery: "mailbox", operation: result.content } : { delivery: "realtime", result: result.content }
30867
+ };
30868
+ }
30869
+ return result;
30870
+ }
30871
+
30872
+ // ../app/src/routes/mcp.ts
30873
+ var callInput = z5.strictObject({
30874
+ path: z5.string().min(1).describe("Complete command path returned by tb_help or tb_search; the same path as tb call."),
30875
+ args: z5.record(z5.string(), z5.unknown()).optional().describe("The command arguments object. Defaults to {}."),
30876
+ delivery: z5.enum(["realtime", "mailbox", "fallback"]).optional(),
30877
+ ttlSeconds: z5.number().int().positive().optional(),
30878
+ idempotencyKey: z5.string().min(1).optional()
30879
+ });
30880
+ var operationIdentity = {
30881
+ deviceId: z5.string().min(1),
30882
+ operationId: z5.string().min(1)
30883
+ };
30884
+ var deviceOperationInput = z5.discriminatedUnion("action", [
30885
+ z5.strictObject({
30886
+ action: z5.literal("list"),
30887
+ deviceId: z5.string().min(1),
30888
+ cursor: z5.string().min(1).optional(),
30889
+ limit: z5.number().int().positive().optional(),
30890
+ states: z5.array(deviceOperationStateSchema).min(1).optional()
30891
+ }),
30892
+ z5.strictObject({ action: z5.literal("get"), ...operationIdentity }),
30893
+ z5.strictObject({ action: z5.literal("cancel"), ...operationIdentity })
30894
+ ]);
30895
+ function mcpBridgeFor(c, env, app) {
30896
+ const operations = new OperationRegistry();
30897
+ const request = async (path, body) => mcpResponse(await mcpRequest(c, app, path, body));
30898
+ if (env.globalSearchCapabilities().includes("search")) {
30899
+ operations.register("tb_search", {
30900
+ description: 'Search visible Tool Bridge tools, compact by default. Use tb_help on a selected command path for its full schema, then tb_call. Use detail="full" to include schemas in this response.',
30901
+ effect: "read",
30902
+ inputSchema: toolSearchOptionsSchema.extend({ query: z5.string().trim().min(1) })
30903
+ }, async ({ query, ...opts }) => request("/~search", { query, opts }));
30904
+ }
30905
+ operations.register("tb_help", {
30906
+ description: "Describe a visible node or complete command path, including input schema, permissions, effects and device delivery. Root help browses available nodes. Equivalent to tb help.",
30907
+ effect: "read",
30908
+ inputSchema: z5.strictObject({
30909
+ path: z5.string().optional(),
30910
+ tool: z5.string().min(1).regex(/^[^/]+$/).optional(),
30911
+ format: z5.enum(["json", "markdown", "dsl"]).optional(),
30912
+ schemas: z5.boolean().optional()
30913
+ })
30914
+ }, async ({ path = "", tool, format = "json", schemas: schemas2 }) => {
30915
+ const prefix = mcpPath(path, true);
30916
+ if (tool !== void 0 && prefix === "") {
30917
+ throw new TBError("invalid_argument", "tool detail requires a node path");
30918
+ }
30919
+ const command = tool === void 0 ? prefix : `${prefix}/${mcpPath(tool)}`;
30920
+ const response = await mcpRequest(c, app, `/${command ? `${command}/` : ""}~help${schemas2 ? "?schemas=1" : ""}`, void 0, {
30921
+ accept: format === "json" ? "application/json" : format === "dsl" ? "text/plain" : "text/markdown"
30922
+ });
30923
+ return mcpResponse(response);
30924
+ });
30925
+ operations.register("tb_list_nodes", {
30926
+ description: "Browse visible Tool Bridge nodes at a bounded depth. Use tb_help to inspect one node or command. Equivalent to tb ls / tb tree; works without search.",
30927
+ effect: "read",
30928
+ inputSchema: z5.strictObject({
30929
+ path: z5.string().optional(),
30930
+ depth: z5.number().int().min(0).max(MAX_TREE_DEPTH).optional()
30931
+ })
30932
+ }, async ({ path = "", depth = 1 }) => {
30933
+ const prefix = mcpPath(path, true);
30934
+ return request(`/${prefix ? `${prefix}/` : ""}~tree?depth=${depth}`);
30935
+ });
30936
+ operations.register("tb_call", {
30937
+ description: "Invoke one Tool Bridge command by its complete path and arguments, equivalent to tb call. Discover unknown commands with tb_search / tb_help; check the target effect and confirm before writes. Supports tools, Context, Skill and system commands. Device delivery may return a mailbox operation; inspect it with tb_device_operations. Never retry a write merely because the result is unknown.",
30938
+ effect: "destructive",
30939
+ confirm: true,
30940
+ inputSchema: callInput
30941
+ }, async (input) => invokeMcpTarget(c, env, app, input));
30942
+ operations.register("tb_device_operations", {
30943
+ description: "List, inspect or cancel device mailbox operations, equivalent to tb device op. Use the original deviceId and operationId. Cancellation of a claimed operation is cooperative; result_unknown and expired with executionMayHaveOccurred do not establish that a retry is safe.",
30944
+ effect: "destructive",
30945
+ confirm: true,
30946
+ inputSchema: deviceOperationInput
30947
+ }, async (input) => {
30948
+ const { action, deviceId } = input;
30949
+ const body = action === "list" ? { deviceId, opts: { cursor: input.cursor, limit: input.limit, states: input.states } } : { deviceId, operationId: input.operationId };
30950
+ return request(`/~device/operations/${action}`, body);
30951
+ });
31035
30952
  return {
31036
- list,
31037
- call: async (tool, args) => {
31038
- const resultFromResponse = async (response2) => {
31039
- const text = await response2.text();
31040
- let value = text;
31041
- try {
31042
- value = JSON.parse(text);
31043
- } catch {
31044
- }
31045
- return { content: value, ...response2.ok ? {} : { isError: true } };
31046
- };
31047
- if (tool.operation !== void 0) {
31048
- const rawPath = args.path ?? "";
31049
- if (typeof rawPath !== "string") {
31050
- throw new TBError("invalid_argument", "path must be a string");
31051
- }
31052
- const path = rawPath.replace(/^\/+|\/+$/g, "");
31053
- const pathError = validatePath(path, { allowRoot: true });
31054
- if (pathError !== null) throw pathError;
31055
- const segments = path === "" ? [] : path.split("/");
31056
- if (segments.some((segment) => segment === "." || segment === "..")) {
31057
- throw new TBError("invalid_argument", "path contains a dot segment");
31058
- }
31059
- const encoded = segments.map((segment) => encodeURIComponent(segment));
31060
- const headers2 = new Headers({
31061
- authorization: c.req.header("authorization") ?? ""
31062
- });
31063
- if (tool.operation === "search") {
31064
- headers2.set("accept", "application/json");
31065
- headers2.set("content-type", "application/json");
31066
- const opts = {
31067
- detail: args.detail ?? "compact",
31068
- ...Object.fromEntries(
31069
- [
31070
- "mode",
31071
- "limit",
31072
- "cursor",
31073
- "effects",
31074
- "federation",
31075
- "matching",
31076
- "minCoverage",
31077
- "pathPrefix"
31078
- ].filter((key) => args[key] !== void 0).map((key) => [key, args[key]])
31079
- )
31080
- };
31081
- const response2 = await app.request(new Request(new URL("/~search", c.req.url), {
31082
- method: "POST",
31083
- headers: headers2,
31084
- body: JSON.stringify({
31085
- query: args.query,
31086
- opts
31087
- })
31088
- }));
31089
- return await resultFromResponse(response2);
31090
- }
31091
- if (tool.operation === "help") {
31092
- const detail = args.tool;
31093
- if (detail !== void 0) {
31094
- if (path === "" || typeof detail !== "string" || detail.includes("/")) {
31095
- throw new TBError("invalid_argument", "tool detail requires a node path and one segment");
31096
- }
31097
- encoded.push(encodeURIComponent(detail));
31098
- }
31099
- const format = args.format ?? "json";
31100
- headers2.set("accept", format === "json" ? "application/json" : format === "dsl" ? "text/plain" : "text/markdown");
31101
- const prefix2 = encoded.length === 0 ? "" : `/${encoded.join("/")}`;
31102
- return await resultFromResponse(await app.request(new Request(
31103
- new URL(`${prefix2}/~help`, c.req.url),
31104
- { headers: headers2 }
31105
- )));
31106
- }
31107
- headers2.set("accept", "application/json");
31108
- const prefix = encoded.length === 0 ? "" : `/${encoded.join("/")}`;
31109
- const url5 = new URL(`${prefix}/~tree`, c.req.url);
31110
- if (args.depth !== void 0) url5.searchParams.set("depth", String(args.depth));
31111
- return await resultFromResponse(await app.request(new Request(url5, { headers: headers2 })));
31112
- }
31113
- if (tool.providerBacked === true) {
31114
- let node;
31115
- try {
31116
- node = await registry.get(tool.sourcePath);
31117
- } catch {
31118
- throw TBError.notFound("not found");
31119
- }
31120
- if (!check(ctx, node.path, "read").allow) throw TBError.notFound("not found");
31121
- if (!check(ctx, node.path, "call").allow) {
31122
- throw new TBError("permission_denied", `no scope grants 'call' on '${node.path}'`);
31123
- }
31124
- if (node.kind !== "mcp" && node.kind !== "http" && node.kind !== "tool" || node.config === void 0) {
31125
- throw TBError.notFound("not found");
31126
- }
31127
- const provider = await providerFor(node, ctx, deps);
31128
- const raw = await upstreamTools(node, provider, deps, false, (/* @__PURE__ */ new Date()).toISOString(), searchSync);
31129
- const upstreamName = resolveUpstreamTool(node.virtualize, raw, tool.toolName);
31130
- const result2 = await provider.call(upstreamName, args);
31131
- return {
31132
- content: result2.contentBlocks ?? result2.content,
31133
- ...result2.isError === true ? { isError: true } : {},
31134
- ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {}
31135
- };
31136
- }
31137
- const url4 = new URL(tool.invokePath, c.req.url);
31138
- const headers = new Headers({
31139
- "accept": "application/json",
31140
- "authorization": c.req.header("authorization") ?? "",
31141
- "content-type": "application/json"
31142
- });
31143
- const response = await app.request(
31144
- new Request(url4, { method: "POST", headers, body: JSON.stringify(args) })
31145
- );
31146
- const result = await resultFromResponse(response);
31147
- if (args["~delivery"] !== void 0 && response.ok) {
31148
- return {
31149
- content: response.status === 202 ? { delivery: "mailbox", operation: result.content } : { delivery: "realtime", result: result.content }
31150
- };
31151
- }
31152
- return result;
31153
- }
30953
+ list: async () => operations.list(),
30954
+ call: async (tool, args) => operations.call(tool.name, args, void 0)
31154
30955
  };
31155
30956
  }
31156
30957
  function registerMcpRoute(app, env) {
31157
- app.all("/~mcp", (c) => runHandler(async () => await handleMcpRequest(
30958
+ app.all("/~mcp", (c) => runHandler(async () => handleMcpRequest(
31158
30959
  c.req.raw,
31159
30960
  env.deps.version,
31160
30961
  mcpBridgeFor(c, env, app),
@@ -35371,104 +35172,104 @@ function createTbServer(config) {
35371
35172
 
35372
35173
  // ../plugins/src/registry.generated.ts
35373
35174
  var BUILTIN_PLUGIN_LOADERS = {
35374
- airtable: () => import("./airtable-ML2GIGCH.js"),
35375
- alt_text_generator_ai: () => import("./alt_text_generator_ai-FGYQAF2A.js"),
35376
- amap: () => import("./amap-G7V65NGK.js"),
35377
- anthropic: () => import("./anthropic-6ZK24TBB.js"),
35378
- apify: () => import("./apify-VDGYHP6W.js"),
35379
- brave_search: () => import("./brave_search-TAATLZOS.js"),
35380
- circleci: () => import("./circleci-BQ2UWXL2.js"),
35381
- clerk: () => import("./clerk-2JYWQNOI.js"),
35382
- cohere: () => import("./cohere-6MI7AQM4.js"),
35383
- confluence: () => import("./confluence-6Y6GDETF.js"),
35384
- convertapi: () => import("./convertapi-HBZCSWOX.js"),
35385
- currencyapi: () => import("./currencyapi-337U5TGI.js"),
35386
- deepseek: () => import("./deepseek-OSPASO7O.js"),
35387
- docker_hub: () => import("./docker_hub-3ATQOY5L.js"),
35388
- dropbox: () => import("./dropbox-LSA4GM2Y.js"),
35389
- dub: () => import("./dub-VZPJSPI6.js"),
35390
- e2b: () => import("./e2b-6SDYQZL2.js"),
35391
- exa: () => import("./exa-B6EUYDVP.js"),
35392
- fal_ai: () => import("./fal_ai-K4Q27IME.js"),
35393
- feishu: () => import("./feishu-4TKXG4SU.js"),
35394
- feishu_custom_bot: () => import("./feishu_custom_bot-7CTEAKRB.js"),
35395
- figma: () => import("./figma-OUKSZLD5.js"),
35396
- firehydrant: () => import("./firehydrant-7MOYL7QH.js"),
35397
- fixer: () => import("./fixer-H2BV2NO4.js"),
35398
- fly: () => import("./fly-PXKY6PNH.js"),
35399
- front: () => import("./front-UZYIV5DW.js"),
35400
- geocodio: () => import("./geocodio-NSYO7TFP.js"),
35401
- ghost: () => import("./ghost-QBKKQQMF.js"),
35402
- github: () => import("./github-ENLSJ4CL.js"),
35403
- gmail: () => import("./gmail-KQSDEB47.js"),
35404
- googlecalendar: () => import("./googlecalendar-5YCX4SZF.js"),
35405
- googledocs: () => import("./googledocs-SSWIVFKA.js"),
35406
- grafana: () => import("./grafana-TF2LXDQA.js"),
35407
- graphhopper: () => import("./graphhopper-CURBVHYK.js"),
35408
- ip2proxy: () => import("./ip2proxy-YNVMTZEM.js"),
35409
- ipgeolocation_io: () => import("./ipgeolocation_io-NAU7VJ4J.js"),
35410
- ipqualityscore: () => import("./ipqualityscore-OT63L4TI.js"),
35411
- jina_ai: () => import("./jina_ai-6WBMBGWM.js"),
35412
- jira: () => import("./jira-DVDVTHNO.js"),
35413
- langsmith: () => import("./langsmith-IM5N4YAU.js"),
35414
- lemlist: () => import("./lemlist-277SHUQF.js"),
35415
- linear: () => import("./linear-AEPH7B5V.js"),
35416
- logsnag: () => import("./logsnag-VEWRAGBG.js"),
35417
- mattermost: () => import("./mattermost-Q3A2BIZA.js"),
35418
- mem0: () => import("./mem0-AXS7GZGI.js"),
35419
- memos: () => import("./memos-7TXG4ZTU.js"),
35420
- metabase: () => import("./metabase-NSVACWQM.js"),
35421
- mistral_ai: () => import("./mistral_ai-YZ52RGD4.js"),
35422
- mother_duck: () => import("./mother_duck-XLLL6FVL.js"),
35423
- ngrok: () => import("./ngrok-4O4W3WZW.js"),
35424
- notes: () => import("./notes-YTB3TJOO.js"),
35425
- notion: () => import("./notion-4W4O7J7V.js"),
35426
- open_exchange_rates: () => import("./open_exchange_rates-23QJXVO5.js"),
35427
- openai: () => import("./openai-ZS7GLSLH.js"),
35428
- opengraph_io: () => import("./opengraph_io-PMVH7OYI.js"),
35429
- openrouter: () => import("./openrouter-ONRGFNB6.js"),
35430
- outline: () => import("./outline-UCUYZZ7J.js"),
35431
- paddle: () => import("./paddle-35HTKTUL.js"),
35432
- perplexity: () => import("./perplexity-NVOBYN24.js"),
35433
- pinecone: () => import("./pinecone-55CJY73C.js"),
35434
- pipedrive: () => import("./pipedrive-WMCAKS72.js"),
35435
- polar: () => import("./polar-SM4ZM53B.js"),
35436
- posthog: () => import("./posthog-NWCXVXNJ.js"),
35437
- postmark: () => import("./postmark-QJV5PHUQ.js"),
35438
- prerender: () => import("./prerender-2W7UT4SC.js"),
35439
- pubmed: () => import("./pubmed-VVV7XYLU.js"),
35440
- railway: () => import("./railway-KGI57S3O.js"),
35441
- readwise: () => import("./readwise-AGHTQB42.js"),
35442
- render: () => import("./render-O4HBROCK.js"),
35443
- replicate: () => import("./replicate-KWBOMX7D.js"),
35444
- resend: () => import("./resend-DRH6Y6I4.js"),
35445
- rootly: () => import("./rootly-XGCUDW6X.js"),
35446
- scrapfly: () => import("./scrapfly-OIQ3OY6L.js"),
35447
- scrapingbee: () => import("./scrapingbee-OCZO4FHX.js"),
35448
- screenshot_fyi: () => import("./screenshot_fyi-CUNGFZOC.js"),
35449
- semantic_scholar: () => import("./semantic_scholar-3UJRLKES.js"),
35450
- sentry: () => import("./sentry-5BWVZFJV.js"),
35451
- shodan: () => import("./shodan-HI37CTEW.js"),
35452
- shopify: () => import("./shopify-LJ2EV2HH.js"),
35453
- stripe: () => import("./stripe-3LHMV55K.js"),
35454
- supabase: () => import("./supabase-H2KPX3Q2.js"),
35455
- tavily: () => import("./tavily-FBEWGH3Z.js"),
35456
- telegram: () => import("./telegram-MYYO3JND.js"),
35457
- telnyx: () => import("./telnyx-H4ALEGAX.js"),
35458
- todoist: () => import("./todoist-OBWICA5Q.js"),
35459
- trello: () => import("./trello-GUFUJRWO.js"),
35460
- turso: () => import("./turso-NIAK4UMS.js"),
35461
- twilio: () => import("./twilio-K6SLK2CX.js"),
35462
- umami: () => import("./umami-42RS2EPS.js"),
35463
- unsplash: () => import("./unsplash-V7TOTETT.js"),
35464
- upstash_redis: () => import("./upstash_redis-AWQMB6Y3.js"),
35465
- uptimerobot: () => import("./uptimerobot-AZC2DHOT.js"),
35466
- v2ex: () => import("./v2ex-4TVRNMLS.js"),
35467
- vercel: () => import("./vercel-DPHTJ3XR.js"),
35468
- wolfram_alpha_api: () => import("./wolfram_alpha_api-XAG7IXBW.js"),
35469
- wordpress: () => import("./wordpress-KMPKVGUX.js"),
35470
- workos: () => import("./workos-PKB2YR7S.js"),
35471
- zhihu: () => import("./zhihu-Y4H4XYPB.js")
35175
+ airtable: () => import("./airtable-VMQVXLHK.js"),
35176
+ alt_text_generator_ai: () => import("./alt_text_generator_ai-FTWY4RU4.js"),
35177
+ amap: () => import("./amap-OK5VBHYC.js"),
35178
+ anthropic: () => import("./anthropic-S67AZFZ3.js"),
35179
+ apify: () => import("./apify-ZE6MYQI3.js"),
35180
+ brave_search: () => import("./brave_search-TG2M2KOD.js"),
35181
+ circleci: () => import("./circleci-3L3WJVSK.js"),
35182
+ clerk: () => import("./clerk-EZPF2V7T.js"),
35183
+ cohere: () => import("./cohere-U27DPO64.js"),
35184
+ confluence: () => import("./confluence-G2KL6ERH.js"),
35185
+ convertapi: () => import("./convertapi-L4CLJ2M3.js"),
35186
+ currencyapi: () => import("./currencyapi-YPYXG7YY.js"),
35187
+ deepseek: () => import("./deepseek-6HT6WF3M.js"),
35188
+ docker_hub: () => import("./docker_hub-LPE3PCVB.js"),
35189
+ dropbox: () => import("./dropbox-V56ABHTA.js"),
35190
+ dub: () => import("./dub-6OKQ7MHT.js"),
35191
+ e2b: () => import("./e2b-JS2U2QFU.js"),
35192
+ exa: () => import("./exa-DPWVYXSP.js"),
35193
+ fal_ai: () => import("./fal_ai-DK6Y27ZF.js"),
35194
+ feishu: () => import("./feishu-K7EGSRF3.js"),
35195
+ feishu_custom_bot: () => import("./feishu_custom_bot-2JUGFYNG.js"),
35196
+ figma: () => import("./figma-FT5I7ITD.js"),
35197
+ firehydrant: () => import("./firehydrant-EIKLKHX2.js"),
35198
+ fixer: () => import("./fixer-U5QXIAKD.js"),
35199
+ fly: () => import("./fly-T3JYFZ2M.js"),
35200
+ front: () => import("./front-D72MGVOR.js"),
35201
+ geocodio: () => import("./geocodio-BOTD5TI5.js"),
35202
+ ghost: () => import("./ghost-KYP7SZGB.js"),
35203
+ github: () => import("./github-AGMZT3VY.js"),
35204
+ gmail: () => import("./gmail-BLJNJOBG.js"),
35205
+ googlecalendar: () => import("./googlecalendar-GTHWNCF6.js"),
35206
+ googledocs: () => import("./googledocs-6EEDMEBQ.js"),
35207
+ grafana: () => import("./grafana-GMW6EKSQ.js"),
35208
+ graphhopper: () => import("./graphhopper-6OLGBDGB.js"),
35209
+ ip2proxy: () => import("./ip2proxy-4QN42E6T.js"),
35210
+ ipgeolocation_io: () => import("./ipgeolocation_io-SK4XKDA2.js"),
35211
+ ipqualityscore: () => import("./ipqualityscore-6ANBPXKT.js"),
35212
+ jina_ai: () => import("./jina_ai-IHPSVRM5.js"),
35213
+ jira: () => import("./jira-46KKM4LC.js"),
35214
+ langsmith: () => import("./langsmith-JZA345I5.js"),
35215
+ lemlist: () => import("./lemlist-43IV7DK6.js"),
35216
+ linear: () => import("./linear-5LFH7XNL.js"),
35217
+ logsnag: () => import("./logsnag-5RMYAHIL.js"),
35218
+ mattermost: () => import("./mattermost-XFL6SHGG.js"),
35219
+ mem0: () => import("./mem0-NYO4DTP6.js"),
35220
+ memos: () => import("./memos-37MPQOLV.js"),
35221
+ metabase: () => import("./metabase-KYBKZFMG.js"),
35222
+ mistral_ai: () => import("./mistral_ai-6PL3K4PM.js"),
35223
+ mother_duck: () => import("./mother_duck-2NH7W5M5.js"),
35224
+ ngrok: () => import("./ngrok-UO6CDYCA.js"),
35225
+ notes: () => import("./notes-I3P32WP4.js"),
35226
+ notion: () => import("./notion-BEAJFUOE.js"),
35227
+ open_exchange_rates: () => import("./open_exchange_rates-CMF62JB4.js"),
35228
+ openai: () => import("./openai-IHOMKVND.js"),
35229
+ opengraph_io: () => import("./opengraph_io-ZQ2FGLHX.js"),
35230
+ openrouter: () => import("./openrouter-HXLW3NR5.js"),
35231
+ outline: () => import("./outline-ELCP4NEU.js"),
35232
+ paddle: () => import("./paddle-O77LALVN.js"),
35233
+ perplexity: () => import("./perplexity-JCU654QV.js"),
35234
+ pinecone: () => import("./pinecone-W5W4LRKL.js"),
35235
+ pipedrive: () => import("./pipedrive-ITMJLLPZ.js"),
35236
+ polar: () => import("./polar-R33XU4L3.js"),
35237
+ posthog: () => import("./posthog-JVNTOLTV.js"),
35238
+ postmark: () => import("./postmark-6SBG7HWV.js"),
35239
+ prerender: () => import("./prerender-GPNKTNFC.js"),
35240
+ pubmed: () => import("./pubmed-UP6ZEEIH.js"),
35241
+ railway: () => import("./railway-KWEWXHTY.js"),
35242
+ readwise: () => import("./readwise-BLATSMC3.js"),
35243
+ render: () => import("./render-QYRVIXII.js"),
35244
+ replicate: () => import("./replicate-JYTWH4NF.js"),
35245
+ resend: () => import("./resend-L5N75SZM.js"),
35246
+ rootly: () => import("./rootly-MHCPEQ26.js"),
35247
+ scrapfly: () => import("./scrapfly-PTPGYSJM.js"),
35248
+ scrapingbee: () => import("./scrapingbee-JN6UH5PY.js"),
35249
+ screenshot_fyi: () => import("./screenshot_fyi-4QRVSV55.js"),
35250
+ semantic_scholar: () => import("./semantic_scholar-LRSWA2GE.js"),
35251
+ sentry: () => import("./sentry-2G6PF5W6.js"),
35252
+ shodan: () => import("./shodan-WKKPP5C6.js"),
35253
+ shopify: () => import("./shopify-64YDK5T2.js"),
35254
+ stripe: () => import("./stripe-QAIGGOTF.js"),
35255
+ supabase: () => import("./supabase-WHTGR6ND.js"),
35256
+ tavily: () => import("./tavily-QIXCDDWO.js"),
35257
+ telegram: () => import("./telegram-E6G7CWI6.js"),
35258
+ telnyx: () => import("./telnyx-ZDVVS7PK.js"),
35259
+ todoist: () => import("./todoist-DMYF3G7J.js"),
35260
+ trello: () => import("./trello-HY2RYIRP.js"),
35261
+ turso: () => import("./turso-HAQWMGRP.js"),
35262
+ twilio: () => import("./twilio-MLXYPHJE.js"),
35263
+ umami: () => import("./umami-DZHBLPVG.js"),
35264
+ unsplash: () => import("./unsplash-GS2OPXKE.js"),
35265
+ upstash_redis: () => import("./upstash_redis-XMXWTNAJ.js"),
35266
+ uptimerobot: () => import("./uptimerobot-TF5WRZ4G.js"),
35267
+ v2ex: () => import("./v2ex-2BVXAPDF.js"),
35268
+ vercel: () => import("./vercel-YMJGQYJE.js"),
35269
+ wolfram_alpha_api: () => import("./wolfram_alpha_api-I56SXWFZ.js"),
35270
+ wordpress: () => import("./wordpress-R7TDFZ2P.js"),
35271
+ workos: () => import("./workos-HDNHWVMS.js"),
35272
+ zhihu: () => import("./zhihu-LX6AGGDS.js")
35472
35273
  };
35473
35274
 
35474
35275
  // ../plugins/src/catalog.generated.ts