@gluip/chart-canvas-mcp 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,18 @@
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import { stateManager } from "./state.js";
4
+ const app = express();
5
+ const PORT = 3000;
6
+ app.use(cors());
7
+ app.use(express.json());
8
+ // Get current canvas state
9
+ app.get("/state", (req, res) => {
10
+ res.json(stateManager.getState());
11
+ });
12
+ // Health check
13
+ app.get("/health", (req, res) => {
14
+ res.json({ status: "ok" });
15
+ });
16
+ app.listen(PORT, () => {
17
+ console.log(`API server listening on http://localhost:${PORT}`);
18
+ });
package/dist/api.js ADDED
@@ -0,0 +1,32 @@
1
+ import express from "express";
2
+ import cors from "cors";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { stateManager } from "./state.js";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ export function startApiServer() {
9
+ const app = express();
10
+ const PORT = 3000;
11
+ app.use(cors());
12
+ app.use(express.json());
13
+ // API endpoints
14
+ app.get("/state", (req, res) => {
15
+ res.json(stateManager.getState());
16
+ });
17
+ app.get("/health", (req, res) => {
18
+ res.json({ status: "ok" });
19
+ });
20
+ // Serve frontend static files in production
21
+ const frontendDistPath = path.join(__dirname, "../../frontend/dist");
22
+ app.use(express.static(frontendDistPath));
23
+ // Fallback to index.html for client-side routing
24
+ app.get("*", (req, res) => {
25
+ res.sendFile(path.join(frontendDistPath, "index.html"));
26
+ });
27
+ app.listen(PORT, () => {
28
+ console.error(`Server running on http://localhost:${PORT}`);
29
+ console.error(`- API: http://localhost:${PORT}/state`);
30
+ console.error(`- Frontend: http://localhost:${PORT}`);
31
+ });
32
+ }
package/dist/index.js ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { stateManager } from "./state.js";
6
+ import { startApiServer } from "./api.js";
7
+ import open from "open";
8
+ const server = new Server({
9
+ name: "chart-canvas-server",
10
+ version: "0.1.0",
11
+ }, {
12
+ capabilities: {
13
+ tools: {},
14
+ },
15
+ });
16
+ // Register tools
17
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
18
+ return {
19
+ tools: [
20
+ {
21
+ name: "addVisualization",
22
+ description: "Add a new chart visualization to the canvas",
23
+ inputSchema: {
24
+ type: "object",
25
+ properties: {
26
+ type: {
27
+ type: "string",
28
+ enum: ["line", "bar", "scatter", "table", "flowchart", "pie"],
29
+ description: "Type of visualization to create",
30
+ },
31
+ series: {
32
+ type: "array",
33
+ items: {
34
+ type: "object",
35
+ properties: {
36
+ name: {
37
+ type: "string",
38
+ description: "Name of the series (e.g., '2015', 'Product A')",
39
+ },
40
+ data: {
41
+ type: "array",
42
+ items: {
43
+ type: "array",
44
+ items: { type: "number" },
45
+ minItems: 2,
46
+ maxItems: 2,
47
+ },
48
+ description: "Array of [x, y] coordinate pairs for this series",
49
+ },
50
+ },
51
+ required: ["name", "data"],
52
+ },
53
+ description: "Array of data series to display (required for charts)",
54
+ },
55
+ table: {
56
+ type: "object",
57
+ properties: {
58
+ headers: {
59
+ type: "array",
60
+ items: { type: "string" },
61
+ description: "Column headers for the table",
62
+ },
63
+ rows: {
64
+ type: "array",
65
+ items: {
66
+ type: "array",
67
+ items: { type: ["string", "number"] },
68
+ },
69
+ description: "Table rows, each row is an array of values",
70
+ },
71
+ },
72
+ required: ["headers", "rows"],
73
+ description: "Table data (required for table type)",
74
+ },
75
+ mermaid: {
76
+ type: "string",
77
+ description: "Mermaid diagram syntax (required for flowchart type)",
78
+ },
79
+ title: {
80
+ type: "string",
81
+ description: "Optional title for the chart",
82
+ },
83
+ description: {
84
+ type: "string",
85
+ description: "Optional description or explanation for the chart",
86
+ },
87
+ xLabels: {
88
+ type: "array",
89
+ items: { type: "string" },
90
+ description: "Optional labels for the x-axis (e.g., dates). Should match the number of data points.",
91
+ },
92
+ },
93
+ required: ["type"],
94
+ },
95
+ },
96
+ {
97
+ name: "removeVisualization",
98
+ description: "Remove a visualization from the canvas by ID",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ id: {
103
+ type: "string",
104
+ description: "ID of the visualization to remove",
105
+ },
106
+ },
107
+ required: ["id"],
108
+ },
109
+ },
110
+ {
111
+ name: "clearCanvas",
112
+ description: "Remove all visualizations from the canvas",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {},
116
+ },
117
+ },
118
+ {
119
+ name: "showCanvas",
120
+ description: "Open the canvas in a browser window to view visualizations",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {},
124
+ },
125
+ },
126
+ ],
127
+ };
128
+ });
129
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
130
+ const { name, arguments: args } = request.params;
131
+ switch (name) {
132
+ case "addVisualization": {
133
+ const { type, series, table, mermaid, title, description, xLabels } = args;
134
+ const viz = stateManager.addVisualization({
135
+ type,
136
+ series,
137
+ table,
138
+ mermaid,
139
+ title,
140
+ description,
141
+ xLabels,
142
+ });
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: `Added ${type} chart with ID ${viz.id}. View it at http://localhost:3000`,
148
+ },
149
+ ],
150
+ };
151
+ }
152
+ case "removeVisualization": {
153
+ const { id } = args;
154
+ const removed = stateManager.removeVisualization(id);
155
+ return {
156
+ content: [
157
+ {
158
+ type: "text",
159
+ text: removed
160
+ ? `Removed visualization ${id}`
161
+ : `Visualization ${id} not found`,
162
+ },
163
+ ],
164
+ };
165
+ }
166
+ case "clearCanvas": {
167
+ stateManager.clearCanvas();
168
+ return {
169
+ content: [
170
+ {
171
+ type: "text",
172
+ text: "Cleared all visualizations from canvas",
173
+ },
174
+ ],
175
+ };
176
+ }
177
+ case "showCanvas": {
178
+ try {
179
+ await open("http://localhost:3000");
180
+ return {
181
+ content: [
182
+ {
183
+ type: "text",
184
+ text: "Opened canvas in browser at http://localhost:3000",
185
+ },
186
+ ],
187
+ };
188
+ }
189
+ catch (error) {
190
+ return {
191
+ content: [
192
+ {
193
+ type: "text",
194
+ text: `Failed to open browser: ${error instanceof Error ? error.message : String(error)}`,
195
+ },
196
+ ],
197
+ };
198
+ }
199
+ }
200
+ default:
201
+ throw new Error(`Unknown tool: ${name}`);
202
+ }
203
+ });
204
+ async function main() {
205
+ // Start the API server for frontend
206
+ startApiServer();
207
+ // Start MCP server on stdio
208
+ const transport = new StdioServerTransport();
209
+ await server.connect(transport);
210
+ console.error("Chart Canvas MCP Server running");
211
+ console.error("API server running on http://localhost:3000");
212
+ }
213
+ main().catch((error) => {
214
+ console.error("Fatal error:", error);
215
+ process.exit(1);
216
+ });
package/dist/state.js ADDED
@@ -0,0 +1,35 @@
1
+ import { randomUUID } from "crypto";
2
+ class StateManager {
3
+ state = {
4
+ visualizations: [],
5
+ };
6
+ getState() {
7
+ return JSON.parse(JSON.stringify(this.state));
8
+ }
9
+ addVisualization(viz) {
10
+ // Flowcharts krijgen een grotere default grootte
11
+ const isFlowchart = viz.type === "flowchart";
12
+ const defaultWidth = isFlowchart ? 10 : 6;
13
+ const defaultHeight = isFlowchart ? 8 : 4;
14
+ const newViz = {
15
+ id: randomUUID(),
16
+ ...viz,
17
+ // Auto-position: calculate next available spot
18
+ x: (this.state.visualizations.length % 2) * 6,
19
+ y: Math.floor(this.state.visualizations.length / 2) * 4,
20
+ w: defaultWidth,
21
+ h: defaultHeight,
22
+ };
23
+ this.state.visualizations.push(newViz);
24
+ return newViz;
25
+ }
26
+ removeVisualization(id) {
27
+ const initialLength = this.state.visualizations.length;
28
+ this.state.visualizations = this.state.visualizations.filter((v) => v.id !== id);
29
+ return this.state.visualizations.length < initialLength;
30
+ }
31
+ clearCanvas() {
32
+ this.state.visualizations = [];
33
+ }
34
+ }
35
+ export const stateManager = new StateManager();
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@gluip/chart-canvas-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for creating interactive visualizations (charts, diagrams, tables) through AI assistants",
5
+ "author": "Martijn",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "bin": {
10
+ "chart-canvas-mcp": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "visualization",
21
+ "charts",
22
+ "echarts",
23
+ "mermaid",
24
+ "ai",
25
+ "llm"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/martijn/chart-canvas.git"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "build:all": "npm run build && cd ../frontend && npm run build",
34
+ "dev": "tsx src/index.ts",
35
+ "dev:api": "tsx src/api-server.ts",
36
+ "start": "node dist/index.js",
37
+ "start:prod": "npm run build:all && node dist/index.js"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.0.4",
41
+ "express": "^4.21.2",
42
+ "cors": "^2.8.5",
43
+ "open": "^10.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/express": "^5.0.0",
47
+ "@types/cors": "^2.8.17",
48
+ "@types/node": "^22.10.5",
49
+ "typescript": "^5.7.3",
50
+ "tsx": "^4.19.2"
51
+ }
52
+ }