opencode-toolbox 0.3.1 → 0.5.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 +157 -8
  2. package/dist/index.js +238 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ Create `~/.config/opencode/toolbox.jsonc`:
30
30
 
31
31
  ```jsonc
32
32
  {
33
- "servers": {
33
+ "mcp": {
34
34
  "time": {
35
35
  "type": "local",
36
36
  "command": ["npx", "-y", "@anthropic/mcp-time"]
@@ -63,7 +63,7 @@ Create `~/.config/opencode/toolbox.jsonc`:
63
63
 
64
64
  ## Usage
65
65
 
66
- The plugin exposes three tools:
66
+ The plugin exposes four tools:
67
67
 
68
68
  ### toolbox_search_bm25
69
69
 
@@ -135,6 +135,89 @@ Toolbox: { "datetime": "2026-01-07T02:15:00+09:00", "timezone": "Asia/Tokyo" }
135
135
  LLM: "The current time in Tokyo is 2:15 AM on January 7, 2026."
136
136
  ```
137
137
 
138
+ ### toolbox_status
139
+
140
+ Get toolbox status including plugin health, MCP server connections, and tool counts:
141
+
142
+ ```
143
+ toolbox_status({})
144
+ ```
145
+
146
+ Returns a comprehensive status object:
147
+
148
+ ```json
149
+ {
150
+ "plugin": {
151
+ "initialized": true,
152
+ "configPath": "/Users/username/.config/opencode/toolbox.jsonc",
153
+ "uptime": 123.45,
154
+ "searches": 23,
155
+ "executions": 15,
156
+ "successRate": "93%"
157
+ },
158
+ "servers": {
159
+ "total": 3,
160
+ "connected": 2,
161
+ "failed": 1,
162
+ "connecting": 0,
163
+ "connectionRatio": "2/3",
164
+ "details": [
165
+ {
166
+ "name": "time",
167
+ "status": "connected",
168
+ "type": "local",
169
+ "toolCount": 5,
170
+ "error": null,
171
+ "healthy": true
172
+ },
173
+ {
174
+ "name": "github",
175
+ "status": "connected",
176
+ "type": "local",
177
+ "toolCount": 12,
178
+ "error": null,
179
+ "healthy": true
180
+ },
181
+ {
182
+ "name": "weather",
183
+ "status": "error",
184
+ "type": "remote",
185
+ "toolCount": 0,
186
+ "error": "Failed to connect: timeout",
187
+ "healthy": false
188
+ }
189
+ ]
190
+ },
191
+ "tools": {
192
+ "total": 17,
193
+ "available": 17,
194
+ "serversWithTools": 2
195
+ },
196
+ "health": {
197
+ "status": "degraded",
198
+ "message": "1 server(s) failed to connect"
199
+ }
200
+ }
201
+ ```
202
+
203
+ **Health Status:**
204
+ - `healthy`: All servers connected successfully
205
+ - `degraded`: Some servers failed to connect (check `servers.failed`)
206
+ - `unknown`: No servers configured or initialization failed
207
+
208
+ ### /toolbox-status Slash Command
209
+
210
+ The plugin automatically creates and maintains a `/toolbox-status` slash command:
211
+
212
+ ```
213
+ ~/.config/opencode/command/toolbox-status.md
214
+ ```
215
+
216
+ - **Auto-created** on first plugin launch
217
+ - **Auto-updated** when plugin version changes (tracked via `toolbox_version` in frontmatter)
218
+
219
+ Use it in OpenCode by typing `/toolbox-status` to get a formatted status report.
220
+
138
221
  ## Search Modes
139
222
 
140
223
  ### BM25 (Natural Language)
@@ -172,13 +255,69 @@ bun test --coverage # Run with coverage
172
255
  bun run build
