opencode-toolbox 0.9.0 → 0.10.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 (3) hide show
  1. package/README.md +27 -7
  2. package/dist/index.js +118 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -168,6 +168,9 @@ Returns a comprehensive status object:
168
168
  {
169
169
  "plugin": {
170
170
  "initialized": true,
171
+ "initState": "ready",
172
+ "initMode": "eager",
173
+ "initDurationMs": 1234,
171
174
  "configPath": "/Users/username/.config/opencode/toolbox.jsonc",
172
175
  "uptime": 123.45,
173
176
  "searches": 23,
@@ -185,8 +188,9 @@ Returns a comprehensive status object:
185
188
  "name": "time",
186
189
  "status": "connected",
187
190
  "type": "local",
188
- "toolCount": 5,
191
+ "toolCount": 2,
189
192
  "error": null,
193
+ "commandString": "uvx mcp-server-time",
190
194
  "healthy": true
191
195
  },
192
196
  {
@@ -195,6 +199,7 @@ Returns a comprehensive status object:
195
199
  "type": "local",
196
200
  "toolCount": 12,
197
201
  "error": null,
202
+ "commandString": "npx -y @anthropic/mcp-github",
198
203
  "healthy": true
199
204
  },
200
205
  {
@@ -202,16 +207,25 @@ Returns a comprehensive status object:
202
207
  "status": "error",
203
208
  "type": "remote",
204
209
  "toolCount": 0,
205
- "error": "Failed to connect: timeout",
210
+ "error": "Connection timeout after 5000ms",
211
+ "url": "https://mcp.example.com/weather",
206
212
  "healthy": false
207
213
  }
208
214
  ]
209
215
  },
210
216
  "tools": {
211
- "total": 17,
212
- "available": 17,
217
+ "total": 14,
218
+ "indexed": 14,
213
219
  "serversWithTools": 2
214
220
  },
