aui-mcp-server 0.0.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.
Files changed (62) hide show
  1. package/README.md +122 -0
  2. package/dist/cli.cjs +1088 -0
  3. package/dist/cli.cjs.map +1 -0
  4. package/dist/cli.d.cts +1 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1076 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/client/index.cjs +619 -0
  9. package/dist/client/index.cjs.map +1 -0
  10. package/dist/client/index.d.cts +194 -0
  11. package/dist/client/index.d.ts +194 -0
  12. package/dist/client/index.js +584 -0
  13. package/dist/client/index.js.map +1 -0
  14. package/dist/index.cjs +1053 -0
  15. package/dist/index.cjs.map +1 -0
  16. package/dist/index.d.cts +163 -0
  17. package/dist/index.d.ts +163 -0
  18. package/dist/index.js +1036 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/rsbuild.cjs +1049 -0
  21. package/dist/rsbuild.cjs.map +1 -0
  22. package/dist/rsbuild.d.cts +12 -0
  23. package/dist/rsbuild.d.ts +12 -0
  24. package/dist/rsbuild.js +1038 -0
  25. package/dist/rsbuild.js.map +1 -0
  26. package/dist/rspack.cjs +1016 -0
  27. package/dist/rspack.cjs.map +1 -0
  28. package/dist/rspack.d.cts +40 -0
  29. package/dist/rspack.d.ts +40 -0
  30. package/dist/rspack.js +1005 -0
  31. package/dist/rspack.js.map +1 -0
  32. package/dist/server.cjs +304 -0
  33. package/dist/server.cjs.map +1 -0
  34. package/dist/server.d.cts +16 -0
  35. package/dist/server.d.ts +16 -0
  36. package/dist/server.js +297 -0
  37. package/dist/server.js.map +1 -0
  38. package/package.json +72 -0
  39. package/src/catalog/build.ts +89 -0
  40. package/src/catalog/entry.ts +183 -0
  41. package/src/catalog/parser.ts +173 -0
  42. package/src/catalog/tool_parser.ts +145 -0
  43. package/src/cli.ts +318 -0
  44. package/src/client/handshake.ts +166 -0
  45. package/src/client/index.ts +6 -0
  46. package/src/client/registry.tsx +184 -0
  47. package/src/client/types.ts +136 -0
  48. package/src/client/useA2UIStream.ts +378 -0
  49. package/src/client/useLogger.ts +147 -0
  50. package/src/generator.ts +100 -0
  51. package/src/index.ts +17 -0
  52. package/src/mcp-app-poc.html +69 -0
  53. package/src/poc.ts +88 -0
  54. package/src/rsbuild.ts +46 -0
  55. package/src/rspack.ts +282 -0
  56. package/src/server.ts +391 -0
  57. package/src/templates.ts +51 -0
  58. package/src/types.ts +195 -0
  59. package/src/utils.ts +29 -0
  60. package/test.js +16 -0
  61. package/tsconfig.json +19 -0
  62. package/tsup.config.ts +27 -0
