Commit 9a870adb authored by Pham Tuan's avatar Pham Tuan

Merge branch 'main' into 'master'

Main

See merge request !3
parents 045c1da9 f163640a
export interface ProjectContext { export interface ProjectContext {
framework: "react"; framework: "react";
language: "ts" | "js"; language: "ts" | "js";
hasPackageJson: boolean;
} }
export declare function inferProjectContext(): ProjectContext; export declare function inferProjectContext(): ProjectContext;
import fs from "fs";
import path from "path";
export function inferProjectContext() { export function inferProjectContext() {
const hasPackageJson = fs.existsSync(path.join(process.cwd(), "package.json"));
return { return {
framework: "react", framework: "react",
language: "ts", language: "ts",
hasPackageJson,
}; };
} }
import { UIIntent } from "./intent.core.js"; import { UIIntent } from "./intent.core.js";
export declare function generateReactCode(intent: UIIntent): string; export declare function generateReactCode(intent: UIIntent): {
code: string;
};
export function generateReactCode(intent) { export function generateReactCode(intent) {
return `import { ${intent.components.join(", ")} } from 'awing-library' const code = `import { ${intent.components.join(", ")} } from 'awing-library'
// ${intent.description}
export default function GeneratedUI() { export default function GeneratedUI() {
return ( return (
<div> <div>
${intent.components.map((c) => `<${c} />`).join("\n ")} ${intent.components.map((c) => `<${c} />`).join("\n ")}<
</div> /div>
) )
}`; }`;
return { code };
} }
#!/usr/bin/env node
export {}; export {};
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; #!/usr/bin/env node
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { tools, executeTool } from './tools/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import { tools, executeTool } from "./tools/index.js";
// Create MCP server // Create MCP server
const server = new Server({ const server = new Server({
name: 'ui-platform-mcp', name: "ui-platform-mcp",
version: '1.0.0' version: "1.0.0",
}, { }, {
capabilities: { capabilities: {
tools: {} tools: {},
} },
}); });
// List available tools // List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(ListToolsRequestSchema, async () => {
return { return {
tools: tools tools: tools,
}; };
}); });
// Handle tool calls // Handle tool calls
...@@ -25,10 +26,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { ...@@ -25,10 +26,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
return { return {
content: [ content: [
{ {
type: 'text', type: "text",
text: JSON.stringify(result, null, 2) text: JSON.stringify(result, null, 2),
} },
] ],
}; };
} }
catch (error) { catch (error) {
...@@ -36,11 +37,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { ...@@ -36,11 +37,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
return { return {
content: [ content: [
{ {
type: 'text', type: "text",
text: `Error: ${errorMessage}` text: `Error: ${errorMessage}`,
} },
], ],
isError: true isError: true,
}; };
} }
}); });
...@@ -48,9 +49,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { ...@@ -48,9 +49,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
async function main() { async function main() {
const transport = new StdioServerTransport(); const transport = new StdioServerTransport();
await server.connect(transport); await server.connect(transport);
console.error('UI Platform MCP Server running on stdio'); console.error("UI Platform MCP Server running on stdio");
} }
main().catch((error) => { main().catch((error) => {
console.error('Server error:', error); console.error("Server error:", error);
process.exit(1); process.exit(1);
}); });
export declare const generateUICodeTool: { export declare const generateUICodeTool: {
name: string; name: string;
run(input: any): string; run(input: any): {
code: string;
};
}; };
export default {
preset: "ts-jest/presets/default-esm",
testEnvironment: "node",
extensionsToTreatAsEsm: [".ts"],
roots: ["<rootDir>/src", "<rootDir>/tests"],
testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"],
transform: {
"^.+\\.ts$": [
"ts-jest",
{
useESM: true,
},
],
},
collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"],
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
};
This diff is collapsed.
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
], ],
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"test": "jest",
"test:watch": "jest --watch",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"start": "node dist/index.js", "start": "node dist/index.js",
"dev": "tsx src/index.ts" "dev": "tsx src/index.ts"
...@@ -20,7 +22,10 @@ ...@@ -20,7 +22,10 @@
"@modelcontextprotocol/sdk": "^1.0.0" "@modelcontextprotocol/sdk": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^30.0.0",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"jest": "^30.2.0",
"ts-jest": "^29.4.6",
"tsx": "^4.0.0", "tsx": "^4.0.0",
"typescript": "^5.6.0" "typescript": "^5.6.0"
}, },
......
import fs from "fs";
import path from "path";
export interface ProjectContext { export interface ProjectContext {
framework: "react"; framework: "react";
language: "ts" | "js"; language: "ts" | "js";
hasPackageJson: boolean;
} }
export function inferProjectContext(): ProjectContext { export function inferProjectContext(): ProjectContext {
const hasPackageJson = fs.existsSync(
path.join(process.cwd(), "package.json"),
);
return { return {
framework: "react", framework: "react",
language: "ts", language: "ts",
hasPackageJson,
}; };
} }
import { UIIntent } from "./intent.core.js"; import { UIIntent } from "./intent.core.js";
export function generateReactCode(intent: UIIntent): string { export function generateReactCode(intent: UIIntent): { code: string } {
return `import { ${intent.components.join(", ")} } from 'awing-library' const code = `import { ${intent.components.join(", ")} } from 'awing-library'
// ${intent.description}
export default function GeneratedUI() { export default function GeneratedUI() {
return ( return (
<div> <div>
${intent.components.map((c) => `<${c} />`).join("\n ")} ${intent.components.map((c) => `<${c} />`).join("\n ")}<
</div> /div>
) )
}`; }`;
return { code };
} }
import { Server } from '@modelcontextprotocol/sdk/server/index.js' #!/usr/bin/env node
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js' import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { tools, executeTool } from './tools/index.js' import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { tools, executeTool } from "./tools/index.js";
// Create MCP server // Create MCP server
const server = new Server( const server = new Server(
{ {
name: 'ui-platform-mcp', name: "ui-platform-mcp",
version: '1.0.0' version: "1.0.0",
}, },
{ {
capabilities: { capabilities: {
tools: {} tools: {},
} },
} },
) );
// List available tools // List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(ListToolsRequestSchema, async () => {
return { return {
tools: tools tools: tools,
} };
}) });
// Handle tool calls // Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => { server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params const { name, arguments: args } = request.params;
try { try {
const result = await executeTool(name, args as Record<string, unknown>) const result = await executeTool(name, args as Record<string, unknown>);
return { return {
content: [ content: [
{ {
type: 'text', type: "text",
text: JSON.stringify(result, null, 2) text: JSON.stringify(result, null, 2),
} },
] ],
} };
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error);
return { return {
content: [ content: [
{ {
type: 'text', type: "text",
text: `Error: ${errorMessage}` text: `Error: ${errorMessage}`,
} },
], ],
isError: true isError: true,
} };
} }
}) });
// Start the server // Start the server
async function main() { async function main() {
const transport = new StdioServerTransport() const transport = new StdioServerTransport();
await server.connect(transport) await server.connect(transport);
console.error('UI Platform MCP Server running on stdio') console.error("UI Platform MCP Server running on stdio");
} }
main().catch((error) => { main().catch((error) => {
console.error('Server error:', error) console.error("Server error:", error);
process.exit(1) process.exit(1);
}) });
import { generateReactCode } from "../src/core/codegen.core.js";
import { UIIntent } from "../src/core/intent.core.js";
describe("Code Generator", () => {
it("should generate code with Button component", () => {
const intent: UIIntent = {
description: "A simple button",
components: ["Button"],
};
const result = generateReactCode(intent);
expect(result.code).toContain("import");
expect(result.code).toContain("Button");
expect(result.code).toContain("export");
});
it("should generate code with multiple components", () => {
const intent: UIIntent = {
description: "Button and Modal",
components: ["Button", "Modal"],
};
const result = generateReactCode(intent);
expect(result.code).toContain("Button");
expect(result.code).toContain("Modal");
});
it("should include description in code", () => {
const intent: UIIntent = {
description: "User profile form",
components: ["Form"],
};
const result = generateReactCode(intent);
expect(result.code).toContain("User profile form");
});
it("should return valid React component structure", () => {
const intent: UIIntent = {
description: "Test component",
components: ["Button"],
};
const result = generateReactCode(intent);
expect(result.code).toMatch(/export\s+(default\s+)?function/);
});
});
import { parseUIIntent } from "../src/core/intent.core.js";
describe("Intent Parser", () => {
it("should parse button intent", () => {
const result = parseUIIntent("Create a button");
expect(result.description).toBe("Create a button");
expect(result.components).toContain("Button");
});
it("should parse modal intent", () => {
const result = parseUIIntent("Show a modal dialog");
expect(result.description).toBe("Show a modal dialog");
expect(result.components).toContain("Modal");
});
it("should parse multiple components", () => {
const result = parseUIIntent("Create a button that opens a modal");
expect(result.components).toContain("Button");
expect(result.components).toContain("Modal");
expect(result.components).toHaveLength(2);
});
it("should return empty components for unknown intent", () => {
const result = parseUIIntent("Something random");
expect(result.components).toHaveLength(0);
});
it("should be case insensitive", () => {
const result = parseUIIntent("CREATE A BUTTON");
expect(result.components).toContain("Button");
});
});
import { executeTool, tools } from "../src/tools/index.js";
describe("MCP Tools", () => {
describe("Tool Definitions", () => {
it("should have all required tools defined", () => {
const toolNames = tools.map((t) => t.name);
expect(toolNames).toContain("analyze_design_system");
expect(toolNames).toContain("analyze_storybook");
expect(toolNames).toContain("parse_ui_intent");
expect(toolNames).toContain("get_project_context");
expect(toolNames).toContain("generate_ui_code");
});
it("should have valid tool schemas", () => {
tools.forEach((tool) => {
expect(tool.name).toBeDefined();
expect(tool.description).toBeDefined();
expect(tool.inputSchema).toBeDefined();
expect(tool.inputSchema.type).toBe("object");
});
});
});
describe("executeTool", () => {
it("should throw error for unknown tool", async () => {
await expect(executeTool("unknown_tool", {})).rejects.toThrow(
"Unknown tool: unknown_tool",
);
});
it("should execute parse_ui_intent tool", async () => {
const result = await executeTool("parse_ui_intent", {
prompt: "Create a button and modal",
});
expect(result).toHaveProperty("description");
expect(result).toHaveProperty("components");
expect((result as any).components).toContain("Button");
expect((result as any).components).toContain("Modal");
});
it("should execute get_project_context tool", async () => {
const result = await executeTool("get_project_context", {});
expect(result).toHaveProperty("framework");
expect(result).toHaveProperty("hasPackageJson");
});
it("should execute generate_ui_code tool", async () => {
const result = await executeTool("generate_ui_code", {
description: "A simple button",
components: ["Button"],
});
expect(result).toHaveProperty("code");
expect((result as any).code).toContain("Button");
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment