--- name: mcpc-core description: Build agentic MCP servers using @mcpc/core by composing existing MCP tools. Use when creating, extending, or debugging MCPC agentic servers — including setting up compositions, choosing execution modes, writing plugins, loading skills, and connecting transports. --- # @mcpc/core — Build Agentic MCP Servers `@mcpc/core` lets you compose existing MCP tools into agentic MCP tools. You write a description that references tools via `` XML tags, and MCPC wires everything up into a working MCP server. ## Installation ```bash # Deno (preferred in this repo) deno add jsr:@mcpc/core # npm npm install @mcpc-tech/core # pnpm (from JSR) pnpm add jsr:@mcpc/core ``` ## Minimal Example ```typescript import { mcpc } from "@mcpc/core"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = await mcpc( [{ name: "my-agent", version: "1.0.0" }, { capabilities: { tools: {} } }], [{ name: "file-agent", description: `I help manage files. Available tools: `, deps: { mcpServers: { "@wonderwhy-er/desktop-commander": { command: "npx", args: ["-y", "@wonderwhy-er/desktop-commander@latest"], transportType: "stdio", }, }, }, }], ); await server.connect(new StdioServerTransport()); ``` ## Core API ### `mcpc(serverConf, composeConf?, options?)` | Param | Type | Description | | ------------- | ---------------------------------- | -------------------------------------------------------- | | `serverConf` | `[ServerMeta, ServerCapabilities]` | Name/version + capabilities tuple | | `composeConf` | `ComposeInput[]` | Array of `ComposeDefinition` objects or `.md` file paths | | `options` | `McpcOptions` | Loader plugins + setup callback | Returns `Promise`. ### `ComposeDefinition` ```typescript { name: string | null, // null = composition-only (no tool exposed) description?: string, // Natural language + XML refs manual?: string, // Progressive disclosure: long docs hidden behind man tool deps?: MCPSetting, // Dependent MCP servers to connect plugins?: (ToolPlugin | string)[], // Runtime plugins options?: { mode?: "agentic" | "ai_sampling" | "ai_acp", // default: "agentic" maxSteps?: number, // default: 50 maxTokens?: number, // default: 128_000 (ai_sampling only) samplingConfig?: SamplingConfig, providerOptions?: { modelPreferences?: {...} }, // ai_sampling only acpSettings?: { command, args, env, session, persistSession }, // ai_acp only tracingEnabled?: boolean, // OpenTelemetry refs?: ToolRefXml[], } } ``` ## Tool References (`` XML) In the `description` field, reference tools with XML tags: ``` — specific tool — all tools from a server — truncate large results — use internally, hide from public API — expose at top-level scope ``` ## MCP Server Dependencies (`deps.mcpServers`) Three transport types: ```typescript // stdio "my-server": { command: "npx", args: ["-y", "pkg@latest"], transportType: "stdio" } // Streamable HTTP "remote": { transportType: "streamable-http", url: "https://api.example.com/mcp/", headers: { "Authorization": "Bearer ${TOKEN}" } } // SSE "sse-server": { transportType: "sse", url: "https://api.example.com/sse/" } ``` ## Execution Modes | Mode | Description | Best For | | ------------- | ------------------------------------------------ | ------------------------------- | | `agentic` | Fully autonomous, uses MCP sampling | Default; most flexible | | `ai_sampling` | Uses AI SDK `streamText` + MCP sampling provider | When you control the model | | `ai_acp` | AI SDK + ACP protocol for coding agents | Claude Code, Codex-style agents | ## Plugins ### Built-in Plugins (import from `@mcpc/core/plugins`) ```typescript import { createBashPlugin, // Add bash execution tool createLargeResultPlugin, // Truncate/paginate large tool results createSearchPlugin, // Add ripgrep-based search tool createSkillsPlugin, // Lazy-load skill knowledge } from "@mcpc/core/plugins"; ``` ### Using Plugins ```typescript // In ComposeDefinition plugins: [ createLargeResultPlugin({ maxSize: 8000 }), createSkillsPlugin({ paths: [".agents/skills"] }), createBashPlugin(), "./my-plugin.ts?param=value", // File path with query params ]; // Or in mcpc() options (loader plugins, run before composition) await mcpc(serverConf, composeConf, { plugins: ["@mcpc/plugin-markdown-loader"], }); ``` ### Writing a Custom Plugin (`ToolPlugin`) ```typescript import type { ToolPlugin } from "@mcpc/core"; const myPlugin: ToolPlugin = { name: "my-plugin", // Mutate/filter tool descriptions at compose time transformTool?: (tool: ComposedTool, ctx: TransformContext) => ComposedTool, // Intercept inputs before execution transformInput?: (args: unknown, ctx: BeforeToolExecuteContext) => unknown, // Intercept outputs after execution transformOutput?: (result: unknown, ctx: AfterToolExecuteContext) => unknown, // Lifecycle hooks onComposeStart?: (ctx: ComposeStartContext) => void, onComposeEnd?: (ctx: ComposeEndContext) => void, onFinalize?: (ctx: FinalizeContext) => void, }; ``` ## Skills Plugin `createSkillsPlugin` adds a `{agent}__load-skill` tool that lazy-loads skill content: ```typescript createSkillsPlugin({ paths: [join(projectRoot, ".agents/skills")], }); ``` Skills directory structure: ``` .agents/skills/ └── my-skill/ ├── SKILL.md ← main instructions (loaded by default) └── references/ └── api.md ← loaded via { skill, ref: "references/api.md" } ``` Agent calls: ``` agent__load-skill({ skill: "my-skill" }) agent__load-skill({ skill: "my-skill", ref: "references/api.md" }) ``` ## Markdown Agent Files Load agent definitions from `.md` files (requires `@mcpc/plugin-markdown-loader`): ```typescript await mcpc( [{ name: "server", version: "1.0.0" }, { capabilities: { tools: {} } }], ["./agents/my-agent.md"], { plugins: ["@mcpc/plugin-markdown-loader"] }, ); ``` Markdown format: ```markdown --- name: my-agent deps: mcpServers: some-server: command: npx args: ["-y", "some-server"] transportType: stdio options: mode: agentic --- I am an agent that does X. ``` ## Setup Callback (Custom Tools) Register tools directly on the server before composition: ```typescript import { jsonSchema } from "@mcpc/core"; await mcpc(serverConf, composeConf, { setup: async (server) => { server.tool( "my-tool", "Description of tool", jsonSchema({ type: "object", properties: { input: { type: "string" } } }), async (args) => ({ content: [{ type: "text", text: `Got: ${args.input}` }], }), ); await server.addPlugin(myPlugin); }, }); ``` ## Gotchas - **`transportType` is required** in every `mcpServers` entry — omitting it causes a silent failure. - **`name: null`** in `ComposeDefinition` skips creating the composed tool; useful for composition-only (just wiring up deps + plugins). - **Plugin load order matters**: `McpcOptions.plugins` run before composition (use for loaders). `ComposeDefinition.plugins` run after composition (use for runtime transforms). - **`hide` vs `global` on ``**: `hide` keeps a tool internal to the agent. `global` exposes it at top-level without a namespace prefix. - **`manual` field**: if set, `description` becomes a short summary and full content is behind a `man` tool call — reduces initial context size. - **Environment variables in headers**: use `${VAR_NAME}` syntax inside strings in `deps.mcpServers[*].headers` — MCPC expands them at runtime. - **`maxSteps` default is 50** for ai_sampling/ai_acp modes — increase for complex multi-step tasks. ## References - See `references/examples.md` for complete working examples - Source: https://github.com/mcpc-tech/mcpc/tree/main/packages/core - Docs: https://github.com/mcpc-tech/mcpc/tree/main/docs