@outfitter/mcp 0.2.0 → 0.4.0

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.
@@ -0,0 +1,165 @@
1
+ // @bun
2
+ // packages/mcp/src/transport.ts
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ CompleteRequestSchema,
8
+ GetPromptRequestSchema,
9
+ ListPromptsRequestSchema,
10
+ ListResourcesRequestSchema,
11
+ ListResourceTemplatesRequestSchema,
12
+ ListToolsRequestSchema,
13
+ ReadResourceRequestSchema,
14
+ McpError as SdkMcpError,
15
+ SetLevelRequestSchema,
16
+ SubscribeRequestSchema,
17
+ UnsubscribeRequestSchema
18
+ } from "@modelcontextprotocol/sdk/types.js";
19
+ import { safeStringify } from "@outfitter/contracts";
20
+ function isMcpToolResponse(value) {
21
+ if (!value || typeof value !== "object") {
22
+ return false;
23
+ }
24
+ const content = value.content;
25
+ return Array.isArray(content);
26
+ }
27
+ function toTextPayload(value) {
28
+ if (typeof value === "string") {
29
+ return value;
30
+ }
31
+ return safeStringify(value);
32
+ }
33
+ function serializeError(error) {
34
+ if (error && typeof error === "object") {
35
+ const record = error;
36
+ return {
37
+ _tag: record._tag ?? "McpError",
38
+ message: record.message ?? "Unknown error",
39
+ code: record.code,
40
+ context: record.context
41
+ };
42
+ }
43
+ return {
44
+ _tag: "McpError",
45
+ message: String(error)
46
+ };
47
+ }
48
+ function wrapToolResult(value) {
49
+ if (isMcpToolResponse(value)) {
50
+ return value;
51
+ }
52
+ const structuredContent = value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
53
+ return {
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: toTextPayload(value)
58
+ }
59
+ ],
60
+ ...structuredContent ? { structuredContent } : {}
61
+ };
62
+ }
63
+ function wrapToolError(error) {
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: toTextPayload(serializeError(error))
69
+ }
70
+ ],
71
+ isError: true
72
+ };
73
+ }
74
+ function toSdkError(error) {
75
+ return new SdkMcpError(error.code, error.message, error.context);
76
+ }
77
+ function createSdkServer(server) {
78
+ const capabilities = {
79
+ tools: { listChanged: true },
80
+ resources: { listChanged: true, subscribe: true },
81
+ prompts: { listChanged: true },
82
+ completions: {},
83
+ logging: {}
84
+ };
85
+ const sdkServer = new Server({ name: server.name, version: server.version }, { capabilities });
86
+ sdkServer.setRequestHandler(ListToolsRequestSchema, async () => ({
87
+ tools: server.getTools()
88
+ }));
89
+ sdkServer.setRequestHandler(CallToolRequestSchema, async (request) => {
90
+ const { name, arguments: args } = request.params;
91
+ const progressToken = request.params._meta?.progressToken;
92
+ const options = progressToken !== undefined ? { progressToken } : undefined;
93
+ const result = await server.invokeTool(name, args ?? {}, options);
94
+ if (result.isErr()) {
95
+ return wrapToolError(result.error);
96
+ }
97
+ return wrapToolResult(result.value);
98
+ });
99
+ sdkServer.setRequestHandler(ListResourcesRequestSchema, async () => ({
100
+ resources: server.getResources().map((r) => ({
101
+ uri: r.uri,
102
+ name: r.name,
103
+ ...r.description ? { description: r.description } : {},
104
+ ...r.mimeType ? { mimeType: r.mimeType } : {}
105
+ }))
106
+ }));
107
+ sdkServer.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
108
+ resourceTemplates: server.getResourceTemplates().map((t) => ({
109
+ uriTemplate: t.uriTemplate,
110
+ name: t.name,
111
+ ...t.description ? { description: t.description } : {},
112
+ ...t.mimeType ? { mimeType: t.mimeType } : {}
113
+ }))
114
+ }));
115
+ sdkServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
116
+ const { uri } = request.params;
117
+ const result = await server.readResource(uri);
118
+ if (result.isErr()) {
119
+ throw toSdkError(result.error);
120
+ }
121
+ return { contents: result.value };
122
+ });
123
+ sdkServer.setRequestHandler(SubscribeRequestSchema, async (request) => {
124
+ server.subscribe(request.params.uri);
125
+ return {};
126
+ });
127
+ sdkServer.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
128
+ server.unsubscribe(request.params.uri);
129
+ return {};
130
+ });
131
+ sdkServer.setRequestHandler(ListPromptsRequestSchema, async () => ({
132
+ prompts: server.getPrompts()
133
+ }));
134
+ sdkServer.setRequestHandler(GetPromptRequestSchema, async (request) => {
135
+ const { name, arguments: args } = request.params;
136
+ const result = await server.getPrompt(name, args ?? {});
137
+ if (result.isErr()) {
138
+ throw toSdkError(result.error);
139
+ }
140
+ return { ...result.value };
141
+ });
142
+ sdkServer.setRequestHandler(CompleteRequestSchema, async (request) => {
143
+ const { ref, argument } = request.params;
144
+ const completionRef = ref.type === "ref/prompt" ? { type: "ref/prompt", name: ref.name } : { type: "ref/resource", uri: ref.uri };
145
+ const result = await server.complete(completionRef, argument.name, argument.value);
146
+ if (result.isErr()) {
147
+ throw toSdkError(result.error);
148
+ }
149
+ return { completion: result.value };
150
+ });
151
+ sdkServer.setRequestHandler(SetLevelRequestSchema, async (request) => {
152
+ const level = request.params.level;
153
+ server.setLogLevel?.(level);
154
+ return {};
155
+ });
156
+ server.bindSdkServer?.(sdkServer);
157
+ return sdkServer;
158
+ }
159
+ async function connectStdio(server, transport = new StdioServerTransport) {
160
+ const sdkServer = createSdkServer(server);
161
+ await sdkServer.connect(transport);
162
+ return sdkServer;
163
+ }
164
+
165
+ export { wrapToolResult, wrapToolError, createSdkServer, connectStdio };
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * JSON Schema representation.
4
+ */
5
+ interface JsonSchema {
6
+ type?: string;
7
+ properties?: Record<string, JsonSchema>;
8
+ required?: string[];
9
+ items?: JsonSchema | JsonSchema[];
10
+ description?: string;
11
+ default?: unknown;
12
+ minimum?: number;
13
+ maximum?: number;
14
+ exclusiveMinimum?: number;
15
+ exclusiveMaximum?: number;
16
+ minLength?: number;
17
+ maxLength?: number;
18
+ pattern?: string;
19
+ format?: string;
20
+ enum?: unknown[];
21
+ const?: unknown;
22
+ anyOf?: JsonSchema[];
23
+ oneOf?: JsonSchema[];
24
+ allOf?: JsonSchema[];
25
+ not?: JsonSchema | Record<string, never>;
26
+ $ref?: string;
27
+ $schema?: string;
28
+ $defs?: Record<string, JsonSchema>;
29
+ definitions?: Record<string, JsonSchema>;
30
+ additionalProperties?: boolean | JsonSchema;
31
+ }
32
+ /**
33
+ * Convert a Zod schema to JSON Schema format.
34
+ *
35
+ * This is a simplified converter that handles common Zod types.
36
+ * For complex schemas, consider using a full zod-to-json-schema library.
37
+ *
38
+ * @param schema - Zod schema to convert
39
+ * @returns JSON Schema representation
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const zodSchema = z.object({
44
+ * name: z.string(),
45
+ * age: z.number().optional(),
46
+ * });
47
+ *
48
+ * const jsonSchema = zodToJsonSchema(zodSchema);
49
+ * // {
50
+ * // type: "object",
51
+ * // properties: {
52
+ * // name: { type: "string" },
53
+ * // age: { type: "number" },
54
+ * // },
55
+ * // required: ["name"],
56
+ * // }
57
+ * ```
58
+ */
59
+ declare function zodToJsonSchema(schema: z.ZodType<unknown>): JsonSchema;
60
+ export { JsonSchema, zodToJsonSchema };
@@ -0,0 +1,143 @@
1
+ // @bun
2
+ // packages/mcp/src/core-tools.ts
3
+ import { Result, ValidationError } from "@outfitter/contracts";
4
+ import { z } from "zod";
5
+ var DEFAULT_DOCS = {
6
+ overview: "No documentation configured yet.",
7
+ tools: [],
8
+ examples: [],
9
+ schemas: {}
10
+ };
11
+ var docsSchema = z.object({
12
+ section: z.enum(["overview", "tools", "examples", "schemas"]).optional()
13
+ });
14
+ function pickDocsSection(payload, section) {
15
+ if (!section) {
16
+ return payload;
17
+ }
18
+ return {
19
+ [section]: payload[section]
20
+ };
21
+ }
22
+ function defineDocsTool(options = {}) {
23
+ return {
24
+ name: "docs",
25
+ description: options.description ?? "Documentation, usage patterns, and examples for this MCP server.",
26
+ deferLoading: false,
27
+ inputSchema: docsSchema,
28
+ handler: async (input) => {
29
+ const payload = options.getDocs ? await options.getDocs(input.section) : options.docs ?? DEFAULT_DOCS;
30
+ return Result.ok(pickDocsSection(payload, input.section));
31
+ }
32
+ };
33
+ }
34
+ var configSchema = z.object({
35
+ action: z.enum(["get", "set", "list"]),
36
+ key: z.string().optional(),
37
+ value: z.unknown().optional()
38
+ });
39
+ function createInMemoryStore(initial = {}) {
40
+ const store = new Map(Object.entries(initial));
41
+ return {
42
+ get(key) {
43
+ return { value: store.get(key), found: store.has(key) };
44
+ },
45
+ set(key, value) {
46
+ store.set(key, value);
47
+ },
48
+ list() {
49
+ return Object.fromEntries(store.entries());
50
+ }
51
+ };
52
+ }
53
+ function defineConfigTool(options = {}) {
54
+ const store = options.store ?? createInMemoryStore(options.initial);
55
+ return {
56
+ name: "config",
57
+ description: options.description ?? "Read or modify server configuration values.",
58
+ deferLoading: false,
59
+ inputSchema: configSchema,
60
+ handler: async (input) => {
61
+ switch (input.action) {
62
+ case "list": {
63
+ const config = await store.list();
64
+ return Result.ok({ action: "list", config });
65
+ }
66
+ case "get": {
67
+ if (!input.key) {
68
+ return Result.err(new ValidationError({
69
+ message: "Config key is required for action 'get'.",
70
+ field: "key"
71
+ }));
72
+ }
73
+ const { value, found } = await store.get(input.key);
74
+ return Result.ok({ action: "get", key: input.key, value, found });
75
+ }
76
+ case "set": {
77
+ if (!input.key) {
78
+ return Result.err(new ValidationError({
79
+ message: "Config key is required for action 'set'.",
80
+ field: "key"
81
+ }));
82
+ }
83
+ await store.set(input.key, input.value);
84
+ return Result.ok({
85
+ action: "set",
86
+ key: input.key,
87
+ value: input.value
88
+ });
89
+ }
90
+ default:
91
+ return Result.err(new ValidationError({
92
+ message: `Unknown action: ${input.action}`,
93
+ field: "action"
94
+ }));
95
+ }
96
+ }
97
+ };
98
+ }
99
+ var querySchema = z.object({
100
+ q: z.string().min(1).describe("Search query. Supports natural language or filter syntax.").optional(),
101
+ query: z.string().min(1).describe("Alias for q. Supports natural language or filter syntax.").optional(),
102
+ limit: z.number().int().positive().optional(),
103
+ cursor: z.string().optional(),
104
+ filters: z.record(z.string(), z.unknown()).optional()
105
+ }).refine((value) => {
106
+ const queryValue = (value.q ?? value.query)?.trim();
107
+ return typeof queryValue === "string" && queryValue.length > 0;
108
+ }, {
109
+ message: "Query is required.",
110
+ path: ["q"]
111
+ });
112
+ function defineQueryTool(options = {}) {
113
+ return {
114
+ name: "query",
115
+ description: options.description ?? "Search and discover resources with filters and pagination.",
116
+ deferLoading: false,
117
+ inputSchema: querySchema,
118
+ handler: (input, ctx) => {
119
+ const normalized = {
120
+ ...input,
121
+ q: (input.q ?? input.query ?? "").trim()
122
+ };
123
+ if (options.handler) {
124
+ return options.handler(normalized, ctx);
125
+ }
126
+ return Promise.resolve(Result.ok({
127
+ results: [],
128
+ _meta: {
129
+ note: "No query handler configured."
130
+ }
131
+ }));
132
+ }
133
+ };
134
+ }
135
+ function createCoreTools(options = {}) {
136
+ return [
137
+ defineDocsTool(options.docs),
138
+ defineConfigTool(options.config),
139
+ defineQueryTool(options.query)
140
+ ];
141
+ }
142
+
143
+ export { defineDocsTool, defineConfigTool, defineQueryTool, createCoreTools };
@@ -0,0 +1,4 @@
1
+ import { McpToolResponse, connectStdio, createSdkServer, wrapToolError, wrapToolResult } from "./shared/@outfitter/mcp-jk0ka9hw";
2
+ import "./shared/@outfitter/mcp-h2twz77x";
3
+ import "./shared/@outfitter/mcp-cqpyer9m";
4
+ export { wrapToolResult, wrapToolError, createSdkServer, connectStdio, McpToolResponse };
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ import {
3
+ connectStdio,
4
+ createSdkServer,
5
+ wrapToolError,
6
+ wrapToolResult
7
+ } from "./shared/@outfitter/mcp-mzky3ck8.js";
8
+ export {
9
+ wrapToolResult,
10
+ wrapToolError,
11
+ createSdkServer,
12
+ connectStdio
13
+ };
@@ -0,0 +1,3 @@
1
+ import { BlobResourceContent, CompletionHandler, CompletionRef, CompletionResult, ContentAnnotations, InvokeToolOptions, McpError, McpHandlerContext, McpServer, McpServerOptions, ProgressReporter, PromptArgument, PromptDefinition, PromptHandler, PromptMessage, PromptMessageContent, PromptResult, ResourceContent, ResourceDefinition, ResourceReadHandler, ResourceTemplateDefinition, ResourceTemplateReadHandler, Result, SerializedTool, TOOL_ANNOTATIONS, TaggedError, TextResourceContent, ToolAnnotations, ToolDefinition, adaptHandler } from "./shared/@outfitter/mcp-h2twz77x";
2
+ import "./shared/@outfitter/mcp-cqpyer9m";
3
+ export { adaptHandler, ToolDefinition, ToolAnnotations, TextResourceContent, TaggedError, TOOL_ANNOTATIONS, SerializedTool, Result, ResourceTemplateReadHandler, ResourceTemplateDefinition, ResourceReadHandler, ResourceDefinition, ResourceContent, PromptResult, PromptMessageContent, PromptMessage, PromptHandler, PromptDefinition, PromptArgument, ProgressReporter, McpServerOptions, McpServer, McpHandlerContext, McpError, InvokeToolOptions, ContentAnnotations, CompletionResult, CompletionRef, CompletionHandler, BlobResourceContent };
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ import {
3
+ McpError,
4
+ TOOL_ANNOTATIONS,
5
+ TaggedError,
6
+ adaptHandler
7
+ } from "./shared/@outfitter/mcp-9m5hs2z0.js";
8
+ export {
9
+ adaptHandler,
10
+ TaggedError,
11
+ TOOL_ANNOTATIONS,
12
+ McpError
13
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outfitter/mcp",
3
3
  "description": "MCP server framework with typed tools for Outfitter",
4
- "version": "0.2.0",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"
@@ -15,10 +15,29 @@
15
15
  "default": "./dist/index.js"
16
16
  }