173
256
  ```
174
257
 
258
+ ## Observability
259
+
260
+ The toolbox plugin provides built-in logging and status monitoring to help you understand what's happening.
261
+
262
+ ### Logging
263
+
264
+ All plugin operations are logged **silently** to a dedicated log file (no screen output):
265
+
266
+ ```
267
+ ~/.local/share/opencode/toolbox.log
268
+ ```
269
+
270
+ Log entries include:
271
+ - Plugin initialization status
272
+ - MCP server connection status (connected/error)
273
+ - Tool search operations (BM25/regex queries + result counts)
274
+ - Tool execution results (success/failure with duration)
275
+ - Errors with details
276
+
277
+ **View logs:**
278
+ ```bash
279
+ # Watch logs in real-time
280
+ tail -f ~/.local/share/opencode/toolbox.log
281
+
282
+ # Check for errors only
283
+ grep "ERROR" ~/.local/share/opencode/toolbox.log
284
+
285
+ # Check for warnings
286
+ grep "WARN" ~/.local/share/opencode/toolbox.log
287
+ ```
288
+
289
+ **Log format:**
290
+ ```
291
+ 2026-01-08T12:34:56.789Z [INFO] Toolbox plugin loaded successfully {"configPath":"...","serverCount":6}
292
+ 2026-01-08T12:34:57.123Z [INFO] Initialization complete: 5/6 servers connected, 42 tools indexed
293
+ 2026-01-08T12:34:57.124Z [WARN] 1 server(s) failed to connect: weather
294
+ 2026-01-08T12:35:00.456Z [INFO] BM25 search completed: "web search" -> 3 results
295
+ ```
296
+
297
+ ### Status Tool
298
+
299
+ Use the `toolbox_status` command to check plugin health at any time:
300
+
301
+ ```
302
+ toolbox_status({})
303
+ ```
304
+
305
+ This shows:
306
+ - **Plugin Status**: Initialization, config path, uptime, search/execution counts
307
+ - **Server Status**: Connection ratio (e.g., "2/3"), details per server
308
+ - **Tools**: Total available tools, servers with tools
309
+ - **Health**: Overall health status (healthy/degraded/unknown)
310
+
311
+ **Connection Ratio**: Shows `success/total` for servers. If `success < total`, it indicates failed connections.
312
+
175
313
  ## Troubleshooting
176
314
 
177
315
  ### Plugin not loading
178
316
 
179
- 1. Check OpenCode logs for plugin errors
180
- 2. Verify `opencode-toolbox` is in the `plugin` array in `opencode.jsonc`
181
- 3. Ensure `toolbox.jsonc` exists and is valid JSON
317
+ 1. Run `toolbox_status({})` to check initialization status
318
+ 2. Check OpenCode logs at `~/.local/share/opencode/log/` for plugin errors
319
+ 3. Verify `opencode-toolbox` is in the `plugin` array in `opencode.jsonc`
320
+ 4. Ensure `toolbox.jsonc` exists and is valid JSON
182
321
 
183
322
  ### Search finds no tools
184
323
 
@@ -186,11 +325,21 @@ bun run build
186
325
  2. Check tool descriptions for relevant keywords
187
326
  3. Try broader search terms or regex patterns
188
327
 
328
+ ### MCP servers not connecting
329
+
330
+ 1. Run `toolbox_status({})` to see which servers failed
331
+ 2. Check logs for specific error messages from failed servers
332
+ 3. Verify server command works standalone: `npx -y @anthropic/mcp-time`
333
+ 4. For remote servers, verify URL is accessible
334
+ 5. Check environment variables are set correctly
335
+
189
336
  ### Execute fails
190
337
 
191
- 1. Verify the tool name format: `serverName_toolName`
192
- 2. Check `arguments` is valid JSON
193
- 3. Ensure underlying MCP server is running
338
+ 1. Run `toolbox_status({})` to check server health
339
+ 2. Verify tool name format: `serverName_toolName`
340
+ 3. Check `arguments` is valid JSON
341
+ 4. Ensure underlying MCP server is running and connected
342
+ 5. Check logs for detailed error messages
194
343
 
195
344
  ## License
196
345
 
package/dist/index.js CHANGED
@@ -19266,6 +19266,9 @@ function tool(input) {
19266
19266
  return input;
19267
19267
  }
19268
19268
  tool.schema = exports_external;
19269
+ // src/plugin.ts
19270
+ import { appendFile, mkdir, writeFile, readFile } from "fs/promises";
19271
+
19269
19272
  // node_modules/zod/v4/classic/external.js
19270
19273
  var exports_external2 = {};
19271
19274
  __export(exports_external2, {
@@ -32817,7 +32820,7 @@ var SettingsConfigSchema = exports_external2.object({
32817
32820
  defaultLimit: exports_external2.number().min(1).max(20).default(5)
32818
32821
  });
32819
32822
  var ConfigSchema = exports_external2.object({
32820
- servers: exports_external2.record(exports_external2.string(), ServerConfigSchema),
32823
+ mcp: exports_external2.record(exports_external2.string(), ServerConfigSchema),
32821
32824
  settings: SettingsConfigSchema.optional()
32822
32825
  });
32823
32826
  // node_modules/jsonc-parser/lib/esm/impl/scanner.js
@@ -37908,8 +37911,69 @@ function generateSignature(tool3) {
37908
37911
  }).join(", ");
37909
37912
  return `${tool3.id.name}(${argList})`;
37910
37913
  }
37914
+ // package.json
37915
+ var package_default = {
37916
+ name: "opencode-toolbox",
37917
+ version: "0.5.0",
37918
+ description: "Tool Search Tool Plugin for OpenCode - search and execute tools from MCP servers on-demand",
37919
+ main: "dist/index.js",
37920
+ module: "dist/index.js",
37921
+ type: "module",
37922
+ exports: {
37923
+ ".": {
37924
+ import: "./dist/index.js",
37925
+ types: "./dist/index.d.ts"
37926
+ }
37927
+ },
37928
+ scripts: {
37929
+ build: "bun build src/index.ts --outdir dist --target bun",
37930
+ dev: "bun --hot src/index.ts",
37931
+ test: "bun test",
37932
+ "test:coverage": "bun test --coverage",
37933
+ typecheck: "tsc --noEmit"
37934
+ },
37935
+ files: [
37936
+ "dist",
37937
+ "README.md"
37938
+ ],
37939
+ keywords: [
37940
+ "opencode",
37941
+ "plugin",
37942
+ "tool-search",
37943
+ "mcp"
37944
+ ],
37945
+ license: "MIT",
37946
+ devDependencies: {
37947
+ "@types/bun": "latest",
37948
+ typescript: "^5.9.3"
37949
+ },
37950
+ peerDependencies: {
37951
+ typescript: "^5"
37952
+ },
37953
+ dependencies: {
37954
+ "@modelcontextprotocol/sdk": "^1.25.1",
37955
+ "@opencode-ai/plugin": "latest",
37956
+ "jsonc-parser": "^3.3.1",
37957
+ zod: "^4.3.5"
37958
+ }
37959
+ };
37960
+
37911
37961
  // src/plugin.ts
37962
+ var PLUGIN_VERSION = package_default.version;
37912
37963
  var DEFAULT_CONFIG_PATH = `${process.env.HOME}/.config/opencode/toolbox.jsonc`;
37964
+ var LOG_FILE_PATH = `${process.env.HOME}/.local/share/opencode/toolbox.log`;
37965
+ var LOG_DIR = `${process.env.HOME}/.local/share/opencode`;
37966
+ var COMMAND_DIR = `${process.env.HOME}/.config/opencode/command`;
37967
+ var COMMAND_FILE_PATH = `${COMMAND_DIR}/toolbox-status.md`;
37968
+ function getCommandContent() {
37969
+ return `---
37970
+ description: Check toolbox plugin status and server health
37971
+ toolbox_version: ${PLUGIN_VERSION}
37972
+ ---
37973
+ Run toolbox_status({}) and show me the results in a readable format.
37974
+ Highlight any failed servers or issues.
37975
+ `;
37976
+ }
37913
37977
  function parseToolName(fullName) {
37914
37978
  const underscoreIndex = fullName.indexOf("_");
37915
37979
  if (underscoreIndex === -1) {
@@ -37948,14 +38012,38 @@ Returns tools with schemas. Use toolbox_execute() to run them.`;
37948
38012
  var EXECUTE_DESC = `Execute a tool discovered via toolbox_search_bm25 or toolbox_search_regex.
37949
38013
 
37950
38014
  Pass arguments as JSON string matching the tool's schema.`;