package/dist/server.js ADDED
@@ -0,0 +1,297 @@
1
+ import http from 'http';
2
+ import { promises } from 'fs';
3
+ import chokidar from 'chokidar';
4
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8
+ import { init, loadRemote } from '@module-federation/runtime';
9
+
10
+ /* aui-mcp-server */
11
+
12
+ async function readJsonFile(filePath) {
13
+ const raw = await promises.readFile(filePath, "utf-8");
14
+ return JSON.parse(raw);
15
+ }
16
+ function toSnakeCase(input) {
17
+ return input.trim().replace(/[\s-]+/g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/_+/g, "_").toLowerCase();
18
+ }
19
+ function toToolName(componentName) {
20
+ return `get_${toSnakeCase(componentName)}`;
21
+ }
22
+
23
+ // src/server.ts
24
+ var __mfGlobal = globalThis;
25
+ if (typeof __mfGlobal.self === "undefined") __mfGlobal.self = __mfGlobal;
26
+ if (typeof __mfGlobal.window === "undefined") __mfGlobal.window = __mfGlobal;
27
+ var EMPTY_INPUT_SCHEMA = {
28
+ type: "object",
29
+ properties: {},
30
+ required: []
31
+ };
32
+ async function loadManifestFromFile(manifestPath) {
33
+ const manifest = await readJsonFile(manifestPath);
34
+ if (!manifest || typeof manifest !== "object" || manifest.schemaVersion !== 1) {
35
+ throw new Error(`Invalid manifest: ${manifestPath}`);
36
+ }
37
+ return manifest;
38
+ }
39
+ async function loadManifestFromCatalog(catalogPath) {
40
+ const catalog = await readJsonFile(catalogPath);
41
+ if (!catalog || typeof catalog !== "object" || Array.isArray(catalog)) {
42
+ throw new Error(`Invalid aui-x-catalog.json: ${catalogPath}`);
43
+ }
44
+ return {
45
+ schemaVersion: 1,
46
+ tools: Object.entries(catalog).map(([componentName, entry]) => ({
47
+ name: toToolName(componentName),
48
+ componentName,
49
+ description: entry.description,
50
+ inputSchema: EMPTY_INPUT_SCHEMA,
51
+ entry
52
+ }))
53
+ };
54
+ }
55
+ function buildToolMap(manifest) {
56
+ const map = /* @__PURE__ */ new Map();
57
+ for (const tool of manifest.tools ?? []) {
58
+ map.set(tool.name, tool);
59
+ }
60
+ return map;
61
+ }
62
+ async function executeCatalogTool(manifest, toolName, args) {
63
+ const tools = buildToolMap(manifest);
64
+ const tool = tools.get(toolName);
65
+ if (!tool) {
66
+ return { ok: false, message: `Tool not found: ${toolName}` };
67
+ }
68
+ const entry = tool.entry;
69
+ const xLoader = entry["x-loader"];
70
+ const xTools = entry["x-tools"] ?? [];
71
+ if (!xLoader) {
72
+ return { ok: false, message: `No x-loader config for tool: ${toolName}` };
73
+ }
74
+ if (!xTools.length) {
75
+ return { ok: false, message: `No x-tools defined for component: ${tool.componentName}` };
76
+ }
77
+ const selectedTool = xTools.find((t) => t.name === toolName) ?? xTools[0];
78
+ if (!selectedTool) {
79
+ return { ok: false, message: `No matching x-tools entry for tool: ${toolName}` };
80
+ }
81
+ const loaderPath = selectedTool.loader.slice(2);
82
+ const logicFilePath = selectedTool.logicFilePath;
83
+ try {
84
+ const inputArgs = args && typeof args === "object" ? args : {};
85
+ if (loaderPath) {
86
+ const scope = xLoader.scope;
87
+ const remoteEntryUrl = xLoader.url;
88
+ try {
89
+ init({
90
+ name: "aui-mcp-mf-host",
91
+ remotes: [
92
+ {
93
+ name: scope,
94
+ entry: remoteEntryUrl
95
+ }
96
+ ]
97
+ });
98
+ } catch {
99
+ }
100
+ const remoteKey = `${scope}/${loaderPath}`;
101
+ const mod = await loadRemote(remoteKey);
102
+ const loaderFn = mod?.loader ?? mod?.default;
103
+ if (typeof loaderFn !== "function") {
104
+ return {
105
+ ok: false,
106
+ message: `Loaded MF module '${remoteKey}' does not export a callable loader`
107
+ };
108
+ }
109
+ const result = await loaderFn(inputArgs);
110
+ return { ok: true, value: result };
111
+ }
112
+ if (logicFilePath) {
113
+ const moduleUrl = new URL(`file://${logicFilePath}`);
114
+ const mod = await import(moduleUrl.href);
115
+ const loaderFn = mod?.loader ?? mod?.default;
116
+ if (typeof loaderFn !== "function") {
117
+ return {
118
+ ok: false,
119
+ message: `Local logic file '${logicFilePath}' does not export a callable loader`
120
+ };
121
+ }
122
+ const result = await loaderFn(inputArgs);
123
+ return { ok: true, value: result };
124
+ }
125
+ return { ok: false, message: `No executable loader found for tool: ${toolName}` };
126
+ } catch (error) {
127
+ console.error("[aui-mcp] tool execution failed:", error);
128
+ return {
129
+ ok: false,
130
+ message: error instanceof Error ? error.message : String(error)
131
+ };
132
+ }
133
+ }
134
+ async function createServer(getManifest) {
135
+ const server = new Server(
136
+ { name: "aui-x-catalog", version: "0.1.0" },
137
+ { capabilities: { tools: {} } }
138
+ );
139
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
140
+ const manifest = getManifest();
141
+ return {
142
+ tools: (manifest.tools ?? []).map((t) => ({
143
+ name: t.name,
144
+ description: t.description,
145
+ inputSchema: t.inputSchema ?? EMPTY_INPUT_SCHEMA
146
+ }))
147
+ };
148
+ });
149
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
150
+ const manifest = getManifest();
151
+ const exec = await executeCatalogTool(manifest, request.params.name, request.params.arguments);
152
+ if (!exec.ok) {
153
+ return {
154
+ content: [{ type: "text", text: exec.message }],
155
+ isError: true
156
+ };
157
+ }
158
+ return {
159
+ // Return raw loader result as JSON for the Agent to consume.
160
+ content: [{ type: "text", text: JSON.stringify(exec.value) }]
161
+ };
162
+ });
163
+ return server;
164
+ }
165
+ async function startWatch(filePath, onReload, onReloadError) {
166
+ await promises.access(filePath);
167
+ const watcher = chokidar.watch(filePath, { ignoreInitial: true });
168
+ watcher.on("change", async () => {
169
+ try {
170
+ await onReload();
171
+ } catch (err) {
172
+ onReloadError(err);
173
+ }
174
+ });
175
+ return watcher;
176
+ }
177
+ async function serveAuiMcpServer(options = {}) {
178
+ const transport = options.transport ?? "stdio";
179
+ const port = options.port ?? 8001;
180
+ const manifestPath = options.manifestPath;
181
+ const catalogPath = options.catalogPath;
182
+ if (!manifestPath && !catalogPath) {
183
+ throw new Error("Either manifestPath or catalogPath is required");
184
+ }
185
+ const resolvedCatalogPath = catalogPath ?? "";
186
+ let currentManifest = manifestPath ? await loadManifestFromFile(manifestPath) : await loadManifestFromCatalog(resolvedCatalogPath);
187
+ const getRawCatalog = () => {
188
+ const catalog = {};
189
+ for (const tool of currentManifest.tools ?? []) {
190
+ if (tool.entry) {
191
+ catalog[tool.componentName] = tool.entry;
192
+ }
193
+ }
194
+ return catalog;
195
+ };
196
+ const server = await createServer(() => currentManifest);
197
+ if (options.watch) {
198
+ const watchPath = manifestPath ?? resolvedCatalogPath;
199
+ await startWatch(
200
+ watchPath,
201
+ async () => {
202
+ currentManifest = manifestPath ? await loadManifestFromFile(manifestPath) : await loadManifestFromCatalog(resolvedCatalogPath);
203
+ await server.sendToolListChanged();
204
+ console.error(
205
+ `[aui-mcp] reloaded: ${(currentManifest.tools ?? []).length} tools from ${watchPath}`
206
+ );
207
+ },
208
+ (error) => {
209
+ console.error(`[aui-mcp] reload failed:`, error);
210
+ }
211
+ );
212
+ }
213
+ if (transport === "stdio") {
214
+ await server.connect(new StdioServerTransport());
215
+ return;
216
+ }
217
+ const transports = /* @__PURE__ */ new Map();
218
+ const httpServer = http.createServer(async (req, res) => {
219
+ try {
220
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
221
+ if (req.method === "GET" && url.pathname === "/catalog") {
222
+ res.writeHead(200, { "Content-Type": "application/json" });
223
+ res.end(JSON.stringify(getRawCatalog(), null, 2));
224
+ return;
225
+ }
226
+ if (req.method === "POST" && url.pathname === "/call-tool") {
227
+ const chunks = [];
228
+ for await (const chunk of req) {
229
+ chunks.push(chunk);
230
+ }
231
+ let payload = {};
232
+ try {
233
+ const raw = Buffer.concat(chunks).toString("utf-8");
234
+ payload = raw ? JSON.parse(raw) : {};
235
+ } catch (error) {
236
+ console.error("[aui-mcp] invalid /call-tool payload:", error);
237
+ res.writeHead(400, { "Content-Type": "application/json" });
238
+ res.end(JSON.stringify({ error: "Invalid JSON payload" }));
239
+ return;
240
+ }
241
+ const name = payload.name;
242
+ const args = payload.arguments ?? payload.args ?? {};
243
+ if (!name || typeof name !== "string") {
244
+ res.writeHead(400, { "Content-Type": "application/json" });
245
+ res.end(JSON.stringify({ error: "Missing tool name" }));
246
+ return;
247
+ }
248
+ const exec = await executeCatalogTool(currentManifest, name, args);
249
+ if (!exec.ok) {
250
+ res.writeHead(400, { "Content-Type": "application/json" });
251
+ res.end(JSON.stringify({ error: exec.message }));
252
+ return;
253
+ }
254
+ res.writeHead(200, { "Content-Type": "application/json" });
255
+ res.end(JSON.stringify(exec.value));
256
+ return;
257
+ }
258
+ if (req.method === "GET" && url.pathname === "/sse") {
259
+ const transportInstance = new SSEServerTransport("/message", res);
260
+ transports.set(transportInstance.sessionId, transportInstance);
261
+ req.on("close", () => {
262
+ transports.delete(transportInstance.sessionId);
263
+ });
264
+ await server.connect(transportInstance);
265
+ return;
266
+ }
267
+ if (req.method === "POST" && url.pathname === "/message") {
268
+ const sessionId = url.searchParams.get("sessionId");
269
+ if (!sessionId) {
270
+ res.writeHead(400).end("Missing sessionId");
271
+ return;
272
+ }
273
+ const transportInstance = transports.get(sessionId);
274
+ if (!transportInstance) {
275
+ res.writeHead(404).end("Unknown sessionId");
276
+ return;
277
+ }
278
+ await transportInstance.handlePostMessage(req, res);
279
+ return;
280
+ }
281
+ res.writeHead(404).end("Not found");
282
+ } catch (error) {
283
+ console.error("[aui-mcp] request handler error:", error);
284
+ res.writeHead(500).end("Internal server error");
285
+ }
286
+ });
287
+ httpServer.on("error", (error) => {
288
+ console.error("[aui-mcp] http server error:", error);
289
+ });
290
+ httpServer.listen(port, () => {
291
+ console.error(`[aui-mcp] SSE listening on http://localhost:${port}/sse`);
292
+ });
293
+ }
294
+
295
+ export { serveAuiMcpServer };
296
+ //# sourceMappingURL=server.js.map
297
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/server.ts"],"names":["fs"],"mappings":"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","file":"server.js","sourcesContent":["import { promises as fs } from 'node:fs';\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n const raw = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(raw) as T;\n}\n\nexport function toSnakeCase(input: string): string {\n return input\n .trim()\n .replace(/[\\s-]+/g, '_')\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n .replace(/_+/g, '_')\n .toLowerCase();\n}\n\nexport function toToolName(componentName: string): string {\n return `get_${toSnakeCase(componentName)}`;\n}\n","import http from 'node:http';\nimport { promises as fs } from 'node:fs';\n\nimport chokidar from 'chokidar';\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { init, loadRemote } from '@module-federation/runtime';\n\n// In Node.js there is no `self` global by default, but many MF remoteEntry\n// bundles still reference it (especially browser-first builds). Polyfill it so\n// MF runtime can eval remoteEntry safely.\nconst __mfGlobal = globalThis as any;\nif (typeof __mfGlobal.self === 'undefined') __mfGlobal.self = __mfGlobal;\nif (typeof __mfGlobal.window === 'undefined') __mfGlobal.window = __mfGlobal;\n\nimport type { AuiMcpManifest, AuiXCatalog, AuiXToolDefinition } from './types';\nimport { readJsonFile, toToolName } from './utils';\n\nexport type TransportType = 'stdio' | 'sse';\n\nexport interface ServeAuiMcpServerOptions {\n /** If provided, server will load tools from manifest.json (recommended for prod) */\n manifestPath?: string;\n /** If provided, server will derive tools from aui-x-catalog.json (recommended for dev) */\n catalogPath?: string;\n /** Enable hot reload for manifest/catalog */\n watch?: boolean;\n /** Transport type */\n transport?: TransportType;\n /** SSE port (only for transport=sse) */\n port?: number;\n}\n\nconst EMPTY_INPUT_SCHEMA: Tool['inputSchema'] = {\n type: 'object',\n properties: {},\n required: [],\n};\n\nasync function loadManifestFromFile(manifestPath: string): Promise<AuiMcpManifest> {\n const manifest = await readJsonFile<AuiMcpManifest>(manifestPath);\n if (!manifest || typeof manifest !== 'object' || (manifest as any).schemaVersion !== 1) {\n throw new Error(`Invalid manifest: ${manifestPath}`);\n }\n return manifest;\n}\n\nasync function loadManifestFromCatalog(catalogPath: string): Promise<AuiMcpManifest> {\n const catalog = await readJsonFile<AuiXCatalog>(catalogPath);\n if (!catalog || typeof catalog !== 'object' || Array.isArray(catalog)) {\n throw new Error(`Invalid aui-x-catalog.json: ${catalogPath}`);\n }\n\n return {\n schemaVersion: 1,\n tools: Object.entries(catalog).map(([componentName, entry]) => ({\n name: toToolName(componentName),\n componentName,\n description: entry.description,\n inputSchema: EMPTY_INPUT_SCHEMA,\n entry,\n })),\n };\n}\n\nfunction buildToolMap(manifest: AuiMcpManifest): Map<string, (typeof manifest.tools)[number]> {\n const map = new Map<string, (typeof manifest.tools)[number]>();\n for (const tool of manifest.tools ?? []) {\n map.set(tool.name, tool);\n }\n return map;\n}\n\nasync function executeCatalogTool(\n manifest: AuiMcpManifest,\n toolName: string,\n args: unknown\n): Promise<{ ok: true; value: unknown } | { ok: false; message: string }> {\n const tools = buildToolMap(manifest);\n const tool = tools.get(toolName);\n\n if (!tool) {\n return { ok: false, message: `Tool not found: ${toolName}` };\n }\n\n const entry = tool.entry;\n const xLoader = entry['x-loader'];\n const xTools = (entry['x-tools'] ?? []) as AuiXToolDefinition[];\n\n if (!xLoader) {\n return { ok: false, message: `No x-loader config for tool: ${toolName}` };\n }\n\n if (!xTools.length) {\n return { ok: false, message: `No x-tools defined for component: ${tool.componentName}` };\n }\n\n const selectedTool = xTools.find((t) => t.name === toolName) ?? xTools[0];\n\n if (!selectedTool) {\n return { ok: false, message: `No matching x-tools entry for tool: ${toolName}` };\n }\n\n // Prefer MF remote loader when loader path is present; otherwise fall back to local logic file.\n const loaderPath = selectedTool.loader.slice(2); // Remove \"./\" prefix \n const logicFilePath = selectedTool.logicFilePath;\n\n try {\n const inputArgs = (args && typeof args === 'object') ? (args as Record<string, unknown>) : {};\n\n // MF 2.0 remote loader path (e.g. \"./ArtistEvents.data\")\n if (loaderPath) {\n const scope = xLoader.scope;\n const remoteEntryUrl = xLoader.url;\n\n // Initialize MF runtime for this remote; repeated init calls are tolerated.\n try {\n init({\n name: 'aui-mcp-mf-host',\n remotes: [\n {\n name: scope,\n entry: remoteEntryUrl,\n },\n ],\n });\n } catch {\n // Ignore init errors (e.g. duplicate init); runtime will reuse existing instance.\n }\n\n const remoteKey = `${scope}/${loaderPath}`;\n const mod: any = await loadRemote(remoteKey);\n const loaderFn = mod?.loader ?? mod?.default;\n\n if (typeof loaderFn !== 'function') {\n return {\n ok: false,\n message: `Loaded MF module '${remoteKey}' does not export a callable loader`,\n };\n }\n\n const result = await loaderFn(inputArgs);\n return { ok: true, value: result };\n }\n\n // Fallback: execute local logic file directly (dev/non-MF mode)\n if (logicFilePath) {\n const moduleUrl = new URL(`file://${logicFilePath}`);\n const mod: any = await import(moduleUrl.href);\n const loaderFn = mod?.loader ?? mod?.default;\n\n if (typeof loaderFn !== 'function') {\n return {\n ok: false,\n message: `Local logic file '${logicFilePath}' does not export a callable loader`,\n };\n }\n\n const result = await loaderFn(inputArgs);\n return { ok: true, value: result };\n }\n\n return { ok: false, message: `No executable loader found for tool: ${toolName}` };\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[aui-mcp] tool execution failed:', error);\n return {\n ok: false,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nasync function createServer(getManifest: () => AuiMcpManifest): Promise<Server> {\n const server = new Server(\n { name: 'aui-x-catalog', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const manifest = getManifest();\n return {\n tools: (manifest.tools ?? []).map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema ?? EMPTY_INPUT_SCHEMA,\n })),\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const manifest = getManifest();\n const exec = await executeCatalogTool(manifest, request.params.name, request.params.arguments);\n\n if (!exec.ok) {\n return {\n content: [{ type: 'text' as const, text: exec.message }],\n isError: true,\n };\n }\n\n return {\n // Return raw loader result as JSON for the Agent to consume.\n content: [{ type: 'text' as const, text: JSON.stringify(exec.value) }],\n };\n });\n\n return server;\n}\n\nasync function startWatch(\n filePath: string,\n onReload: () => Promise<void>,\n onReloadError: (error: unknown) => void\n): Promise<chokidar.FSWatcher> {\n await fs.access(filePath);\n\n const watcher = chokidar.watch(filePath, { ignoreInitial: true });\n watcher.on('change', async () => {\n try {\n await onReload();\n } catch (err) {\n onReloadError(err);\n }\n });\n\n return watcher;\n}\n\nexport async function serveAuiMcpServer(options: ServeAuiMcpServerOptions = {}): Promise<void> {\n const transport: TransportType = options.transport ?? 'stdio';\n const port = options.port ?? 8001;\n\n const manifestPath = options.manifestPath;\n const catalogPath = options.catalogPath;\n\n if (!manifestPath && !catalogPath) {\n throw new Error('Either manifestPath or catalogPath is required');\n }\n\n const resolvedCatalogPath = catalogPath ?? '';\n let currentManifest: AuiMcpManifest = manifestPath\n ? await loadManifestFromFile(manifestPath)\n : await loadManifestFromCatalog(resolvedCatalogPath);\n\n const getRawCatalog = (): AuiXCatalog => {\n const catalog: AuiXCatalog = {};\n for (const tool of currentManifest.tools ?? []) {\n if (tool.entry) {\n catalog[tool.componentName] = tool.entry;\n }\n }\n return catalog;\n };\n\n const server = await createServer(() => currentManifest);\n\n if (options.watch) {\n const watchPath = manifestPath ?? resolvedCatalogPath;\n\n await startWatch(\n watchPath,\n async () => {\n currentManifest = manifestPath\n ? await loadManifestFromFile(manifestPath)\n : await loadManifestFromCatalog(resolvedCatalogPath);\n\n await server.sendToolListChanged();\n // eslint-disable-next-line no-console\n console.error(\n `[aui-mcp] reloaded: ${(currentManifest.tools ?? []).length} tools from ${watchPath}`\n );\n },\n (error) => {\n // eslint-disable-next-line no-console\n console.error(`[aui-mcp] reload failed:`, error);\n }\n );\n }\n\n if (transport === 'stdio') {\n await server.connect(new StdioServerTransport());\n return;\n }\n\n const transports = new Map<string, SSEServerTransport>();\n\n const httpServer = http.createServer(async (req, res) => {\n try {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n if (req.method === 'GET' && url.pathname === '/catalog') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(getRawCatalog(), null, 2));\n return;\n }\n\n if (req.method === 'POST' && url.pathname === '/call-tool') {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n\n let payload: any = {};\n try {\n const raw = Buffer.concat(chunks).toString('utf-8');\n payload = raw ? JSON.parse(raw) : {};\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[aui-mcp] invalid /call-tool payload:', error);\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Invalid JSON payload' }));\n return;\n }\n\n const name = payload.name as string | undefined;\n const args = payload.arguments ?? payload.args ?? {};\n\n if (!name || typeof name !== 'string') {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Missing tool name' }));\n return;\n }\n\n const exec = await executeCatalogTool(currentManifest, name, args);\n\n if (!exec.ok) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: exec.message }));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(exec.value));\n return;\n }\n\n if (req.method === 'GET' && url.pathname === '/sse') {\n const transportInstance = new SSEServerTransport('/message', res);\n transports.set(transportInstance.sessionId, transportInstance);\n req.on('close', () => {\n transports.delete(transportInstance.sessionId);\n });\n await server.connect(transportInstance);\n return;\n }\n\n if (req.method === 'POST' && url.pathname === '/message') {\n const sessionId = url.searchParams.get('sessionId');\n if (!sessionId) {\n res.writeHead(400).end('Missing sessionId');\n return;\n }\n\n const transportInstance = transports.get(sessionId);\n if (!transportInstance) {\n res.writeHead(404).end('Unknown sessionId');\n return;\n }\n\n await transportInstance.handlePostMessage(req, res);\n return;\n }\n\n res.writeHead(404).end('Not found');\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('[aui-mcp] request handler error:', error);\n res.writeHead(500).end('Internal server error');\n }\n });\n\n httpServer.on('error', (error) => {\n // Keep dev workflow resilient: don't crash the whole build process.\n // eslint-disable-next-line no-console\n console.error('[aui-mcp] http server error:', error);\n });\n\n httpServer.listen(port, () => {\n // eslint-disable-next-line no-console\n console.error(`[aui-mcp] SSE listening on http://localhost:${port}/sse`);\n });\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "aui-mcp-server",
3
+ "version": "0.0.1",
4
+ "description": "AUI-X: generate MCP assets from aui-x-catalog.json",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.cjs"
11
+ },
12
+ "./react": {
13
+ "types": "./dist/client/index.d.ts",
14
+ "import": "./dist/client/index.js",
15
+ "require": "./dist/client/index.cjs"
16
+ },
17
+ "./rsbuild": {
18
+ "types": "./dist/rsbuild.d.ts",
19
+ "import": "./dist/rsbuild.js",
20
+ "require": "./dist/rsbuild.cjs"
21
+ },
22
+ "./rspack": {
23
+ "types": "./dist/rspack.d.ts",
24
+ "import": "./dist/rspack.js",
25
+ "require": "./dist/rspack.cjs"
26
+ }
27
+ },
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "bin": {
31
+ "aui-mcp": "./dist/cli.js"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "typecheck": "tsc --noEmit",
36
+ "clean": "rm -rf dist",
37
+ "poc": "tsx src/poc.ts",
38
+ "postinstall": "tsup"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/ext-apps": "^1.1.2",
42
+ "@modelcontextprotocol/sdk": "^1.27.0",
43
+ "@module-federation/runtime": "2.3.0",
44
+ "chokidar": "^3.6.0",
45
+ "esbuild": "^0.25.0",
46
+ "zod": "^4.3.6"
47
+ },
48
+ "peerDependencies": {
49
+ "@a2ui/react": "^0.8.0",
50
+ "@module-federation/enhanced": "2.3.0",
51
+ "react": "^18.0.0 || ^19.0.0",
52
+ "react-dom": "^18.0.0 || ^19.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@rspack/core": "^1.7.9",
56
+ "@types/node": "^20.0.0",
57
+ "tsup": "^8.0.0",
58
+ "typescript": "^5.8.0"
59
+ },
60
+ "engines": {
61
+ "node": "22.x"
62
+ },
63
+ "keywords": [
64
+ "aui",
65
+ "a2ui",
66
+ "mcp",
67
+ "module-federation",
68
+ "mf",
69
+ "generator"
70
+ ],
71
+ "license": "MIT"
72
+ }
@@ -0,0 +1,89 @@
1
+ import type { AuiXCatalog, MfStats, RemoteContext } from '../types';
2
+ import { generateCatalogEntry } from './entry';
3
+ import { needsJsDocFill, parseJsDoc, parsePropsInterface } from './parser';
4
+
5
+ export interface CatalogBuildIssue {
6
+ componentName?: string;
7
+ message: string;
8
+ detail?: string;
9
+ }
10
+
11
+ export interface BuildCatalogFromStatsOptions {
12
+ /** Parsed mf-stats.json */
13
+ stats: MfStats;
14
+ /** Read compiled type file for given component name */
15
+ readCompiledTypes: (componentName: string) => string | undefined;
16
+ }
17
+
18
+ export interface BuildCatalogFromStatsResult {
19
+ catalog: AuiXCatalog;
20
+ issues: CatalogBuildIssue[];
21
+ /** Components missing @aui-component JSDoc */
22
+ missingJsdocComponents: string[];
23
+ }
24
+
25
+ export function buildCatalogFromStats(options: BuildCatalogFromStatsOptions): BuildCatalogFromStatsResult {
26
+ const { stats, readCompiledTypes } = options;
27
+
28
+ const catalog: AuiXCatalog = {};
29
+ const issues: CatalogBuildIssue[] = [];
30
+ const missingJsdocComponents: string[] = [];
31
+
32
+ const { publicPath, remoteEntry } = stats.metaData;
33
+ // Prefer MF manifest over remoteEntry.js for runtime loading.
34
+ // Keep it co-located with remoteEntry in the same output directory.
35
+ const remoteEntryUrl =
36
+ publicPath.replace(/\/$/, '') + '/' + [remoteEntry.path, 'mf-manifest.json'].filter(Boolean).join('/');
37
+
38
+ const loaderExposes = stats.exposes.filter((e) => e.path.endsWith('.data'));
39
+ const componentExposes = stats.exposes.filter((e) => !e.path.endsWith('.data'));
40
+
41
+ for (const expose of componentExposes) {
42
+ const componentName = expose.name;
43
+ const loaderExpose = loaderExposes.find((le) => le.path === `${expose.path}.data`);
44
+
45
+ // Note: In dev mode, MF type generation may run in parallel with the build.
46
+ // Prefer generating the catalog in MF's `dts.generateTypes.afterGenerate` hook (so compiled d.ts are ready)
47
+ // to avoid races where the first build misses type files.
48
+ const dtsContent = readCompiledTypes(componentName);
49
+ if (!dtsContent) {
50
+ issues.push({ componentName, message: 'Missing compiled d.ts for component' });
51
+ continue;
52
+ }
53
+
54
+ if (needsJsDocFill(dtsContent)) {
55
+ missingJsdocComponents.push(componentName);
56
+ issues.push({
57
+ componentName,
58
+ message: 'Missing @aui-component JSDoc (use skills/aui-jsdoc-gen)',
59
+ });
60
+ continue;
61
+ }
62
+
63
+ const jsdoc = parseJsDoc(dtsContent);
64
+ const props = parsePropsInterface(dtsContent, componentName);
65
+
66
+ const ctx: RemoteContext = {
67
+ stats,
68
+ expose,
69
+ jsdoc,
70
+ props,
71
+ componentName,
72
+ remoteEntryUrl,
73
+ loaderExpose,
74
+ };
75
+
76
+ try {
77
+ catalog[componentName] = generateCatalogEntry(ctx);
78
+ } catch (err) {
79
+ console.error(`[AUI-X] Failed to generate catalog entry for ${componentName}:`, err);
80
+ issues.push({
81
+ componentName,
82
+ message: 'Failed to generate catalog entry',
83
+ detail: err instanceof Error ? err.message : String(err),
84
+ });
85
+ }
86
+ }
87
+
88
+ return { catalog, issues, missingJsdocComponents };
89
+ }