17
17
  },
18
- "./types": {
18
+ "./actions": {
19
19
  "import": {
20
- "types": "./dist/types.d.ts",
21
- "default": "./dist/types.js"
20
+ "types": "./dist/actions.d.ts",
21
+ "default": "./dist/actions.js"
22
+ }
23
+ },
24
+ "./core-tools": {
25
+ "import": {
26
+ "types": "./dist/core-tools.d.ts",
27
+ "default": "./dist/core-tools.js"
28
+ }
29
+ },
30
+ "./logging": {
31
+ "import": {
32
+ "types": "./dist/logging.d.ts",
33
+ "default": "./dist/logging.js"
34
+ }
35
+ },
36
+ "./package.json": "./package.json",
37
+ "./schema": {
38
+ "import": {
39
+ "types": "./dist/schema.d.ts",
40
+ "default": "./dist/schema.js"
22
41
  }
23
42
  },
24
43
  "./server": {
@@ -27,7 +46,18 @@
27
46
  "default": "./dist/server.js"
28
47
  }
29
48
  },
30
- "./package.json": "./package.json"
49
+ "./transport": {
50
+ "import": {
51
+ "types": "./dist/transport.d.ts",
52
+ "default": "./dist/transport.js"
53
+ }
54
+ },
55
+ "./types": {
56
+ "import": {
57
+ "types": "./dist/types.d.ts",
58
+ "default": "./dist/types.js"
59
+ }
60
+ }
31
61
  },
32
62
  "sideEffects": false,
33
63
  "scripts": {
@@ -40,11 +70,17 @@
40
70
  },
41
71
  "dependencies": {
42
72
  "@modelcontextprotocol/sdk": "^1.12.1",
43
- "@outfitter/contracts": "0.2.0",
44
- "@outfitter/logging": "0.2.0",
45
73
  "zod": "^4.3.5"
46
74
  },
75
+ "peerDependencies": {
76
+ "@outfitter/config": ">=0.3.0",
77
+ "@outfitter/contracts": ">=0.2.0",
78
+ "@outfitter/logging": ">=0.3.0"
79
+ },
47
80
  "devDependencies": {
81
+ "@outfitter/config": "0.3.1",
82
+ "@outfitter/contracts": "0.3.0",
83
+ "@outfitter/logging": "0.4.0",
48
84
  "@types/bun": "latest",
49
85
  "typescript": "^5.8.0"
50
86
  },