38015
+ var STATUS_DESC = `Get toolbox status including plugin initialization, MCP server connections, and tool counts.
38016
+
38017
+ Shows success/total metrics to highlight failures. Use to check if toolbox is working correctly.`;
38018
+ function log(level, message, extra) {
38019
+ const timestamp = new Date().toISOString();
38020
+ const extraStr = extra ? ` ${JSON.stringify(extra)}` : "";
38021
+ const line = `${timestamp} [${level.toUpperCase()}] ${message}${extraStr}
38022
+ `;
38023
+ mkdir(LOG_DIR, { recursive: true }).then(() => appendFile(LOG_FILE_PATH, line)).catch(() => {});
38024
+ }
38025
+ function ensureCommandFile() {
38026
+ const content = getCommandContent();
38027
+ readFile(COMMAND_FILE_PATH, "utf-8").then((existing) => {
38028
+ const versionMatch = existing.match(/toolbox_version:\s*([^\n]+)/);
38029
+ const existingVersion = versionMatch?.[1]?.trim() ?? null;
38030
+ if (existingVersion !== PLUGIN_VERSION) {
38031
+ return writeFile(COMMAND_FILE_PATH, content).then(() => {
38032
+ log("info", `Updated /toolbox-status command (${existingVersion} -> ${PLUGIN_VERSION})`);
38033
+ });
38034
+ }
38035
+ }).catch(() => {
38036
+ mkdir(COMMAND_DIR, { recursive: true }).then(() => writeFile(COMMAND_FILE_PATH, content)).then(() => log("info", `Created /toolbox-status command (v${PLUGIN_VERSION})`)).catch(() => {});
38037
+ });
38038
+ }
37951
38039
  var SYSTEM_PROMPT_BASE = `# Extended Toolbox
37952
38040
 
37953
38041
  You have access to an extended toolbox with additional capabilities (web search, time utilities, code search, etc.).
37954
38042
 
37955
- ## Rules
37956
- 1. ALWAYS toolbox_search_* before saying "I cannot do that" or "I don't have access to."
37957
- 2. ALWAYS toolbox_search_* if you think that user wants you to use some tools
37958
- 3. ALWAYS toolbox_search_* if you think that user may refer specific tool name which is not exist in the context
38043
+ ## Rule
38044
+ ALWAYS search before saying "I cannot do that" or "I don't have access to."
38045
+ DO NOT try to execute a tool without having the tool's exact tool schema. If you don't have the tool's schema
38046
+ then run toolbox_search_* to get them.
37959
38047
 
37960
38048
  ## Workflow
37961
38049
  1. Search: toolbox_search_bm25({ text: "what you need" }) or toolbox_search_regex({ pattern: "prefix_.*" })
@@ -37965,43 +38053,78 @@ function generateSystemPrompt(mcpManager) {
37965
38053
  if (servers.length === 0) {
37966
38054
  return SYSTEM_PROMPT_BASE;
37967
38055
  }
37968
- const serverLines = [];
38056
+ const toolboxSchema = {};
37969
38057
  for (const server of servers) {
37970
38058
  if (server.status === "connected" && server.tools.length > 0) {
37971
- const toolNames = server.tools.map((t) => t.id.name).join(", ");
37972
- serverLines.push(`- ${server.name}: ${toolNames}`);
38059
+ toolboxSchema[server.name] = server.tools.map((t) => t.idString);
37973
38060
  }
37974
38061
  }
37975
- if (serverLines.length === 0) {
38062
+ if (Object.keys(toolboxSchema).length === 0) {
37976
38063
  return SYSTEM_PROMPT_BASE;
37977
38064
  }
37978
38065
  return `${SYSTEM_PROMPT_BASE}
