pi-mcp-adapter 1.4.0 → 1.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.5.0] - 2026-01-22
9
+
10
+ ### Changed
11
+ - **BREAKING: `args` parameter is now a JSON string** - The `args` parameter which previously accepted an object now accepts a JSON string. This change was required for compatibility with Claude's Vertex AI API (`google-antigravity` provider) which rejects `patternProperties` in JSON schemas (generated by `Type.Record()`).
12
+
13
+ ### Added
14
+ - **Type validation for args** - Parsed args are now validated to ensure they're a JSON object (not null, array, or primitive). Clear error messages for invalid input.
15
+ - **`isError: true` on error responses** - JSON parse errors and type validation errors now properly set `isError: true` to indicate failure to the LLM.
16
+
17
+ ### Migration
18
+ ```typescript
19
+ // Before (1.4.x)
20
+ mcp({ tool: "my_tool", args: { key: "value" } })
21
+
22
+ // After (1.5.0)
23
+ mcp({ tool: "my_tool", args: '{"key": "value"}' })
24
+ ```
25
+
26
+ ## [1.4.1] - 2026-01-19
27
+
28
+ ### Changed
29
+
30
+ - Status bar shows server count instead of tool count ("MCP: 5 servers")
31
+
8
32
  ## [1.4.0] - 2026-01-19
9
33
 
10
34
  ### Changed
package/README.md CHANGED
@@ -6,11 +6,11 @@ https://github.com/user-attachments/assets/4b7c66ff-e27e-4639-b195-22c3db406a5a
6
6
 
7
7
  ## Why This Exists
8
8
 
