Files
fusion/packages/pi-claude-cli/src/mcp-config.ts
gsxdsm 4cabe7f613 refactor: eliminate ~400 no-explicit-any warnings across the workspace
Parallel subagent pass: four typescript-pro agents on non-overlapping scopes.

Patterns applied:
- catch (err: any) { ... err.message ... } → catch (err) { ... getErrorMessage(err) ... }
  using the new @fusion/core helper. Bare catch {} where the error was unused.
- SQLite row types: defined typed XxxRow interfaces per table and cast
  .all()/.get() results via `as unknown as XxxRow[]` (the double cast is
  required because better-sqlite3 returns Record<string, SQLOutputValue>).
- rowToX(row: any) converters: typed argument with the matching row interface.
- Dynamic settings key writes: (settings as Record<string, unknown>)[key].
- React event handlers and setState callbacks: inferred types or concrete
  React.{Mouse,Change,Form}Event<...> where needed.
- pi-claude-cli: local PiMessage / PiContext duck types to avoid re-typing
  pi-ai concrete shapes; typed Claude stream event message fields.

72 files changed, ~400 anys eliminated. Typecheck passes across the workspace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:02:36 -07:00

112 lines
2.9 KiB
TypeScript

/**
* Custom tool discovery and MCP config file generation.
*
* Discovers non-built-in tools from pi, writes their schemas to a temp file,
* and generates an MCP config that points to the schema-only MCP server.
*/
import { writeFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
/**
* A single tool descriptor returned by pi.getAllTools().
*/
interface PiToolInfo {
name: string;
description: string;
parameters: Record<string, unknown>;
}
/**
* Minimal duck-type interface for the pi ExtensionAPI instance.
* We only call getAllTools(), so we only declare that method.
* The return type is unknown to accommodate defensive runtime checks.
*/
interface PiInstance {
getAllTools(): unknown;
}
/** The 6 built-in tools that pi handles natively (match pi tool names). */
const BUILT_IN_TOOL_NAMES = new Set([
"read",
"write",
"edit",
"bash",
"grep",
"find",
]);
/** A custom tool definition with MCP-compatible schema. */
export interface McpToolDef {
name: string;
description: string;
inputSchema: Record<string, unknown>;
}
/**
* Get custom tool definitions from pi, filtering out built-in tools.
*
* @param pi - The pi ExtensionAPI instance
* @returns Array of custom tool definitions (empty if all tools are built-in)
*/
export function getCustomToolDefs(pi: PiInstance): McpToolDef[] {
const allTools = pi.getAllTools();
if (!Array.isArray(allTools)) {
return [];
}
return (allTools as PiToolInfo[])
.filter((tool) => !BUILT_IN_TOOL_NAMES.has(tool.name))
.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.parameters,
}));
}
/**
* Write MCP config and tool schemas to temp files.
*
* Creates two temp files:
* 1. Schema file: JSON array of tool definitions
* 2. Config file: MCP config pointing to the schema-only server
*
* @param toolDefs - Array of custom tool definitions
* @returns Path to the MCP config file
*/
export function writeMcpConfig(toolDefs: McpToolDef[]): string {
// Write tool schemas to temp file
const schemaFilePath = join(
tmpdir(),
`pi-claude-mcp-schemas-${process.pid}.json`,
);
writeFileSync(schemaFilePath, JSON.stringify(toolDefs));
// Resolve path to the schema server .cjs file (sibling of this module)
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const serverPath = join(__dirname, "mcp-schema-server.cjs");
// Build MCP config
const config = {
mcpServers: {
"custom-tools": {
command: "node",
args: [serverPath, schemaFilePath],
},
},
};
// Write config to temp file
const configFilePath = join(
tmpdir(),
`pi-claude-mcp-config-${process.pid}.json`,
);
writeFileSync(configFilePath, JSON.stringify(config));
return configFilePath;
}