37979
38066
 
37980
- ## Registered MCP Servers
37981
- ${serverLines.join(`
37982
- `)}`;
38067
+ ## Toolbox Schema
38068
+ Tool names use \`<server>_<tool>\` format. Pass exact names to toolbox_execute().
38069
+ \`\`\`json
38070
+ ${JSON.stringify(toolboxSchema, null, 2)}
38071
+ \`\`\``;
37983
38072
  }
37984
38073
  var ToolboxPlugin = async (ctx) => {
38074
+ const { client } = ctx;
37985
38075
  const configPath = process.env.OPENCODE_TOOLBOX_CONFIG || DEFAULT_CONFIG_PATH;
37986
38076
  const configResult = await loadConfig(configPath);
37987
38077
  if (!configResult.success) {
37988
- console.error("[Toolbox] Failed to load config:", configResult.error.issues);
38078
+ const errorMsg = `Failed to load config from ${configPath}: ${configResult.error.issues.map((i) => i.message).join(", ")}`;
38079
+ log("error", errorMsg);
37989
38080
  return {};
37990
38081
  }
37991
38082
  const config3 = configResult.data;
37992
38083
  const mcpManager = new MCPManager;
37993
38084
  const bm25Index = new BM25Index;
37994
38085
  let initialized = false;
38086
+ let searchCount = 0;
38087
+ let executionCount = 0;
38088
+ let executionSuccessCount = 0;
38089
+ ensureCommandFile();
38090
+ const serverNames = Object.keys(config3.mcp);
38091
+ log("info", `Toolbox plugin loaded successfully`, {
38092
+ configPath,
38093
+ serverCount: serverNames.length,
38094
+ servers: serverNames
38095
+ });
37995
38096
  async function ensureInitialized() {
37996
38097
  if (initialized)
37997
38098
  return;
37998
38099
  try {
37999
- await mcpManager.initialize(config3.servers);
38100
+ log("info", "Initializing MCP servers...");
38101
+ await mcpManager.initialize(config3.mcp);
38000
38102
  const allTools = mcpManager.getAllCatalogTools();
38001
38103
  bm25Index.indexTools(allTools);
38002
38104
  initialized = true;
38105
+ const servers = mcpManager.getAllServers();
38106
+ const connectedServers = servers.filter((s) => s.status === "connected");
38107
+ const failedServers = servers.filter((s) => s.status === "error");
38108
+ const initMsg = `Initialization complete: ${connectedServers.length}/${servers.length} servers connected, ${allTools.length} tools indexed`;
38109
+ log("info", initMsg, {
38110
+ totalServers: servers.length,
38111
+ connectedServers: connectedServers.length,
38112
+ failedServers: failedServers.length,
38113
+ totalTools: allTools.length,
38114
+ servers: servers.map((s) => ({
38115
+ name: s.name,
38116
+ status: s.status,
38117
+ toolCount: s.tools.length,
38118
+ error: s.error || null
38119
+ }))
38120
+ });
38121
+ if (failedServers.length > 0) {
38122
+ const warnMsg = `${failedServers.length} server(s) failed to connect: ${failedServers.map((s) => s.name).join(", ")}`;
38123
+ log("warn", warnMsg);
38124
+ }
38003
38125
  } catch (error92) {
38004
- console.error("[Toolbox] Failed to initialize MCP servers:", error92);
38126
+ const errorMsg = `Failed to initialize MCP servers: ${error92 instanceof Error ? error92.message : String(error92)}`;
38127
+ log("error", errorMsg);
38005
38128
  throw error92;
38006
38129
  }
38007
38130
  }
@@ -38022,9 +38145,16 @@ var ToolboxPlugin = async (ctx) => {
38022
38145
  error: `Failed to initialize: ${error92 instanceof Error ? error92.message : String(error92)}`
38023
38146
  });
38024
38147
  }
38148
+ searchCount++;
38025
38149
  const searchLimit = args.limit || config3.settings?.defaultLimit || 5;
38026
38150
  const allTools = mcpManager.getAllCatalogTools();
38027
38151
  const results = bm25Index.search(args.text, searchLimit);
38152
+ log("info", `BM25 search completed: "${args.text}" -> ${results.length} results`, {
38153
+ searchType: "bm25",
38154
+ query: args.text,
38155
+ resultsCount: results.length,
38156
+ limit: searchLimit
38157
+ });
38028
38158
  return formatSearchResults(results, allTools);
38029
38159
  }
38030
38160
  }),
@@ -38043,15 +38173,23 @@ var ToolboxPlugin = async (ctx) => {
38043
38173
  error: `Failed to initialize: ${error92 instanceof Error ? error92.message : String(error92)}`
38044
38174
  });
38045
38175
  }
38176
+ searchCount++;
38046
38177
  const searchLimit = args.limit || config3.settings?.defaultLimit || 5;
38047
38178
  const allTools = mcpManager.getAllCatalogTools();
38048
38179
  const result = searchWithRegex(allTools, args.pattern, searchLimit);
38049
38180
  if ("error" in result) {
38181
+ log("warn", `Regex search failed: "${args.pattern}" -> ${result.error}`);
38050
38182
  return JSON.stringify({
38051
38183
  success: false,
38052
38184
  error: result.error
38053
38185
  });
38054
38186
  }