9
- Mario (Pi's creator) wrote about [why you might not need MCP](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/). The core issue: MCP servers ship dozens of tools with verbose descriptions. Playwright MCP has 21 tools eating 13.7k tokens. Chrome DevTools MCP has 26 tools eating 18k tokens. Connect a few servers and you've burned 30-50k tokens before the conversation starts. That's context bloat, slower responses, higher costs, and confused agents drowning in tool options.
9
+ Mario wrote about [why you might not need MCP](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/). The problem: tool definitions are verbose. A single MCP server can burn 10k+ tokens, and you're paying that cost whether you use those tools or not. Connect a few servers and you've burned half your context window before the conversation starts.
10
10
 
11
- His solution: skip MCP, write simple CLI tools.
11
+ His take: skip MCP entirely, write simple CLI tools instead.
12
12
 
13
- But there's an active ecosystem of well-maintained MCP servers for databases, browsers, APIs, file systems, and more. This adapter lets you tap into that ecosystem without the context cost. One proxy tool (~200 tokens) instead of hundreds of individual tool definitions. The LLM discovers what it needs on-demand.
13
+ But the MCP ecosystem has useful stuff - databases, browsers, APIs. This adapter gives you access without the bloat. One proxy tool (~200 tokens) instead of hundreds. The agent discovers what it needs on-demand.
14
14
 
15
15
  ## Install
16
16
 
@@ -50,9 +50,11 @@ chrome_devtools_take_screenshot
50
50
  fullPage (boolean) - Full page instead of viewport
51
51
  ```
52
52
  ```
53
- mcp({ tool: "chrome_devtools_take_screenshot", args: { format: "png" } })
53
+ mcp({ tool: "chrome_devtools_take_screenshot", args: '{"format": "png"}' })
54
54
  ```
55
55
 
56
+ Note: `args` is a JSON string, not an object.
57
+
56
58
  Two calls instead of 26 tools cluttering the context.
57
59
 
58
60
  ## Config
@@ -102,7 +104,7 @@ Supported: `cursor`, `claude-code`, `claude-desktop`, `vscode`, `windsurf`, `cod
102
104
  |------|---------|
103
105
  | Search | `mcp({ search: "screenshot navigate" })` |
104
106
  | Describe | `mcp({ describe: "tool_name" })` |
105
- | Call | `mcp({ tool: "...", args: {...} })` |
107
+ | Call | `mcp({ tool: "...", args: '{"key": "value"}' })` |
106
108
  | Status | `mcp({ })` or `mcp({ server: "name" })` |
107
109
 
108
110
  Search includes parameter schemas by default. Space-separated words are OR'd.
package/index.ts CHANGED
@@ -71,9 +71,10 @@ export default function mcpAdapter(pi: ExtensionAPI) {
71
71
 
72
72
  // Update status bar when ready
73
73
  if (ctx.hasUI) {
74
- const totalTools = [...s.registeredTools.values()].flat().length;
75
- if (totalTools > 0) {
76
- ctx.ui.setStatus("mcp", ctx.ui.theme.fg("accent", `MCP: ${totalTools} tools`));
74
+ const serverCount = s.registeredTools.size;
75
+ if (serverCount > 0) {
76
+ const label = serverCount === 1 ? "server" : "servers";
77
+ ctx.ui.setStatus("mcp", ctx.ui.theme.fg("accent", `MCP: ${serverCount} ${label}`));
77
78
  } else {
78
79
  ctx.ui.setStatus("mcp", "");
79
80
  }
@@ -174,13 +175,13 @@ Usage:
174
175
  mcp({ server: "name" }) → List tools from server
175
176
  mcp({ search: "query" }) → Search for tools (includes schemas, space-separated words OR'd)
176
177
  mcp({ describe: "tool_name" }) → Show tool details and parameters
177
- mcp({ tool: "name", args: {...} }) → Call a tool
178
+ mcp({ tool: "name", args: '{"key": "value"}' }) → Call a tool (args is JSON string)
178
179
 
179
180
  Mode: tool (call) > describe > search > server (list) > nothing (status)`,
180
181
  parameters: Type.Object({
181
182
  // Call mode
182
183
  tool: Type.Optional(Type.String({ description: "Tool name to call (e.g., 'xcodebuild_list_sims')" })),
183
- args: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: "Arguments for tool call" })),
184
+ args: Type.Optional(Type.String({ description: "Arguments as JSON string (e.g., '{\"key\": \"value\"}')" })),
184
185
  // Describe mode
185
186
  describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
186
187
  // Search mode
@@ -192,13 +193,35 @@ Mode: tool (call) > describe > search > server (list) > nothing (status)`,
192
193
  }),
193
194
  async execute(_toolCallId, params: {
194
195
  tool?: string;
195
- args?: Record<string, unknown>;
196
+ args?: string;
196
197
  describe?: string;
197
198
  search?: string;
198
199
  regex?: boolean;
199
200
  includeSchemas?: boolean;
200
201
  server?: string;
201
202
  }) {
203
+ // Parse args from JSON string if provided
204
+ let parsedArgs: Record<string, unknown> | undefined;
205
+ if (params.args) {
206
+ try {
207
+ parsedArgs = JSON.parse(params.args);
208
+ if (typeof parsedArgs !== "object" || parsedArgs === null || Array.isArray(parsedArgs)) {
209
+ const gotType = Array.isArray(parsedArgs) ? "array" : parsedArgs === null ? "null" : typeof parsedArgs;
210
+ return {
211
+ content: [{ type: "text", text: `Invalid args: expected a JSON object, got ${gotType}` }],
212
+ isError: true,
213
+ details: { error: "invalid_args_type" },
214
+ };
215
+ }
216
+ } catch (e) {
217
+ return {
218
+ content: [{ type: "text", text: `Invalid args JSON: ${e instanceof Error ? e.message : e}` }],
219
+ isError: true,
220
+ details: { error: "invalid_args" },
221
+ };
222
+ }
223
+ }
224
+
202
225
  // Wait for init if still in progress
203
226
  if (!state && initPromise) {
204
227
  try {
@@ -219,7 +242,7 @@ Mode: tool (call) > describe > search > server (list) > nothing (status)`,
219
242
 
220
243
  // Mode resolution: tool > describe > search > server > status
221
244
  if (params.tool) {
222
- return executeCall(state, params.tool, params.args);
245
+ return executeCall(state, params.tool, parsedArgs);
223
246
  }
224
247
  if (params.describe) {
225
248
  return executeDescribe(state, params.describe);
@@ -901,11 +924,12 @@ async function reconnectServers(
901
924
  }
902
925
  }
903
926
 
904
- // Update status bar with new tool count
927
+ // Update status bar with server count
905
928
  if (ctx.hasUI) {
906
- const totalTools = [...state.registeredTools.values()].flat().length;
907
- if (totalTools > 0) {
908
- ctx.ui.setStatus("mcp", ctx.ui.theme.fg("accent", `MCP: ${totalTools} tools`));
929
+ const serverCount = state.registeredTools.size;
930
+ if (serverCount > 0) {
931
+ const label = serverCount === 1 ? "server" : "servers";
932
+ ctx.ui.setStatus("mcp", ctx.ui.theme.fg("accent", `MCP: ${serverCount} ${label}`));
909
933
  } else {
910
934
  ctx.ui.setStatus("mcp", "");
911
935
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-adapter",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",