@capsuleer/mcp
v1.0.2
Published
MCP adapter for capsuleer — wrap any MCP server as a capsuleer module
Downloads
37
Maintainers
Readme
@capsuleer/mcp
MCP adapter for Capsuleer. Wrap any Model Context Protocol server as a Capsuleer module — its tools are automatically discovered, typed, and registered as globals in the capsule's execution scope.
capsuleer install mcpWhat it does
MCP servers expose tools over a stdio JSON-RPC protocol. @capsuleer/mcp connects to any MCP server, calls tools/list to discover its full tool surface, and registers each tool as an async function on a named global. The capsule then emits a module:manifest event with TypeScript declarations derived from each tool's JSON Schema — so the LLM has accurate signatures and can't hallucinate a tool that doesn't exist.
Usage
Create a .module.ts file in ~/.capsuleer/environment/modules/ that calls defineMcpModule:
// ~/.capsuleer/environment/modules/linear.module.ts
import { defineMcpModule } from "@capsuleer/mcp"
export default await defineMcpModule({
name: "linear",
description: "Linear issue tracker",
command: "npx",
args: ["-y", "@linear/mcp-server"],
env: {
LINEAR_API_KEY: process.env.LINEAR_API_KEY!,
},
})After the capsule boots, agent code can call any of Linear's tools directly:
const issues = await linear.list_issues({ team_id: "ENG", state: "open" })
const issue = await linear.create_issue({ title: "Fix auth timeout", team_id: "ENG" })The tool names and parameter shapes come directly from the MCP server — no hand-written wrappers needed.
Config
defineMcpModule({
/** Module name — becomes the global namespace in the capsule */
name: string
/** Shown in registry and injected into LLM context */
description?: string
/** Command to spawn the MCP server subprocess */
command: string
/** Arguments passed to the command */
args?: string[]
/** Environment variables forwarded to the server process */
env?: Record<string, string>
})How it works
- Spawns the MCP server subprocess via
StdioClientTransport - Connects with
@modelcontextprotocol/sdk - Calls
tools/list(with pagination) to discover all tools - For each tool, builds an async wrapper that maps positional args → the tool's JSON Schema parameter names
- Calls
tools/calland extracts the text content from the response - Generates TypeScript declarations from each tool's
inputSchemafor the manifest - Registers graceful shutdown on
exit,SIGTERM,SIGINT
Tool results are returned as strings. For tools that return structured data, parse with JSON.parse().
Examples
Filesystem via MCP (e.g. @modelcontextprotocol/server-filesystem):
export default await defineMcpModule({
name: "files",
description: "Filesystem access via MCP",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/project"],
})GitHub via MCP:
export default await defineMcpModule({
name: "github",
description: "GitHub API via MCP",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: { GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_TOKEN! },
})Any local MCP server:
export default await defineMcpModule({
name: "mytools",
description: "Internal tooling",
command: "bun",
args: ["run", "/home/user/my-mcp-server/index.ts"],
env: { API_KEY: process.env.INTERNAL_API_KEY! },
})Notes
- The MCP server process runs as a child of the capsule — it is killed when the capsule shuts down
- Tools with no input parameters accept an optional
argsobject - If the server reports zero tools,
defineMcpModulethrows immediately rather than registering an empty module - The module is async (
await defineMcpModule(...)) — use top-level await in your.module.tsfile