221
+ "toolboxTools": [
222
+ "toolbox_search_bm25",
223
+ "toolbox_search_regex",
224
+ "toolbox_execute",
225
+ "toolbox_status",
226
+ "toolbox_perf",
227
+ "toolbox_test"
228
+ ],
215
229
  "health": {
216
230
  "status": "degraded",
217
231
  "message": "1 server(s) failed to connect"
@@ -226,7 +240,7 @@ Returns a comprehensive status object:
226
240
 
227
241
  ### /toolbox-status Slash Command
228
242
 
229
- The plugin automatically creates a `/toolbox-status` slash command on first launch:
243
+ The plugin automatically creates and maintains the `/toolbox-status` slash command:
230
244
 
231
245
  ```
232
246
  ~/.config/opencode/command/toolbox-status.md
@@ -234,6 +248,8 @@ The plugin automatically creates a `/toolbox-status` slash command on first laun
234
248
 
235
249
  Use it in OpenCode by typing `/toolbox-status` to get a formatted status report.
236
250
 
251
+ > **Note:** The command file auto-updates when the plugin version changes.
252
+
237
253
  ### toolbox_perf
238
254
 
239
255
  Get detailed performance metrics for the toolbox plugin:
@@ -340,8 +356,10 @@ grep "WARN" ~/.local/share/opencode/toolbox.log
340
356
  **Log format:**
341
357
  ```
342
358
  2026-01-08T12:34:56.789Z [INFO] Toolbox plugin loaded successfully {"configPath":"...","serverCount":6}
343
- 2026-01-08T12:34:57.123Z [INFO] Initialization complete: 5/6 servers connected, 42 tools indexed
344
- 2026-01-08T12:34:57.124Z [WARN] 1 server(s) failed to connect: weather
359
+ 2026-01-08T12:34:57.100Z [INFO] time - connection time: 648.12ms, indexed 2 tools in 0.07ms
360
+ 2026-01-08T12:34:57.200Z [INFO] github - connection time: 892.45ms, indexed 12 tools in 0.15ms
361
+ 2026-01-08T12:34:58.500Z [INFO] Initialization complete in 1723.45ms: 2/3 servers, 14 tools indexed
362
+ 2026-01-08T12:34:58.501Z [WARN] Server weather failed: Connection timeout after 5000ms
345
363
  2026-01-08T12:35:00.456Z [INFO] BM25 search completed: "web search" -> 3 results
346
364
  ```
347
365
 
@@ -384,6 +402,8 @@ This shows:
384
402
  4. For remote servers, verify URL is accessible
385
403
  5. Check environment variables are set correctly
386
404
 
405
+ > **Note:** Connection retries use exponential backoff (100ms → 200ms → 400ms..., max 30s) before failing.
406
+
387
407
  ### Execute fails
388
408
 
389
409
  1. Run `toolbox_status({})` to check server health
package/dist/index.js CHANGED
@@ -19267,7 +19267,7 @@ function tool(input) {
19267
19267
  }
19268
19268
  tool.schema = exports_external;
19269
19269
  // src/plugin.ts
19270
- import { appendFile, mkdir, writeFile, access } from "fs/promises";
19270
+ import { appendFile, mkdir as mkdir2, writeFile as writeFile2, readFile } from "fs/promises";
19271
19271
 
19272
19272
  // node_modules/zod/v4/classic/external.js
19273
19273
  var exports_external2 = {};
@@ -32812,10 +32812,14 @@ var RemoteServerConfigSchema = exports_external2.object({
32812
32812
  url: exports_external2.string().url().describe("Remote MCP endpoint URL"),
32813
32813
  headers: exports_external2.record(exports_external2.string(), exports_external2.string()).optional().describe("HTTP headers for authentication")
32814
32814
  });
32815
- var ServerConfigSchema = exports_external2.discriminatedUnion("type", [
32815
+ var ServerConfigSchema = exports_external2.object({
32816
+ type: exports_external2.enum(["local", "remote"], {
32817
+ error: 'Server "type" must be "local" or "remote"'
32818
+ })
32819
+ }).passthrough().pipe(exports_external2.discriminatedUnion("type", [
32816
32820
  LocalServerConfigSchema,
32817
32821
  RemoteServerConfigSchema
32818
- ]);
32822
+ ]));
32819
32823
  var ConnectionConfigSchema = exports_external2.object({
32820
32824
  connectTimeout: exports_external2.number().min(100).max(60000).default(5000),
32821
32825
  requestTimeout: exports_external2.number().min(100).max(300000).default(30000),
@@ -32828,6 +32832,7 @@ var SettingsConfigSchema = exports_external2.object({
32828
32832
  connection: ConnectionConfigSchema.optional()
32829
32833
  });
32830
32834
  var ConfigSchema = exports_external2.object({
32835
+ $schema: exports_external2.string().optional(),
32831
32836
  mcp: exports_external2.record(exports_external2.string(), ServerConfigSchema),
32832
32837
  settings: SettingsConfigSchema.optional()
32833
32838
  });
@@ -33636,6 +33641,47 @@ var ParseErrorCode;
33636
33641
  })(ParseErrorCode || (ParseErrorCode = {}));
33637
33642
 
33638
33643
  // src/config/loader.ts
33644
+ import { mkdir, writeFile } from "fs/promises";
33645
+ import { dirname } from "path";
33646
+ var NPM_PACKAGE = "opencode-toolbox";
33647
+ function getSchemaUrl(_version) {
33648
+ return `https://unpkg.com/${NPM_PACKAGE}@latest/toolbox.schema.json`;
33649
+ }
33650
+ function generateDefaultConfig(version3) {
33651
+ const schemaUrl = getSchemaUrl(version3);
33652
+ return `{
33653
+ "$schema": "${schemaUrl}",
33654
+ "mcp": {
33655
+ // Add your MCP servers here
33656
+ // Example:
33657
+ // "time": {
33658
+ // "type": "local",
33659
+ // "command": ["npx", "-y", "@anthropic/mcp-time"]
33660
+ // }
33661
+ },
33662
+ "settings": {
33663
+ "defaultLimit": 5,
33664
+ "initMode": "eager"
33665
+ }
33666
+ }
33667
+ `;
33668
+ }
33669
+ async function createDefaultConfigIfMissing(filePath, version3) {
33670
+ try {
33671
+ const file3 = Bun.file(filePath);
33672
+ const exists = await file3.exists();
33673
+ if (exists) {
33674
+ return false;
33675
+ }
33676
+ const dir = dirname(filePath);
33677
+ await mkdir(dir, { recursive: true });
33678
+ const content = generateDefaultConfig(version3);
33679
+ await writeFile(filePath, content, "utf-8");
33680
+ return true;
33681
+ } catch {
33682
+ return false;
33683
+ }
33684
+ }
33639
33685
  function interpolateEnvVars(obj) {
33640
33686
  if (typeof obj === "string") {
33641
33687
  return obj.replace(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, varName) => {
@@ -38343,7 +38389,10 @@ class MCPManager extends EventEmitter {
38343
38389
  } catch (error92) {
38344
38390
  lastError = error92 instanceof Error ? error92 : new Error(String(error92));
38345
38391
  if (attempt < maxAttempts) {
38346
- await sleep(this.connectionConfig.retryDelay);
38392
+ const baseDelay = this.connectionConfig.retryDelay;
38393
+ const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
38394
+ const delayMs = Math.min(exponentialDelay, 30000);
38395
+ await sleep(delayMs);
38347
38396
  }
38348
38397
  }
38349
38398
  }
@@ -38379,7 +38428,7 @@ class MCPManager extends EventEmitter {
38379
38428
  });
38380
38429
  this.clients.set(name, client);
38381
38430
  globalProfiler.recordServerConnect(name, connectTime, catalogTools.length, "connected");
38382
- this.emit("server:connected", name, catalogTools);
38431
+ this.emit("server:connected", name, catalogTools, connectTime);
38383
38432
  this.checkPartialReady();
38384
38433
  }
