@outfitter/mcp
v0.1.0
Published
MCP server framework with typed tools for Outfitter
Maintainers
Readme
@outfitter/mcp
MCP (Model Context Protocol) server framework with typed tools and Result-based error handling.
Installation
bun add @outfitter/mcpQuick Start
import { createMcpServer, defineTool } from "@outfitter/mcp";
import { Result } from "@outfitter/contracts";
import { z } from "zod";
const server = createMcpServer({
name: "calculator",
version: "1.0.0",
});
server.registerTool(
defineTool({
name: "add",
description: "Add two numbers together",
inputSchema: z.object({
a: z.number(),
b: z.number(),
}),
handler: async (input, ctx) => {
ctx.logger.debug("Adding numbers", { a: input.a, b: input.b });
return Result.ok({ sum: input.a + input.b });
},
})
);
await server.start();Features
- Typed Tools — Define tools with Zod schemas for automatic input validation
- Result-Based Errors — All operations return
Result<T, E>for explicit error handling - Handler Contract — Tools use the same
Handlerpattern as other Outfitter packages - Core Tools — Built-in docs, config, and query tools for common patterns
- Deferred Loading — Support for MCP tool search with
deferLoadingflag
API Reference
createMcpServer(options)
Creates an MCP server instance.
interface McpServerOptions {
name: string; // Server name for MCP handshake
version: string; // Server version (semver)
logger?: Logger; // Optional structured logger
}
const server = createMcpServer({
name: "my-server",
version: "1.0.0",
logger: createLogger({ name: "mcp" }),
});defineTool(definition)
Helper for defining typed tools with better type inference.
interface ToolDefinition<TInput, TOutput, TError> {
name: string; // Unique tool name (kebab-case)
description: string; // Human-readable description
inputSchema: z.ZodType<TInput>; // Zod schema for validation
handler: Handler<TInput, TOutput, TError>;
deferLoading?: boolean; // Default: true
}
const getUserTool = defineTool({
name: "get-user",
description: "Retrieve a user by their unique ID",
inputSchema: z.object({ userId: z.string().uuid() }),
handler: async (input, ctx) => {
const user = await db.users.find(input.userId);
if (!user) {
return Result.err(new NotFoundError("user", input.userId));
}
return Result.ok(user);
},
});defineResource(definition)
Helper for defining MCP resources.
interface ResourceDefinition {
uri: string; // Unique resource URI
name: string; // Human-readable name
description?: string; // Optional description
mimeType?: string; // Content MIME type
}
const configResource = defineResource({
uri: "file:///etc/app/config.json",
name: "Application Config",
description: "Main configuration file",
mimeType: "application/json",
});Server Methods
interface McpServer {
readonly name: string;
readonly version: string;
// Registration
registerTool<TInput, TOutput, TError>(tool: ToolDefinition): void;
registerResource(resource: ResourceDefinition): void;
// Introspection
getTools(): SerializedTool[];
getResources(): ResourceDefinition[];
// Invocation
invokeTool<T>(name: string, input: unknown, options?: InvokeToolOptions): Promise<Result<T, McpError>>;
// Lifecycle
start(): Promise<void>;
stop(): Promise<void>;
}McpHandlerContext
Extended handler context for MCP tools with additional metadata:
interface McpHandlerContext extends HandlerContext {
toolName?: string; // Name of the tool being invoked
}Core Tools
Pre-built tools for common MCP patterns. These are marked with deferLoading: false for immediate availability.
Docs Tool
Provides documentation, usage patterns, and examples.
import { defineDocsTool, createCoreTools } from "@outfitter/mcp";
const docsTool = defineDocsTool({
docs: {
overview: "Calculator server for arithmetic operations",
tools: [{ name: "add", summary: "Add two numbers" }],
examples: [{ input: { a: 2, b: 3 }, description: "Basic addition" }],
},
});
// Or use getDocs for dynamic content
const dynamicDocsTool = defineDocsTool({
getDocs: async (section) => {
return loadDocsFromFile(section);
},
});Config Tool
Read and modify server configuration.
import { defineConfigTool } from "@outfitter/mcp";
const configTool = defineConfigTool({
initial: { debug: false, maxRetries: 3 },
});
// With custom store
const persistedConfigTool = defineConfigTool({
store: {
get: async (key) => db.config.get(key),
set: async (key, value) => db.config.set(key, value),
list: async () => db.config.all(),
},
});Query Tool
Search and discovery with pagination.
import { defineQueryTool } from "@outfitter/mcp";
const queryTool = defineQueryTool({
handler: async (input, ctx) => {
const results = await searchIndex(input.q, {
limit: input.limit,
cursor: input.cursor,
filters: input.filters,
});
return Result.ok({
results: results.items,
nextCursor: results.nextCursor,
});
},
});Bundle All Core Tools
import { createCoreTools } from "@outfitter/mcp";
const coreTools = createCoreTools({
docs: { docs: myDocs },
config: { initial: myConfig },
query: { handler: myQueryHandler },
});
for (const tool of coreTools) {
server.registerTool(tool);
}Transport Helpers
connectStdio
Connect server to stdio transport for Claude Desktop integration.
import { createMcpServer, connectStdio } from "@outfitter/mcp";
const server = createMcpServer({ name: "my-server", version: "1.0.0" });
// ... register tools ...
await connectStdio(server);createSdkServer
Create the underlying @modelcontextprotocol/sdk server.
import { createSdkServer } from "@outfitter/mcp";
const { server: sdkServer, toolsList, callTool } = createSdkServer(mcpServer);Error Handling
Tools return Results with typed errors. The framework automatically translates OutfitterError categories to JSON-RPC error codes:
| Category | JSON-RPC Code | Description |
|----------|--------------|-------------|
| validation | -32602 | Invalid params |
| not_found | -32601 | Method not found |
| permission | -32600 | Invalid request |
| internal | -32603 | Internal error |
const result = await server.invokeTool("get-user", { userId: "123" });
if (result.isErr()) {
// result.error is McpError with code and context
console.error(result.error.message, result.error.code);
}Schema Utilities
zodToJsonSchema
Convert Zod schemas to JSON Schema for MCP protocol.
import { zodToJsonSchema } from "@outfitter/mcp";
const schema = z.object({
name: z.string(),
age: z.number().optional(),
});
const jsonSchema = zodToJsonSchema(schema);
// { type: "object", properties: { name: { type: "string" }, ... } }Action Adapter
buildMcpTools
Build MCP tools from an action registry (for structured action-based servers).
import { buildMcpTools } from "@outfitter/mcp";
const tools = buildMcpTools({
actions: myActionRegistry,
prefix: "myapp",
});
for (const tool of tools) {
server.registerTool(tool);
}Claude Desktop Configuration
Add your MCP server to Claude Desktop:
{
"mcpServers": {
"my-server": {
"command": "bun",
"args": ["run", "/path/to/server.ts"]
}
}
}Config location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/claude/claude_desktop_config.json
Related Packages
- @outfitter/contracts — Result types and error taxonomy
- @outfitter/logging — Structured logging
- @outfitter/config — Configuration loading
