pi-mcp-extension 1.5.3 → 1.6.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/README.md CHANGED
@@ -159,6 +159,8 @@ Config files are loaded from two locations. **Project config overrides global co
159
159
 
160
160
  ## Commands
161
161
 
162
+ Server-name arguments provide autocomplete suggestions from the configured MCP servers. `/mcp:auth` suggests only servers configured for OAuth.
163
+
162
164
  | Command | Description |
163
165
  |---|---|
164
166
  | `/mcp` | Show status summary of all configured servers |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-extension",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "MCP (Model Context Protocol) client extension for the Pi coding agent — connect Pi to any MCP server",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/index.ts CHANGED
@@ -157,6 +157,42 @@ export function parseAuthArguments(args: string, serverNames: string[]): { name:
157
157
  return { name: reset ? name.replace(/\s+--reset$/, "").trimEnd() : name, reset };
158
158
  }
159
159
 
160
+ interface ServerCompletionEntry {
161
+ name: string;
162
+ state: string;
163
+ config: {
164
+ transport: string;
165
+ lifecycle: string;
166
+ auth?: unknown;
167
+ };
168
+ }
169
+
170
+ export function serverNameCompletions(
171
+ argumentPrefix: string,
172
+ servers: ServerCompletionEntry[],
173
+ options: { authOnly?: boolean } = {},
174
+ ) {
175
+ const prefix = argumentPrefix.trimStart();
176
+ const completions = servers
177
+ .filter((server) => !options.authOnly || server.config.auth)
178
+ .filter((server) => server.name.startsWith(prefix))
179
+ .sort((a, b) => a.name.localeCompare(b.name))
180
+ .map((server) => ({
181
+ value: server.name,
182
+ label: server.name,
183
+ description: [
184
+ server.state,
185
+ server.config.transport,
186
+ server.config.lifecycle,
187
+ server.config.auth ? "OAuth" : undefined,
188
+ ]
189
+ .filter(Boolean)
190
+ .join(" · "),
191
+ }));
192
+
193
+ return completions.length > 0 ? completions : null;
194
+ }
195
+
160
196
  export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string; globalConfigPath?: string; authStorageDir?: string; browserOpen?: (url: string) => void } = {}): Promise<void> {
161
197
  // ── 1. Load and validate config ──────────────────────────────────────────
162
198
  // cwd is available on the ExtensionContext passed to event handlers.
@@ -277,6 +313,7 @@ export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string;
277
313
  ctx.ui.notify(manager.getStatusSummary(), "info");
278
314
  }
279
315
  },
316
+ getArgumentCompletions: (prefix) => serverNameCompletions(prefix, manager.getAllServers()),
280
317
  });
281
318
 
282
319
  // ── 5. /mcp:stop — stop a server ─────────────────────────────────────────
@@ -297,6 +334,7 @@ export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string;
297
334
  await manager.stopServer(serverName);
298
335
  ctx.ui.notify(`pi-mcp: Stopped ${serverName}`, "info");
299
336
  },
337
+ getArgumentCompletions: (prefix) => serverNameCompletions(prefix, manager.getAllServers()),
300
338
  });
301
339
 
302
340
  // ── 6. /mcp:start — manually start a lazy server ─────────────────────────
@@ -320,6 +358,7 @@ export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string;
320
358
  ctx.ui.notify(`pi-mcp: Failed to start ${serverName} — ${msg}`, "error");
321
359
  }
322
360
  },
361
+ getArgumentCompletions: (prefix) => serverNameCompletions(prefix, manager.getAllServers()),
323
362
  });
324
363
 
325
364
  async function authorize(serverName: string, ctx: ExtensionContext, signal: AbortSignal, reset: boolean): Promise<void> {
@@ -565,5 +604,6 @@ export default async function (pi: ExtensionAPI, paths: { bootstrapCwd?: string;
565
604
  ctx.ui.notify(`pi-mcp: ${err instanceof Error ? err.message : String(err)}`, err instanceof AuthCancelledError ? "info" : "error");
566
605
  }
567
606
  },
607
+ getArgumentCompletions: (prefix) => serverNameCompletions(prefix, manager.getAllServers(), { authOnly: true }),
568
608
  });
569
609
  }
@@ -226,7 +226,7 @@ export function buildToolName(prefix: string, serverName: string, toolName: stri
226
226
  type PiTextContent = { type: "text"; text: string };
227
227
  type PiImageContent = { type: "image"; data: string; mimeType: string };
228
228
 
229
- function convertMcpContent(items: unknown[]): (PiTextContent | PiImageContent)[] {
229
+ export function convertMcpContent(items: unknown[]): (PiTextContent | PiImageContent)[] {
230
230
  return items.map((item: any) => {
231
231
  if (!item || typeof item !== "object") {
232
232
  return { type: "text", text: String(item) };
@@ -448,7 +448,8 @@ export class ToolBridge {
448
448
  // Tool execution errors (isError: true) — distinct from protocol errors
449
449
  if (result.isError) {
450
450
  const errorText = content
451
- .map((c) => (c.type === "text" ? c.text : ""))
451
+ .filter((c): c is PiTextContent => c.type === "text")
452
+ .map((c) => c.text)
452
453
  .join("\n");
453
454
  throw new McpError(
454
455
  errorText || "Tool reported an error",