@cat-factory/mcp-server 0.1.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,24 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { type CatFactoryMcpOptions } from './config.ts';
3
+ import { type CatFactoryTool } from './tools.generated.ts';
4
+ /** The npm version, stamped into the SDK's `User-Agent` so a deployment can attribute calls. */
5
+ export declare const MCP_SERVER_VERSION = "0.1.0";
6
+ /** The name this server reports to a host. */
7
+ export declare const MCP_SERVER_NAME = "cat-factory";
8
+ export interface CatFactoryMcpServer {
9
+ /** The MCP server, ready to `connect()` to a transport. */
10
+ server: Server;
11
+ /** The tools it exposes, after the group / read-only filters. */
12
+ tools: readonly CatFactoryTool[];
13
+ }
14
+ /**
15
+ * Build the MCP server for a deployment.
16
+ *
17
+ * The client is constructed HERE rather than taken as a parameter, and a test injects
18
+ * `options.fetch` instead of a whole client. A client parameter would let a caller hand this one
19
+ * pointed at a different deployment than the options describe, and would let a test pass one
20
+ * without the `User-Agent` below — which is exactly the property most worth pinning, since it
21
+ * only exists on the path a test would then not be exercising.
22
+ */
23
+ export declare function createCatFactoryMcpServer(options: CatFactoryMcpOptions): CatFactoryMcpServer;
24
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAElE,OAAO,EAAE,KAAK,oBAAoB,EAAe,MAAM,aAAa,CAAA;AAGpE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAgB7E,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,UAAU,CAAA;AAEzC,8CAA8C;AAC9C,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAwE5F"}
package/dist/server.js ADDED
@@ -0,0 +1,96 @@
1
+ import { CatFactoryClient } from '@cat-factory/sdk';
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { selectTools } from './config.js';
5
+ import { buildInstructions } from './instructions.js';
6
+ import { renderError, renderResult } from './result.js';
7
+ import { CAT_FACTORY_TOOLS } from './tools.generated.js';
8
+ // The facade itself: an MCP server whose every tool is one call on `@cat-factory/sdk`.
9
+ //
10
+ // "Thin" is a design constraint rather than a description. This module decides which tools to
11
+ // list, how to turn one result or one failure into MCP content, and nothing else. Retries, auth,
12
+ // error classes, pagination, encoding and timeouts are the SDK's, which is what makes the tools
13
+ // behave identically to the same calls made from code — and what stops this becoming a second,
14
+ // quietly divergent implementation of the API's rules.
15
+ //
16
+ // The LOW-LEVEL `Server` rather than `McpServer`: the tool schemas are GENERATED as JSON Schema
17
+ // from the same spec the deployment validates against, and the high-level helper wants Zod
18
+ // schemas it would then convert back. Going through Zod would mean the model reads a schema that
19
+ // has been round-tripped through a second type system, and every gap in that conversion becomes a
20
+ // tool that misdescribes its own input.
21
+ /** The npm version, stamped into the SDK's `User-Agent` so a deployment can attribute calls. */
22
+ export const MCP_SERVER_VERSION = '0.1.0';
23
+ /** The name this server reports to a host. */
24
+ export const MCP_SERVER_NAME = 'cat-factory';
25
+ /**
26
+ * Build the MCP server for a deployment.
27
+ *
28
+ * The client is constructed HERE rather than taken as a parameter, and a test injects
29
+ * `options.fetch` instead of a whole client. A client parameter would let a caller hand this one
30
+ * pointed at a different deployment than the options describe, and would let a test pass one
31
+ * without the `User-Agent` below — which is exactly the property most worth pinning, since it
32
+ * only exists on the path a test would then not be exercising.
33
+ */
34
+ export function createCatFactoryMcpServer(options) {
35
+ const selection = selectTools(CAT_FACTORY_TOOLS, options);
36
+ const { exposed } = selection;
37
+ const client = new CatFactoryClient({
38
+ baseUrl: options.baseUrl,
39
+ apiKey: options.apiKey,
40
+ // Named so a deployment's request log attributes a call to the MCP facade rather than to
41
+ // "some SDK user": the two have very different debugging stories, and an operator reading
42
+ // an audit trail is entitled to know a model made the call.
43
+ userAgent: `cat-factory-mcp/${MCP_SERVER_VERSION}`,
44
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
45
+ ...(options.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
46
+ ...(options.fetch ? { fetch: options.fetch } : {}),
47
+ });
48
+ const server = new Server({ name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION }, {
49
+ capabilities: { tools: {} },
50
+ instructions: buildInstructions(selection),
51
+ });
52
+ server.setRequestHandler(ListToolsRequestSchema, () => ({
53
+ tools: exposed.map((tool) => ({
54
+ name: tool.name,
55
+ title: tool.title,
56
+ description: tool.description,
57
+ inputSchema: tool.inputSchema,
58
+ annotations: {
59
+ title: tool.title,
60
+ // The only annotation this facade can state truthfully. `readOnlyHint` follows from the
61
+ // HTTP method; `destructiveHint` and `idempotentHint` are deliberately left unset rather
62
+ // than guessed, because a DELETE here is idempotent AND destructive and the defaults a
63
+ // host assumes for an unset hint are safer than a wrong one.
64
+ readOnlyHint: tool.readOnly,
65
+ },
66
+ })),
67
+ }));
68
+ const byName = new Map(exposed.map((tool) => [tool.name, tool]));
69
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
70
+ const name = request.params.name;
71
+ const tool = byName.get(name);
72
+ if (!tool) {
73
+ // A name this server does not serve. Answered as a tool ERROR rather than a protocol one,
74
+ // and phrased so the model can recover: on a filtered server the tool may genuinely exist
75
+ // in the deployment, just not here.
76
+ return renderError(new Error(`no such tool on this server. Available: ${[...byName.keys()].sort().join(', ')}`), { toolName: name });
77
+ }
78
+ const args = (request.params.arguments ?? {});
79
+ try {
80
+ const result = await tool.invoke(client, args);
81
+ return renderResult(result, {
82
+ toolName: tool.name,
83
+ ...(options.maxResultChars !== undefined ? { maxChars: options.maxResultChars } : {}),
84
+ });
85
+ }
86
+ catch (error) {
87
+ // Every failure lands here, including one thrown while building the request. Nothing is
88
+ // rethrown: a throw out of this handler becomes a JSON-RPC protocol error, which the host
89
+ // treats as the server misbehaving and does not show the model — and the most useful thing
90
+ // this facade ever returns is a 422 naming the field the model got wrong.
91
+ return renderError(error, { toolName: tool.name });
92
+ }
93
+ });
94
+ return { server, tools: exposed };
95
+ }
96
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { CatFactoryClient } from '@cat-factory/sdk';
2
+ /** One exposed operation, as an MCP tool. */
3
+ export interface CatFactoryTool {
4
+ /** The name a host lists and a model calls, `<group>_<method>`. */
5
+ name: string;
6
+ /** One-line label from the spec's `summary`. */
7
+ title: string;
8
+ /** The SDK resource group it belongs to, so a deployment can expose a subset. */
9
+ group: string;
10
+ /** The spec's `operationId`, for correlating a tool call with a deployment's logs. */
11
+ operationId: string;
12
+ /** Whether the call changes nothing (a GET). Hosts use it to decide what needs confirming. */
13
+ readOnly: boolean;
14
+ description: string;
15
+ inputSchema: {
16
+ type: 'object';
17
+ properties: Record<string, unknown>;
18
+ required?: readonly string[];
19
+ additionalProperties: false;
20
+ };
21
+ /** Forward the validated arguments to the SDK. */
22
+ invoke: (client: CatFactoryClient, args: Record<string, unknown>) => Promise<unknown>;
23
+ }
24
+ /**
25
+ * An operation the facade deliberately does NOT expose, and why.
26
+ *
27
+ * Reported rather than merely absent: a caller comparing the tool list against the published API
28
+ * would otherwise have to guess whether a missing operation is an omission, an oversight, or a
29
+ * version skew. The server exposes this list through its instructions for the same reason.
30
+ */
31
+ export interface CatFactoryOmittedOperation {
32
+ operationId: string;
33
+ /** The route it would have called. */
34
+ route: string;
35
+ /** The equivalent call on the TypeScript SDK, which CAN serve it. */
36
+ sdkCall: string;
37
+ reason: string;
38
+ }
39
+ /** Every `/api/v1` operation this facade exposes as a tool, in resource-group order. */
40
+ export declare const CAT_FACTORY_TOOLS: readonly CatFactoryTool[];
41
+ /** The operations this facade does not expose, each with its reason. */
42
+ export declare const CAT_FACTORY_OMITTED_OPERATIONS: readonly CatFactoryOmittedOperation[];
43
+ /** One line per resource group, for the server's instructions. */
44
+ export declare const CAT_FACTORY_TOOL_GROUPS: Readonly<Record<string, string>>;
45
+ //# sourceMappingURL=tools.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.generated.d.ts","sourceRoot":"","sources":["../src/tools.generated.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAA;IACnB,8FAA8F;IAC9F,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAA;QACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACnC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QAC5B,oBAAoB,EAAE,KAAK,CAAA;KAC5B,CAAA;IACD,kDAAkD;IAClD,MAAM,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACtF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAA;IACnB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAwCD,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAyWtD,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,8BAA8B,EAAE,SAAS,0BAA0B,EAa/E,CAAA;AAED,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASpE,CAAA"}