@x12i/api-hub-mcp 0.0.0-stage → 0.1.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 CHANGED
@@ -1,3 +1,11 @@
1
- # Temporary Holding Version
1
+ # @x12i/api-hub-mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ MCP server for the x12i API hub
4
+
5
+ ESM package for Node.js 24 or newer. Import the public name:
6
+
7
+ ```js
8
+ import * as mcp from '@x12i/api-hub-mcp';
9
+ ```
10
+
11
+ The whole set is also available from [`@x12i/api-hub`](https://www.npmjs.com/package/@x12i/api-hub).
package/dist/http.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/http.js ADDED
@@ -0,0 +1,50 @@
1
+ import { createServer } from 'node:http';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import { createRuntime, createServicesFromEnv, loadDotEnv } from '@x12i/api-hub-runtime';
4
+ import { createMcpServer } from "./server.js";
5
+ loadDotEnv();
6
+ const port = Number(process.env.MCP_PORT ?? 8081);
7
+ const maxBody = 25 * 1024 * 1024;
8
+ const services = createServicesFromEnv();
9
+ const runtime = await createRuntime({ services: services.context, jobs: services.jobs, log: services.log, connections: services.connections });
10
+ async function readJson(req) {
11
+ const chunks = [];
12
+ let size = 0;
13
+ for await (const chunk of req) {
14
+ size += chunk.length;
15
+ if (size > maxBody)
16
+ throw new Error('body too large');
17
+ chunks.push(chunk);
18
+ }
19
+ return chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : undefined;
20
+ }
21
+ // Stateless streamable HTTP: one server and transport per request.
22
+ const httpServer = createServer(async (req, res) => {
23
+ if (req.url !== '/mcp') {
24
+ res.writeHead(404).end();
25
+ return;
26
+ }
27
+ if (req.method !== 'POST') {
28
+ res.writeHead(405, { allow: 'POST' }).end();
29
+ return;
30
+ }
31
+ try {
32
+ const body = await readJson(req);
33
+ const server = createMcpServer({ runtime });
34
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
35
+ res.on('close', () => {
36
+ void transport.close();
37
+ void server.close();
38
+ });
39
+ await server.connect(transport);
40
+ await transport.handleRequest(req, res, body);
41
+ }
42
+ catch {
43
+ if (!res.headersSent) {
44
+ res.writeHead(400, { 'content-type': 'application/json' });
45
+ res.end(JSON.stringify({ error: { code: 'invalid_input', message: 'Invalid MCP request' } }));
46
+ }
47
+ }
48
+ });
49
+ httpServer.listen(port, () => runtime.log.info('mcp http listening', { port }));
50
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../../../apps/mcp/src/http.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { createRuntime, createServicesFromEnv, loadDotEnv } from '@x12i/api-hub-runtime';\nimport { createMcpServer } from './server.ts';\n\nloadDotEnv();\nconst port = Number(process.env.MCP_PORT ?? 8081);\nconst maxBody = 25 * 1024 * 1024;\nconst services = createServicesFromEnv();\nconst runtime = await createRuntime({ services: services.context, jobs: services.jobs, log: services.log, connections: services.connections });\n\nasync function readJson(req: import('node:http').IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of req) {\n size += (chunk as Buffer).length;\n if (size > maxBody) throw new Error('body too large');\n chunks.push(chunk as Buffer);\n }\n return chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : undefined;\n}\n\n// Stateless streamable HTTP: one server and transport per request.\nconst httpServer = createServer(async (req, res) => {\n if (req.url !== '/mcp') {\n res.writeHead(404).end();\n return;\n }\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'POST' }).end();\n return;\n }\n try {\n const body = await readJson(req);\n const server = createMcpServer({ runtime });\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });\n res.on('close', () => {\n void transport.close();\n void server.close();\n });\n await server.connect(transport);\n await transport.handleRequest(req, res, body);\n } catch {\n if (!res.headersSent) {\n res.writeHead(400, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ error: { code: 'invalid_input', message: 'Invalid MCP request' } }));\n }\n }\n});\n\nhttpServer.listen(port, () => runtime.log.info('mcp http listening', { port }));\n"]}
@@ -0,0 +1,10 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { type Tool } from '@modelcontextprotocol/sdk/types.js';
3
+ import { type AuthContext } from '@x12i/api-hub-core';
4
+ import type { Runtime } from '@x12i/api-hub-runtime';
5
+ export interface McpOptions {
6
+ runtime: Runtime;
7
+ auth?: AuthContext;
8
+ }
9
+ export declare function capabilityTools(runtime: Runtime): Tool[];
10
+ export declare function createMcpServer(options: McpOptions): Server;
package/dist/server.js ADDED
@@ -0,0 +1,105 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
+ import { z } from 'zod';
4
+ import { anonymousAuth, HubError, SOURCINGS, STATUSES, toJsonSchema } from '@x12i/api-hub-core';
5
+ const searchInput = z.object({
6
+ q: z.string().max(200).optional().describe('Words to match against capability slugs, names and categories'),
7
+ status: z.array(z.enum(STATUSES)).optional().describe('Only these statuses'),
8
+ sourcing: z.array(z.enum(SOURCINGS)).optional().describe('Only these sourcing labels'),
9
+ connected: z.boolean().optional().describe('true: only served capabilities that can answer now; false: served but waiting for a provider or hub'),
10
+ unique: z.boolean().optional().describe('true hides slugs that are aliases of another capability'),
11
+ limit: z.number().int().min(1).max(5000).optional().describe('Page size; all matches when omitted'),
12
+ offset: z.number().int().min(0).optional(),
13
+ });
14
+ const describeInput = z.object({ slug: z.string().describe('Capability slug, e.g. dns-lookup') });
15
+ const callInput = z.object({
16
+ slug: z.string().describe('Capability slug'),
17
+ input: z.record(z.string(), z.unknown()).default({}).describe('Input object for the capability'),
18
+ });
19
+ function json(value) {
20
+ return {
21
+ content: [{ type: 'text', text: JSON.stringify(value) }],
22
+ structuredContent: value,
23
+ };
24
+ }
25
+ function errorResult(error) {
26
+ return { isError: true, content: [{ type: 'text', text: JSON.stringify(error.toBody()) }], structuredContent: error.toBody() };
27
+ }
28
+ function outcomeResult(outcome) {
29
+ if (outcome.kind === 'error')
30
+ return errorResult(outcome.error);
31
+ if (outcome.kind === 'job')
32
+ return json({ job: outcome.job, statusUrl: `/v1/jobs/${outcome.job.id}` });
33
+ return json(outcome.output);
34
+ }
35
+ const META_TOOLS = [
36
+ {
37
+ name: 'search_capabilities',
38
+ description: 'Search the full hub catalog (every listed capability with its status: live, planned, blocked-terms, connector, local-only, excluded). Pass unique: true to hide alias slugs.',
39
+ inputSchema: toJsonSchema(searchInput, 'input'),
40
+ },
41
+ {
42
+ name: 'describe_capability',
43
+ description: 'Describe one capability: status, whether it is connected and what is missing, sourcing, hubs that offer it, and input and output schemas when it is served.',
44
+ inputSchema: toJsonSchema(describeInput, 'input'),
45
+ },
46
+ {
47
+ name: 'list_connections',
48
+ description: 'List the providers and upstream hubs this server can use, whether each is connected, and how many capabilities each unlocks.',
49
+ inputSchema: { type: 'object', properties: {} },
50
+ },
51
+ {
52
+ name: 'call_capability',
53
+ description: 'Call any served capability by slug with an input object.',
54
+ inputSchema: toJsonSchema(callInput, 'input'),
55
+ },
56
+ ];
57
+ export function capabilityTools(runtime) {
58
+ return runtime.registry.servedCapabilities().map((cap) => ({
59
+ name: cap.slug,
60
+ description: cap.summary,
61
+ inputSchema: toJsonSchema(cap.input, 'input'),
62
+ ...(cap.mode === 'sync' ? { outputSchema: toJsonSchema(cap.output, 'output') } : {}),
63
+ }));
64
+ }
65
+ export function createMcpServer(options) {
66
+ const { runtime } = options;
67
+ const auth = options.auth ?? anonymousAuth;
68
+ const server = new Server({ name: 'x12i-api-hub', version: '0.1.0' }, { capabilities: { tools: {} } });
69
+ const capTools = capabilityTools(runtime);
70
+ const capToolNames = new Set(capTools.map((t) => t.name));
71
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...META_TOOLS, ...capTools] }));
72
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
73
+ const { name, arguments: args = {} } = request.params;
74
+ const invalid = (error) => errorResult(new HubError('invalid_input', 'Invalid tool arguments', { issues: error.issues.map((i) => i.message) }));
75
+ switch (name) {
76
+ case 'search_capabilities': {
77
+ const parsed = searchInput.safeParse(args);
78
+ if (!parsed.success)
79
+ return invalid(parsed.error);
80
+ return json(runtime.list(parsed.data));
81
+ }
82
+ case 'describe_capability': {
83
+ const parsed = describeInput.safeParse(args);
84
+ if (!parsed.success)
85
+ return invalid(parsed.error);
86
+ const described = runtime.describe(parsed.data.slug);
87
+ return described ? json(described) : errorResult(new HubError('unsupported', `Unknown capability: ${parsed.data.slug}`));
88
+ }
89
+ case 'list_connections':
90
+ return json({ items: runtime.connections() });
91
+ case 'call_capability': {
92
+ const parsed = callInput.safeParse(args);
93
+ if (!parsed.success)
94
+ return invalid(parsed.error);
95
+ return outcomeResult(await runtime.call(parsed.data.slug, parsed.data.input, { auth, signal: extra.signal }));
96
+ }
97
+ default:
98
+ if (!capToolNames.has(name))
99
+ return errorResult(new HubError('unsupported', `Unknown tool: ${name}`));
100
+ return outcomeResult(await runtime.call(name, args, { auth, signal: extra.signal }));
101
+ }
102
+ });
103
+ return server;
104
+ }
105
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../../apps/mcp/src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema, type CallToolResult, type Tool } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { anonymousAuth, HubError, SOURCINGS, STATUSES, toJsonSchema, type AuthContext } from '@x12i/api-hub-core';\nimport type { CallOutcome, Runtime } from '@x12i/api-hub-runtime';\n\nexport interface McpOptions {\n runtime: Runtime;\n // TODO(owner): the real auth layer supplies the caller identity per session.\n auth?: AuthContext;\n}\n\nconst searchInput = z.object({\n q: z.string().max(200).optional().describe('Words to match against capability slugs, names and categories'),\n status: z.array(z.enum(STATUSES)).optional().describe('Only these statuses'),\n sourcing: z.array(z.enum(SOURCINGS)).optional().describe('Only these sourcing labels'),\n connected: z.boolean().optional().describe('true: only served capabilities that can answer now; false: served but waiting for a provider or hub'),\n unique: z.boolean().optional().describe('true hides slugs that are aliases of another capability'),\n limit: z.number().int().min(1).max(5000).optional().describe('Page size; all matches when omitted'),\n offset: z.number().int().min(0).optional(),\n});\n\nconst describeInput = z.object({ slug: z.string().describe('Capability slug, e.g. dns-lookup') });\n\nconst callInput = z.object({\n slug: z.string().describe('Capability slug'),\n input: z.record(z.string(), z.unknown()).default({}).describe('Input object for the capability'),\n});\n\nfunction json(value: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value) }],\n structuredContent: value as Record<string, unknown>,\n };\n}\n\nfunction errorResult(error: HubError): CallToolResult {\n return { isError: true, content: [{ type: 'text', text: JSON.stringify(error.toBody()) }], structuredContent: error.toBody() as unknown as Record<string, unknown> };\n}\n\nfunction outcomeResult(outcome: CallOutcome): CallToolResult {\n if (outcome.kind === 'error') return errorResult(outcome.error);\n if (outcome.kind === 'job') return json({ job: outcome.job, statusUrl: `/v1/jobs/${outcome.job.id}` });\n return json(outcome.output as Record<string, unknown>);\n}\n\nconst META_TOOLS: Tool[] = [\n {\n name: 'search_capabilities',\n description: 'Search the full hub catalog (every listed capability with its status: live, planned, blocked-terms, connector, local-only, excluded). Pass unique: true to hide alias slugs.',\n inputSchema: toJsonSchema(searchInput, 'input') as Tool['inputSchema'],\n },\n {\n name: 'describe_capability',\n description:\n 'Describe one capability: status, whether it is connected and what is missing, sourcing, hubs that offer it, and input and output schemas when it is served.',\n inputSchema: toJsonSchema(describeInput, 'input') as Tool['inputSchema'],\n },\n {\n name: 'list_connections',\n description: 'List the providers and upstream hubs this server can use, whether each is connected, and how many capabilities each unlocks.',\n inputSchema: { type: 'object', properties: {} },\n },\n {\n name: 'call_capability',\n description: 'Call any served capability by slug with an input object.',\n inputSchema: toJsonSchema(callInput, 'input') as Tool['inputSchema'],\n },\n];\n\nexport function capabilityTools(runtime: Runtime): Tool[] {\n return runtime.registry.servedCapabilities().map((cap) => ({\n name: cap.slug,\n description: cap.summary,\n inputSchema: toJsonSchema(cap.input, 'input') as Tool['inputSchema'],\n ...(cap.mode === 'sync' ? { outputSchema: toJsonSchema(cap.output, 'output') as Tool['outputSchema'] } : {}),\n }));\n}\n\nexport function createMcpServer(options: McpOptions): Server {\n const { runtime } = options;\n const auth = options.auth ?? anonymousAuth;\n const server = new Server({ name: 'x12i-api-hub', version: '0.1.0' }, { capabilities: { tools: {} } });\n const capTools = capabilityTools(runtime);\n const capToolNames = new Set(capTools.map((t) => t.name));\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...META_TOOLS, ...capTools] }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request, extra): Promise<CallToolResult> => {\n const { name, arguments: args = {} } = request.params;\n const invalid = (error: z.ZodError) =>\n errorResult(new HubError('invalid_input', 'Invalid tool arguments', { issues: error.issues.map((i) => i.message) }));\n\n switch (name) {\n case 'search_capabilities': {\n const parsed = searchInput.safeParse(args);\n if (!parsed.success) return invalid(parsed.error);\n return json(runtime.list(parsed.data));\n }\n case 'describe_capability': {\n const parsed = describeInput.safeParse(args);\n if (!parsed.success) return invalid(parsed.error);\n const described = runtime.describe(parsed.data.slug);\n return described ? json(described) : errorResult(new HubError('unsupported', `Unknown capability: ${parsed.data.slug}`));\n }\n case 'list_connections':\n return json({ items: runtime.connections() });\n case 'call_capability': {\n const parsed = callInput.safeParse(args);\n if (!parsed.success) return invalid(parsed.error);\n return outcomeResult(await runtime.call(parsed.data.slug, parsed.data.input, { auth, signal: extra.signal }));\n }\n default:\n if (!capToolNames.has(name)) return errorResult(new HubError('unsupported', `Unknown tool: ${name}`));\n return outcomeResult(await runtime.call(name, args, { auth, signal: extra.signal }));\n }\n });\n\n return server;\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
package/dist/stdio.js ADDED
@@ -0,0 +1,12 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { createLogger } from '@x12i/api-hub-core';
3
+ import { createRuntime, createServicesFromEnv, loadDotEnv } from '@x12i/api-hub-runtime';
4
+ import { createMcpServer } from "./server.js";
5
+ loadDotEnv();
6
+ // stdout carries the MCP protocol, so logs go to stderr only.
7
+ const log = createLogger({ level: 'warn' });
8
+ const services = createServicesFromEnv(process.env, log);
9
+ const runtime = await createRuntime({ services: services.context, jobs: services.jobs, log, connections: services.connections });
10
+ const server = createMcpServer({ runtime });
11
+ await server.connect(new StdioServerTransport());
12
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../../../apps/mcp/src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,UAAU,EAAE,CAAC;AACb,8DAA8D;AAC9D,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAC5C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACzD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AACjI,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC","sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createLogger } from '@x12i/api-hub-core';\nimport { createRuntime, createServicesFromEnv, loadDotEnv } from '@x12i/api-hub-runtime';\nimport { createMcpServer } from './server.ts';\n\nloadDotEnv();\n// stdout carries the MCP protocol, so logs go to stderr only.\nconst log = createLogger({ level: 'warn' });\nconst services = createServicesFromEnv(process.env, log);\nconst runtime = await createRuntime({ services: services.context, jobs: services.jobs, log, connections: services.connections });\nconst server = createMcpServer({ runtime });\nawait server.connect(new StdioServerTransport());\n"]}
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@x12i/api-hub-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "MCP server for the x12i API hub",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "main": "./dist/server.js",
10
+ "types": "./dist/server.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/server.d.ts",
14
+ "default": "./dist/server.js"
15
+ },
16
+ "./stdio": {
17
+ "types": "./dist/stdio.d.ts",
18
+ "default": "./dist/stdio.js"
19
+ },
20
+ "./http": {
21
+ "types": "./dist/http.d.ts",
22
+ "default": "./dist/http.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/x12i/apis-hub.git",
32
+ "directory": "apps/mcp"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/x12i/apis-hub/issues"
36
+ },
37
+ "homepage": "https://github.com/x12i/apis-hub",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "@x12i/api-hub-core": "0.1.1",
43
+ "@x12i/api-hub-runtime": "0.1.1",
44
+ "@modelcontextprotocol/sdk": "1.32.2",
45
+ "zod": "4.6.7"
46
+ }
47
+ }