38385
38434
  checkPartialReady() {
@@ -38693,6 +38742,7 @@ function generateSignature(tool3) {
38693
38742
  return `${tool3.id.name}(${argList})`;
38694
38743
  }
38695
38744
  // src/plugin.ts
38745
+ var PACKAGE_VERSION = "0.8.0";
38696
38746
  var DEFAULT_CONFIG_PATH = `${process.env.HOME}/.config/opencode/toolbox.jsonc`;
38697
38747
  var LOG_FILE_PATH = `${process.env.HOME}/.local/share/opencode/toolbox.log`;
38698
38748
  var LOG_DIR = `${process.env.HOME}/.local/share/opencode`;
@@ -38702,6 +38752,13 @@ var COMMAND_CONTENT = `---
38702
38752
  description: Check toolbox plugin status and server health
38703
38753
  ---
38704
38754
  Run toolbox_status({}) tool and show me the results in a readable format.
38755
+
38756
+ Include:
38757
+ 1. MCP Servers table (name, type, tools, status)
38758
+ 2. Tool distribution chart
38759
+ 3. Toolbox's own tools list (from toolboxTools field)
38760
+ 4. Health status
38761
+
38705
38762
  Highlight any failed servers or issues.
38706
38763
  `;
38707
38764
  function parseToolName(fullName) {
@@ -38833,14 +38890,16 @@ function log(level, message, extra) {
38833
38890
  const extraStr = extra ? ` ${JSON.stringify(extra)}` : "";
38834
38891
  const line = `${timestamp} [${level.toUpperCase()}] ${message}${extraStr}
38835
38892
  `;
38836
- mkdir(LOG_DIR, { recursive: true }).then(() => appendFile(LOG_FILE_PATH, line)).catch(() => {});
38893
+ mkdir2(LOG_DIR, { recursive: true }).then(() => appendFile(LOG_FILE_PATH, line)).catch(() => {});
38837
38894
  }
38838
38895
  function ensureCommandFile() {
38839
38896
  if (isTestEnv)
38840
38897
  return;
38841
- access(COMMAND_FILE_PATH).catch(() => {
38842
- mkdir(COMMAND_DIR, { recursive: true }).then(() => writeFile(COMMAND_FILE_PATH, COMMAND_CONTENT)).then(() => log("info", "Created /toolbox-status command file")).catch(() => {});
38843
- });
38898
+ mkdir2(COMMAND_DIR, { recursive: true }).then(() => readFile(COMMAND_FILE_PATH, "utf-8").catch(() => "")).then((existing) => {
38899
+ if (existing !== COMMAND_CONTENT) {
38900
+ return writeFile2(COMMAND_FILE_PATH, COMMAND_CONTENT).then(() => log("info", existing ? "Updated /toolbox-status command file" : "Created /toolbox-status command file"));
38901
+ }
38902
+ }).catch(() => {});
38844
38903
  }
38845
38904
  function generateSystemPrompt(configuredServers) {
38846
38905
  const registry3 = configuredServers.length > 0 ? configuredServers.map((s) => `${s}_*`).join(`
@@ -38897,9 +38956,19 @@ var ToolboxPlugin = async (ctx) => {
38897
38956
  const pluginLoadStart = performance.now();
38898
38957
  const { client } = ctx;
38899
38958
  const configPath = process.env.OPENCODE_TOOLBOX_CONFIG || DEFAULT_CONFIG_PATH;
38959
+ if (!isTestEnv) {
38960
+ const created = await createDefaultConfigIfMissing(configPath, PACKAGE_VERSION);
38961
+ if (created) {
38962
+ log("info", `Created default config file at ${configPath}`);
38963
+ }
38964
+ }
38900
38965
  const configResult = await loadConfig(configPath);
38901
38966
  if (!configResult.success) {
38902
- const errorMsg = `Failed to load config from ${configPath}: ${configResult.error.issues.map((i) => i.message).join(", ")}`;
38967
+ const formattedErrors = configResult.error.issues.map((issue3) => {
38968
+ const path = issue3.path.length > 0 ? `at "${issue3.path.join(".")}"` : "";
38969
+ return `${issue3.message} ${path}`.trim();
38970
+ }).join("; ");
38971
+ const errorMsg = `Failed to load config from ${configPath}: ${formattedErrors}`;
38903
38972
  log("error", errorMsg);
38904
38973
  return {};
38905
38974
  }
@@ -38927,12 +38996,12 @@ var ToolboxPlugin = async (ctx) => {
38927
38996
  initMode,
38928
38997
  loadDurationMs: Math.round(pluginLoadDuration * 100) / 100
38929
38998
  });
38930
- mcpManager.on("server:connected", (serverName, tools) => {
38999
+ mcpManager.on("server:connected", (serverName, tools, connectTime) => {
38931
39000
  const startTime = performance.now();
38932
39001
  bm25Index.addToolsBatch(tools);
38933
39002
  const indexTime = performance.now() - startTime;
38934
39003
  globalProfiler.recordIncrementalUpdate(tools.length);
38935
- log("info", `Server ${serverName} connected, indexed ${tools.length} tools in ${indexTime.toFixed(2)}ms`);
39004
+ log("info", `${serverName} - connection time: ${connectTime.toFixed(2)}ms, indexed ${tools.length} tools in ${indexTime.toFixed(2)}ms`);
38936
39005
  });
38937
39006
  mcpManager.on("server:error", (serverName, error92) => {
38938
39007
  log("warn", `Server ${serverName} failed: ${error92}`);
@@ -39103,9 +39172,34 @@ var ToolboxPlugin = async (ctx) => {
39103
39172
  error: errorMsg,
39104
39173
  durationMs: duration5
39105
39174
  });
39175
+ const server = mcpManager.getServer(parsed.serverName);
39176
+ const configuredServer = config3.mcp[parsed.serverName];
39177
+ const serverInfo = server ? {
39178
+ name: server.name,
39179
+ status: server.status,
39180
+ type: server.config.type,
39181
+ error: server.error || null,
39182
+ command: server.config.type === "local" ? server.config.command || null : undefined,
39183
+ commandString: server.config.type === "local" && server.config.command ? server.config.command.join(" ") : undefined,
39184
+ url: server.config.type === "remote" ? server.config.url || null : undefined
39185
+ } : configuredServer ? {
39186
+ name: parsed.serverName,
39187
+ status: "unknown",
39188
+ type: configuredServer.type,
39189
+ error: null,
39190
+ command: configuredServer.type === "local" ? configuredServer.command || null : undefined,
39191
+ commandString: configuredServer.type === "local" && configuredServer.command ? configuredServer.command.join(" ") : undefined,
39192
+ url: configuredServer.type === "remote" ? configuredServer.url || null : undefined
39193
+ } : {
39194
+ name: parsed.serverName,
39195
+ status: "unknown",
39196
+ type: "unknown",
39197
+ error: null
39198
+ };
39106
39199
  return JSON.stringify({
39107
39200
  success: false,
39108
- error: errorMsg
39201
+ error: errorMsg,
39202
+ server: serverInfo
39109
39203
  });
39110
39204
  }
39111
39205
  }
@@ -39155,6 +39249,9 @@ var ToolboxPlugin = async (ctx) => {
39155
39249
  type: server.config.type,
39156
39250
  toolCount: server.tools.length,
39157
39251
  error: server.error || null,
39252
+ command: server.config.type === "local" ? server.config.command || null : undefined,
39253
+ commandString: server.config.type === "local" && server.config.command ? server.config.command.join(" ") : undefined,
39254
+ url: server.config.type === "remote" ? server.config.url || null : undefined,
39158
39255
  healthy: server.status === "connected"
39159
39256
  }))
39160
39257
  },
@@ -39163,6 +39260,14 @@ var ToolboxPlugin = async (ctx) => {
39163
39260
  indexed: bm25Index.size,
39164
39261
  serversWithTools: servers.filter((s) => s.tools.length > 0).length
39165
39262
  },
39263
+ toolboxTools: [
39264
+ "toolbox_search_bm25",
39265
+ "toolbox_search_regex",
39266
+ "toolbox_execute",
39267
+ "toolbox_status",
39268
+ "toolbox_perf",
39269
+ "toolbox_test"
39270
+ ],
39166
39271
  health: {
39167
39272
  status: failedServers.length === 0 && servers.length > 0 ? "healthy" : failedServers.length > 0 ? "degraded" : "unknown",
39168
39273
  message: servers.length === 0 ? "No servers configured" : failedServers.length === 0 ? "All servers connected" : `${failedServers.length} server(s) failed to connect`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-toolbox",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Tool Search Tool Plugin for OpenCode - search and execute tools from MCP servers on-demand",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",