38187
+ log("info", `Regex search completed: "${args.pattern}" -> ${result.length} results`, {
38188
+ searchType: "regex",
38189
+ pattern: args.pattern,
38190
+ resultsCount: result.length,
38191
+ limit: searchLimit
38192
+ });
38055
38193
  return formatSearchResults(result, allTools);
38056
38194
  }
38057
38195
  }),
@@ -38072,6 +38210,9 @@ var ToolboxPlugin = async (ctx) => {
38072
38210
  }
38073
38211
  const parsed = parseToolName(args.name);
38074
38212
  if (!parsed) {
38213
+ log("warn", `Invalid tool name format: ${args.name}`, {
38214
+ toolName: args.name
38215
+ });
38075
38216
  return JSON.stringify({
38076
38217
  success: false,
38077
38218
  error: `Invalid tool name format: ${args.name}. Expected format: serverName_toolName (e.g., 'time_get_current_time')`
@@ -38082,25 +38223,106 @@ var ToolboxPlugin = async (ctx) => {
38082
38223
  try {
38083
38224
  toolArgs = JSON.parse(args.arguments);
38084
38225
  } catch (error92) {
38226
+ log("warn", `Failed to parse arguments as JSON for ${args.name}`, {
38227
+ toolName: args.name,
38228
+ arguments: args.arguments
38229
+ });
38085
38230
  return JSON.stringify({
38086
38231
  success: false,
38087
38232
  error: `Failed to parse arguments as JSON: ${error92 instanceof Error ? error92.message : String(error92)}`
38088
38233
  });
38089
38234
  }
38090
38235
  }
38236
+ executionCount++;
38091
38237
  try {
38238
+ const startTime = Date.now();
38092
38239
  const result = await mcpManager.callTool(parsed.serverName, parsed.toolName, toolArgs);
38240
+ const duration5 = Date.now() - startTime;
38241
+ executionSuccessCount++;
38242
+ log("info", `Tool executed successfully: ${args.name}`, {
38243
+ server: parsed.serverName,
38244
+ tool: parsed.toolName,
38245
+ durationMs: duration5
38246
+ });
38093
38247
  return JSON.stringify({
38094
38248
  success: true,
38095
38249
  result
38096
38250
  });
38097
38251
  } catch (error92) {
38252
+ const errorMsg = `Tool execution failed: ${error92 instanceof Error ? error92.message : String(error92)}`;
38253
+ log("error", errorMsg, {
38254
+ server: parsed.serverName,
38255
+ tool: parsed.toolName,
38256
+ error: errorMsg
38257
+ });
38098
38258
  return JSON.stringify({
38099
38259
  success: false,
38100
- error: `Tool execution failed: ${error92 instanceof Error ? error92.message : String(error92)}`
38260
+ error: errorMsg
38101
38261
  });
38102
38262
  }
38103
38263
  }
38264
+ }),
38265
+ toolbox_status: tool({
38266
+ description: STATUS_DESC,
38267
+ args: {},
38268
+ async execute() {
38269
+ if (!initialized) {
38270
+ try {
38271
+ await ensureInitialized();
38272
+ } catch (error92) {
38273
+ return JSON.stringify({
38274
+ status: "error",
38275
+ message: "Failed to initialize toolbox",
38276
+ error: error92 instanceof Error ? error92.message : String(error92)
38277
+ });
38278
+ }
38279
+ }
38280
+ const servers = mcpManager.getAllServers();
38281
+ const connectedServers = servers.filter((s) => s.status === "connected");
38282
+ const failedServers = servers.filter((s) => s.status === "error");
38283
+ const connectingServers = servers.filter((s) => s.status === "connecting");
38284
+ const totalTools = mcpManager.getAllCatalogTools().length;
38285
+ const status = {
38286
+ plugin: {
38287
+ initialized: true,
38288
+ configPath,
38289
+ uptime: process.uptime(),
38290
+ searches: searchCount,
38291
+ executions: executionCount,
38292
+ successRate: executionCount > 0 ? `${Math.round(executionSuccessCount / executionCount * 100)}%` : "N/A"
38293
+ },
38294
+ servers: {
38295
+ total: servers.length,
38296
+ connected: connectedServers.length,
38297
+ failed: failedServers.length,
38298
+ connecting: connectingServers.length,
38299
+ connectionRatio: `${connectedServers.length}/${servers.length}`,
38300
+ details: servers.map((server) => ({
38301
+ name: server.name,
38302
+ status: server.status,
38303
+ type: server.config.type,
38304
+ toolCount: server.tools.length,
38305
+ error: server.error || null,
38306
+ healthy: server.status === "connected"
38307
+ }))
38308
+ },
38309
+ tools: {
38310
+ total: totalTools,
38311
+ available: totalTools,
38312
+ serversWithTools: servers.filter((s) => s.tools.length > 0).length
38313
+ },
38314
+ health: {
38315
+ status: failedServers.length === 0 && servers.length > 0 ? "healthy" : failedServers.length > 0 ? "degraded" : "unknown",
38316
+ message: servers.length === 0 ? "No servers configured" : failedServers.length === 0 ? "All servers connected" : `${failedServers.length} server(s) failed to connect`
38317
+ }
38318
+ };
38319
+ log("info", `Status requested: ${connectedServers.length}/${servers.length} servers connected`, {
38320
+ connectedServers: connectedServers.length,
38321
+ totalServers: servers.length,
38322
+ totalTools
38323
+ });
38324
+ return JSON.stringify(status, null, 2);
38325
+ }
38104
38326
  })
38105
38327
  },
38106
38328
  "experimental.chat.system.transform": async (_input, output) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-toolbox",
3
- "version": "0.3.1",
3
+ "version": "0.5.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",