@outfitter/mcp 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,342 @@
1
+ # @outfitter/mcp
2
+
3
+ MCP (Model Context Protocol) server framework with typed tools and Result-based error handling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @outfitter/mcp
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createMcpServer, defineTool } from "@outfitter/mcp";
15
+ import { Result } from "@outfitter/contracts";
16
+ import { z } from "zod";
17
+
18
+ const server = createMcpServer({
19
+ name: "calculator",
20
+ version: "1.0.0",
21
+ });
22
+
23
+ server.registerTool(
24
+ defineTool({
25
+ name: "add",
26
+ description: "Add two numbers together",
27
+ inputSchema: z.object({
28
+ a: z.number(),
29
+ b: z.number(),
30
+ }),
31
+ handler: async (input, ctx) => {
32
+ ctx.logger.debug("Adding numbers", { a: input.a, b: input.b });
33
+ return Result.ok({ sum: input.a + input.b });
34
+ },
35
+ })
36
+ );
37
+
38
+ await server.start();
39
+ ```
40
+
41
+ ## Features
42
+
43
+ - **Typed Tools** — Define tools with Zod schemas for automatic input validation
44
+ - **Result-Based Errors** — All operations return `Result<T, E>` for explicit error handling
45
+ - **Handler Contract** — Tools use the same `Handler` pattern as other Outfitter packages
46
+ - **Core Tools** — Built-in docs, config, and query tools for common patterns
47
+ - **Deferred Loading** — Support for MCP tool search with `deferLoading` flag
48
+
49
+ ## API Reference
50
+
51
+ ### createMcpServer(options)
52
+
53
+ Creates an MCP server instance.
54
+
55
+ ```typescript
56
+ interface McpServerOptions {
57
+ name: string; // Server name for MCP handshake
58
+ version: string; // Server version (semver)
59
+ logger?: Logger; // Optional structured logger
60
+ }
61
+
62
+ const server = createMcpServer({
63
+ name: "my-server",
64
+ version: "1.0.0",
65
+ logger: createLogger({ name: "mcp" }),
66
+ });
67
+ ```
68
+
69
+ ### defineTool(definition)
70
+
71
+ Helper for defining typed tools with better type inference.
72
+
73
+ ```typescript
74
+ interface ToolDefinition<TInput, TOutput, TError> {
75
+ name: string; // Unique tool name (kebab-case)
76
+ description: string; // Human-readable description
77
+ inputSchema: z.ZodType<TInput>; // Zod schema for validation
78
+ handler: Handler<TInput, TOutput, TError>;
79
+ deferLoading?: boolean; // Default: true
80
+ }
81
+
82
+ const getUserTool = defineTool({
83
+ name: "get-user",
84
+ description: "Retrieve a user by their unique ID",
85
+ inputSchema: z.object({ userId: z.string().uuid() }),
86
+ handler: async (input, ctx) => {
87
+ const user = await db.users.find(input.userId);
88
+ if (!user) {
89
+ return Result.err(new NotFoundError("user", input.userId));
90
+ }
91
+ return Result.ok(user);
92
+ },
93
+ });
94
+ ```
95
+
96
+ ### defineResource(definition)
97
+
98
+ Helper for defining MCP resources.
99
+
100
+ ```typescript
101
+ interface ResourceDefinition {
102
+ uri: string; // Unique resource URI
103
+ name: string; // Human-readable name
104
+ description?: string; // Optional description
105
+ mimeType?: string; // Content MIME type
106
+ }
107
+
108
+ const configResource = defineResource({
109
+ uri: "file:///etc/app/config.json",
110
+ name: "Application Config",
111
+ description: "Main configuration file",
112
+ mimeType: "application/json",
113
+ });
114
+ ```
115
+
116
+ ### Server Methods
117
+
118
+ ```typescript
119
+ interface McpServer {
120
+ readonly name: string;
121
+ readonly version: string;
122
+
123
+ // Registration
124
+ registerTool<TInput, TOutput, TError>(tool: ToolDefinition): void;
125
+ registerResource(resource: ResourceDefinition): void;
126
+
127
+ // Introspection
128
+ getTools(): SerializedTool[];
129
+ getResources(): ResourceDefinition[];
130
+
131
+ // Invocation
132
+ invokeTool<T>(name: string, input: unknown, options?: InvokeToolOptions): Promise<Result<T, McpError>>;
133
+
134
+ // Lifecycle
135
+ start(): Promise<void>;
136
+ stop(): Promise<void>;
137
+ }
138
+ ```
139
+
140
+ ### McpHandlerContext
141
+
142
+ Extended handler context for MCP tools with additional metadata:
143
+
144
+ ```typescript
145
+ interface McpHandlerContext extends HandlerContext {
146
+ toolName?: string; // Name of the tool being invoked
147
+ }
148
+ ```
149
+
150
+ ## Core Tools
151
+
152
+ Pre-built tools for common MCP patterns. These are marked with `deferLoading: false` for immediate availability.
153
+
154
+ ### Docs Tool
155
+
156
+ Provides documentation, usage patterns, and examples.
157
+
158
+ ```typescript
159
+ import { defineDocsTool, createCoreTools } from "@outfitter/mcp";
160
+
161
+ const docsTool = defineDocsTool({
162
+ docs: {
163
+ overview: "Calculator server for arithmetic operations",
164
+ tools: [{ name: "add", summary: "Add two numbers" }],
165
+ examples: [{ input: { a: 2, b: 3 }, description: "Basic addition" }],
166
+ },
167
+ });
168
+
169
+ // Or use getDocs for dynamic content
170
+ const dynamicDocsTool = defineDocsTool({
171
+ getDocs: async (section) => {
172
+ return loadDocsFromFile(section);
173
+ },
174
+ });
175
+ ```
176
+
177
+ ### Config Tool
178
+
179
+ Read and modify server configuration.
180
+
181
+ ```typescript
182
+ import { defineConfigTool } from "@outfitter/mcp";
183
+
184
+ const configTool = defineConfigTool({
185
+ initial: { debug: false, maxRetries: 3 },
186
+ });
187
+
188
+ // With custom store
189
+ const persistedConfigTool = defineConfigTool({
190
+ store: {
191
+ get: async (key) => db.config.get(key),
192
+ set: async (key, value) => db.config.set(key, value),
193
+ list: async () => db.config.all(),
194
+ },
195
+ });
196
+ ```
197
+
198
+ ### Query Tool
199
+
200
+ Search and discovery with pagination.
201
+
202
+ ```typescript
203
+ import { defineQueryTool } from "@outfitter/mcp";
204
+
205
+ const queryTool = defineQueryTool({
206
+ handler: async (input, ctx) => {
207
+ const results = await searchIndex(input.q, {
208
+ limit: input.limit,
209
+ cursor: input.cursor,
210
+ filters: input.filters,
211
+ });
212
+ return Result.ok({
213
+ results: results.items,
214
+ nextCursor: results.nextCursor,
215
+ });
216
+ },
217
+ });
218
+ ```
219
+
220
+ ### Bundle All Core Tools
221
+
222
+ ```typescript
223
+ import { createCoreTools } from "@outfitter/mcp";
224
+
225
+ const coreTools = createCoreTools({
226
+ docs: { docs: myDocs },
227
+ config: { initial: myConfig },
228
+ query: { handler: myQueryHandler },
229
+ });
230
+
231
+ for (const tool of coreTools) {
232
+ server.registerTool(tool);
233
+ }
234
+ ```
235
+
236
+ ## Transport Helpers
237
+
238
+ ### connectStdio
239
+
240
+ Connect server to stdio transport for Claude Desktop integration.
241
+
242
+ ```typescript
243
+ import { createMcpServer, connectStdio } from "@outfitter/mcp";
244
+
245
+ const server = createMcpServer({ name: "my-server", version: "1.0.0" });
246
+ // ... register tools ...
247
+
248
+ await connectStdio(server);
249
+ ```
250
+
251
+ ### createSdkServer
252
+
253
+ Create the underlying `@modelcontextprotocol/sdk` server.
254
+
255
+ ```typescript
256
+ import { createSdkServer } from "@outfitter/mcp";
257
+
258
+ const { server: sdkServer, toolsList, callTool } = createSdkServer(mcpServer);
259
+ ```
260
+
261
+ ## Error Handling
262
+
263
+ Tools return Results with typed errors. The framework automatically translates `OutfitterError` categories to JSON-RPC error codes:
264
+
265
+ | Category | JSON-RPC Code | Description |
266
+ |----------|--------------|-------------|
267
+ | `validation` | -32602 | Invalid params |
268
+ | `not_found` | -32601 | Method not found |
269
+ | `permission` | -32600 | Invalid request |
270
+ | `internal` | -32603 | Internal error |
271
+
272
+ ```typescript
273
+ const result = await server.invokeTool("get-user", { userId: "123" });
274
+
275
+ if (result.isErr()) {
276
+ // result.error is McpError with code and context
277
+ console.error(result.error.message, result.error.code);
278
+ }
279
+ ```
280
+
281
+ ## Schema Utilities
282
+
283
+ ### zodToJsonSchema
284
+
285
+ Convert Zod schemas to JSON Schema for MCP protocol.
286
+
287
+ ```typescript
288
+ import { zodToJsonSchema } from "@outfitter/mcp";
289
+
290
+ const schema = z.object({
291
+ name: z.string(),
292
+ age: z.number().optional(),
293
+ });
294
+
295
+ const jsonSchema = zodToJsonSchema(schema);
296
+ // { type: "object", properties: { name: { type: "string" }, ... } }
297
+ ```
298
+
299
+ ## Action Adapter
300
+
301
+ ### buildMcpTools
302
+
303
+ Build MCP tools from an action registry (for structured action-based servers).
304
+
305
+ ```typescript
306
+ import { buildMcpTools } from "@outfitter/mcp";
307
+
308
+ const tools = buildMcpTools({
309
+ actions: myActionRegistry,
310
+ prefix: "myapp",
311
+ });
312
+
313
+ for (const tool of tools) {
314
+ server.registerTool(tool);
315
+ }
316
+ ```
317
+
318
+ ## Claude Desktop Configuration
319
+
320
+ Add your MCP server to Claude Desktop:
321
+
322
+ ```json
323
+ {
324
+ "mcpServers": {
325
+ "my-server": {
326
+ "command": "bun",
327
+ "args": ["run", "/path/to/server.ts"]
328
+ }
329
+ }
330
+ }
331
+ ```
332
+
333
+ Config location:
334
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
335
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
336
+ - Linux: `~/.config/claude/claude_desktop_config.json`
337
+
338
+ ## Related Packages
339
+
340
+ - [@outfitter/contracts](../contracts/README.md) — Result types and error taxonomy
341
+ - [@outfitter/logging](../logging/README.md) — Structured logging
342
+ - [@outfitter/config](../config/README.md) — Configuration loading