Skip to main content
Glama

MCPMan

by semistrict
CLAUDE.md12.2 kB
# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview MCPMan is a Model Context Protocol (MCP) server manager that acts as a proxy/multiplexer for multiple MCP servers. It provides: - **Upstream Server Management**: Connects to multiple MCP servers via stdio or HTTP transports with OAuth 2.1 support - **Code Generation**: LLM-powered code generation from natural language with TypeScript validation - **Type System**: Automatic TypeScript type generation for all connected MCP tools - **Eval Runtime**: JavaScript execution environment with access to all connected MCP tools and $results array - **Macros**: Reusable JavaScript modules automatically discovered from .mcpman/macros/ directories in project roots - **MCP Server**: Exposes tools for code generation, evaluation, tool invocation, server management, and help - **CLI Commands**: Management commands for configuration, testing, code generation, and serving ## Architecture ### Core Components - **UpstreamServerManager** (`src/mcp/upstream-server-manager.ts`): Manages connections to upstream MCP servers with three-state model (not-connected, connected, enabled) - **MCPServer** (`src/mcp/server.ts`): Exposes MCPMan as an MCP server with tools in `src/mcp/tools/` - **ToolManager** (`src/mcp/tool-manager.ts`): Generates TypeScript definitions for all MCP tools, caches them, and provides tool execution interface - **EvalRuntime** (`src/eval/runtime.ts`): Sandboxed JavaScript execution with MCP tool access and $results array - **MacroManager** (`src/mcp/macros.ts`): Discovers and loads macros from .mcpman/macros/ directories in project roots - **Configuration** (`src/config/`): Zod-based schema validation for server configs - **OAuth Provider** (`src/auth/`): OAuth 2.1 implementation for HTTP MCP servers - **Logging** (`src/utils/logging.ts`): Synchronous file logging with error stack trace support ### Key Files - `index.ts`: Main entry point - CLI mode or server mode based on arguments - `src/config/schema.ts`: Configuration schema for stdio/HTTP servers with OAuth and autoConnect - `src/mcp/tool-manager.ts`: TypeScript type generation from MCP tool schemas with caching - `src/mcp/macros.ts`: MacroManager for discovering and executing macros from .mcpman/macros/ directories - `src/eval/proxy.ts`: Creates server proxies for eval environment with camelCase name mapping - `src/eval/runtime.ts`: VM context with $results array for storing tool outputs - `src/cli/commands/`: CLI command implementations (code, eval, serve, etc.) - `src/mcp/tools/`: Individual MCP tool implementations (code, eval, invoke, list_servers, help, install, enable_server) - `src/mcp/tools/code.ts`: LLM-powered code generation with TypeScript validation (uses Claude Agent SDK) - `src/utils/find-claude.ts`: Locates Claude Code CLI executable for Agent SDK ## Development Commands ```bash # Install dependencies bun install # Development server with hot reload bun dev # Build compiled binary bun run build # CLI commands bun cli add <server-name> bun cli init bun cli list bun cli validate bun cli test bun cli serve bun cli auth bun cli code "natural language description" [--roots /path/to/dir] bun cli eval "function-expression" [--arg '{"key": "value"}'] [--roots /path/to/dir] # Linting and formatting bun run lint bun run format bun run check bun run typecheck # Run tests bun test # Pre-commit hooks (install once) pre-commit install ``` ## Configuration MCPMan uses a JSON config file defining servers with transport types: ```json { "servers": { "my-server": { "transport": "stdio", "command": "npx", "args": ["@modelcontextprotocol/server-everything"], "autoConnect": true // default: true - connect at startup }, "http-server": { "transport": "http", "url": "https://api.example.com/mcp", "autoConnect": false, // connect on-demand when enabled "oauth": { "clientName": "mcpman", "scopes": ["mcp:tools"] } } } } ``` ## Server States Servers in MCPMan have three states: - **not-connected**: Server is configured but not connected (autoConnect: false) - **connected**: Server is connected but tools are not available for use - **enabled**: Server is connected and tools are available (use `enable_server` tool to enable) Servers with `autoConnect: true` (default) automatically connect at startup in the 'connected' state. Use the `enable_server` tool to enable them and make their tools available. ## Code Style - Uses Biome for linting/formatting with 100 char line width, 2-space indents - TypeScript with Zod schemas for validation - Bun APIs preferred over Node.js equivalents - Error handling with proper context and user-friendly messages - Pre-commit hooks run lint, typecheck, and format on commit; tests on push ## Roots Protocol MCPMan acts as a transparent proxy for MCP roots: - In server mode: Gets roots from connected client and forwards to upstream servers - In CLI eval mode: Uses `--roots` option or defaults to current directory - Upstream servers receive proper root directories for filesystem access ## TypeScript Type Generation MCPMan automatically generates TypeScript type definitions for all connected MCP tools: - **Type Generation**: Converts MCP tool JSON schemas to TypeScript interfaces using `json-schema-to-typescript` - **Naming Convention**: - Server names: `agent-debugger` → `agentDebugger` (camelCase variable) - Tool names: `list-targets` → `listTargets` (camelCase method) - Type names: `agent-debugger_list-targets` → `AgentDebuggerListTargetsInput/Output` (PascalCase) - **Caching**: Generated types are cached and only regenerated when tool schemas change - **Server Filtering**: Optional `servers` parameter generates types for only specified servers to reduce context - **Runtime Mapping**: Proxy layer maps camelCase names back to original kebab-case/snake_case tool names Example generated types: ```typescript declare interface PlaywrightBrowserNavigateInput { url: string; } declare interface PlaywrightBrowserNavigateOutput { [key: string]: any } declare const playwright: { browserNavigate: (input: PlaywrightBrowserNavigateInput) => Promise<PlaywrightBrowserNavigateOutput>; }; ``` ## MCP Tools Exposed MCPMan exposes the following tools when running as an MCP server: 1. **code** - Generate and execute JavaScript code from natural language descriptions using LLMs. Code is generated by Claude Agent SDK (or MCP sampling) and validated with TypeScript compiler API before execution. Results stored in $results array. 2. **eval** - Execute JavaScript function expressions with access to all connected MCP tools and macros. Results stored in $results array. 3. **invoke** - Invoke tools from upstream servers or macros with schema validation. Supports parallel and sequential batch invocations. Results stored in $results array. 4. **list_servers** - List all MCP servers with their states (not-connected, connected, enabled) and tool counts 5. **help** - Get help information about MCP tools from enabled servers or macros 6. **install** - Add new MCP servers to configuration dynamically 7. **enable_server** - Enable a server to make its tools available (connects on-demand if not-connected) ### Tool Examples ```javascript // code tool - generate and execute code from natural language { functionDescription: "navigate to google.com and take a screenshot", servers: ["playwright"] // STRONGLY RECOMMENDED to limit context } // eval tool - execute function with MCP access { code: "() => listServers()", arg: null } { code: "(arg) => filesystem.listFiles({path: arg.directory})", arg: {"directory": "."} } // invoke tool - call upstream tools with validation { calls: [{ server: "filesystem", tool: "read_file", parameters: {path: "README.md"} }], parallel: false } // $results array - access previous results { code: "() => $results[0]", arg: null } ``` ## Macros Macros are reusable TypeScript modules that are automatically discovered from `.mcpman/macros/` directories within project roots. Macros are hot-reloaded when files change. ### Creating Macros Create a TypeScript file in `.mcpman/macros/` directory (e.g., `.mcpman/macros/my-macro.ts`): ```typescript import { defineMacro, z } from "mcpman"; import { filesystem } from "server:filesystem"; import { playwright } from "server:playwright"; defineMacro({ description: "Description of what this macro does", parameters: { arg1: z.string().describe("Description of arg1"), arg2: z.number().optional().describe("Optional number parameter"), }, action: async (context, args) => { // Access upstream MCP tools via imported server proxies const files = await filesystem.listDirectory({ path: args.arg1 }); // Return the result return { files, count: files.length }; }, }); ``` **Key Features**: - **Naming**: Macro name is derived from the filename (kebab-case → camelCase: `my-macro.ts` → `myMacro`) - **Virtual Imports**: Use `import { defineMacro, z } from "mcpman"` to access macro definition function - **Server Imports**: Use `import { serverName } from "server:serverName"` to access MCP server tools - **Hot Reload**: Changes to macro files are automatically detected and reloaded - **Type Safety**: Full TypeScript support with Zod schema validation ### Using Macros Macros are automatically loaded and can be invoked via: ```javascript // Via eval macros.myMacro({ arg1: 'value', arg2: 42 }) // Via invoke { server: 'macros', tool: 'myMacro', parameters: { arg1: 'value', arg2: 42 } } ``` Macros are discovered from all project roots and loaded on-demand when accessed. ## Testing ### Running Tests - Always run tests with `bun run test`, not `bun test` - Do not use `bun test`, use `bun run test` - Run a single test: `bun run test -- -t "test name"` - Run tests from a specific file: `bun run test tests/filename.test.ts` - Do not use dynamic imports ### Debugging - Enable trace logging: `MCPMAN_TRACE=1 bun run test` - Trace logs are written to `~/.mcpman/trace.log` - Regular logs are written to `~/.mcpman/mcpman.log` **Using TRACE in code:** - ALWAYS use TRACE as a tagged template literal: `TRACE\`message ${variable}\`` - NEVER use TRACE as a function call: ~~`TRACE("message")`~~ ❌ - NEVER use console.log for debugging - use TRACE instead - Example: `TRACE\`Loading macro '${name}' from ${path}\`` **CRITICAL - TRACE is NOT a substitute for proper error handling:** - TRACE is ONLY for development debugging (enabled via MCPMAN_TRACE=1) - TRACE is NOT a substitute for: - Proper error logging (use `log` from `src/utils/logging.ts` for errors) - Throwing errors when operations fail (throw descriptive Error objects) - User-facing error messages (return clear error messages in tool responses) - ✅ Correct: `throw new Error("Failed to load macro: file not found")` - ✅ Correct: `log(\`Error loading macro: ${error}\`)` - ❌ Wrong: Using only TRACE when an error occurs without throwing or logging ### Writing Tests with Test Fixtures Tests should use the `test-fixtures.js` support for automatic client setup and cleanup: ```typescript import { test, expect, describe } from './test-fixtures.js'; describe('My Test', () => { test('should do something', async ({ client }) => { const result = await client.callTool({ name: 'eval', arguments: { function: '() => 42' }, }); const content = result.content as Array<{ type: string; text: string }>; expect(content[0]?.text).toContain('42'); }); }); ``` **Key points:** - Import `test`, `expect`, `describe` from `./test-fixtures.js` (not directly from vitest) - Use the `client` fixture provided by `test-fixtures.js` - it automatically: - Creates a fresh MCP client for each test - Provides roots via the MCP roots protocol - Uses the default test config at `tests/config` - Cleans up and closes the client after each test - Each test function receives `{ client }` as a parameter - The client is already connected and ready to call tools - Do not manually create or manage client/transport instances

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/semistrict/mcpman'

If you have feedback or need assistance with the MCP directory API, please join our Discord server