@useconstructor/mcp 1.0.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.
- package/bin/constructor-mcp.js +13 -0
- package/package.json +54 -0
- package/src/mcp/server-stdio.ts +174 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const tsx = path.join(__dirname, '..', 'node_modules', '.bin', 'tsx');
|
|
6
|
+
const server = path.join(__dirname, '..', 'src', 'mcp', 'server-stdio.ts');
|
|
7
|
+
|
|
8
|
+
const result = spawnSync(tsx, [server], {
|
|
9
|
+
stdio: 'inherit',
|
|
10
|
+
env: process.env,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
process.exit(result.status ?? 1);
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@useconstructor/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Constructor MCP server — build and deploy web apps from natural language via Claude Code",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"constructor-mcp": "./bin/constructor-mcp.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"dev": "tsx watch src/index.ts",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"db:migrate": "tsx src/db/migrate.ts"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@anthropic-ai/bedrock-sdk": "^0.32.4",
|
|
21
|
+
"@anthropic-ai/sdk": "^0.55.1",
|
|
22
|
+
"@libsql/client": "^0.18.0",
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
24
|
+
"@octokit/rest": "^19.0.13",
|
|
25
|
+
"@supabase/supabase-js": "^2.112.3",
|
|
26
|
+
"@types/express": "^5.0.6",
|
|
27
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
28
|
+
"@types/node": "^26.2.0",
|
|
29
|
+
"@types/pg": "^8.21.0",
|
|
30
|
+
"@types/uuid": "^10.0.0",
|
|
31
|
+
"@vercel/functions": "^3.9.5",
|
|
32
|
+
"@vercel/sandbox": "^3.2.1",
|
|
33
|
+
"cors": "^2.8.6",
|
|
34
|
+
"dotenv": "^17.4.2",
|
|
35
|
+
"express": "^5.2.1",
|
|
36
|
+
"helmet": "^8.3.0",
|
|
37
|
+
"jsonwebtoken": "^9.0.3",
|
|
38
|
+
"libsodium-wrappers": "^0.8.4",
|
|
39
|
+
"modal": "^0.9.0",
|
|
40
|
+
"morgan": "^1.11.0",
|
|
41
|
+
"openai": "^7.5.0",
|
|
42
|
+
"pg": "^8.23.0",
|
|
43
|
+
"playwright": "^1.62.1",
|
|
44
|
+
"stripe": "^22.6.2",
|
|
45
|
+
"tsx": "^4.23.12",
|
|
46
|
+
"typescript": "^7.0.2",
|
|
47
|
+
"uuid": "^11.1.1",
|
|
48
|
+
"zod": "^3.25.76"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/cors": "^2.8.19",
|
|
52
|
+
"@types/morgan": "^1.9.10"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constructor MCP server — stdio transport (compatible with Claude Code / Claude Desktop).
|
|
3
|
+
* Run with: CONSTRUCTOR_API_KEY=csk_... tsx src/mcp/server-stdio.ts
|
|
4
|
+
* Or configure in Claude Code: claude mcp add constructor ...
|
|
5
|
+
*/
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import {
|
|
9
|
+
CallToolRequestSchema,
|
|
10
|
+
ListToolsRequestSchema,
|
|
11
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
|
|
13
|
+
const API_BASE = process.env.CONSTRUCTOR_API_URL || 'http://localhost:4000';
|
|
14
|
+
const API_KEY = process.env.CONSTRUCTOR_API_KEY || '';
|
|
15
|
+
|
|
16
|
+
if (!API_KEY) {
|
|
17
|
+
process.stderr.write('CONSTRUCTOR_API_KEY env var is required\n');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function auth() {
|
|
22
|
+
return { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const server = new Server(
|
|
26
|
+
{ name: 'construct', version: '1.0.0' },
|
|
27
|
+
{ capabilities: { tools: {} } },
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
31
|
+
tools: [
|
|
32
|
+
{
|
|
33
|
+
name: 'create_job',
|
|
34
|
+
description: 'Create a new Constructor project and start building it from a natural-language prompt. Returns immediately with job_id and project_id.',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
required: ['prompt'],
|
|
38
|
+
properties: {
|
|
39
|
+
prompt: { type: 'string', description: 'Natural language description of what to build' },
|
|
40
|
+
project_id: { type: 'string', description: 'Existing project ID to modify (omit for new project)' },
|
|
41
|
+
project_name: { type: 'string', description: 'Name for the new project' },
|
|
42
|
+
context: { type: 'string', description: 'Additional business context' },
|
|
43
|
+
builder_model: { type: 'string', enum: ['modal_claude_code', 'codex'] },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'get_job',
|
|
49
|
+
description: 'Get the current status and progress of a Constructor job.',
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
required: ['job_id'],
|
|
53
|
+
properties: { job_id: { type: 'string' } },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'get_project',
|
|
58
|
+
description: 'Get the state of a Constructor project: production URL, deployments, screenshots, usage.',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
required: ['project_id'],
|
|
62
|
+
properties: { project_id: { type: 'string' } },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'respond_input',
|
|
67
|
+
description: 'Provide an answer when a Constructor job is waiting for user input (needs_input state).',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
required: ['job_id', 'answer'],
|
|
71
|
+
properties: {
|
|
72
|
+
job_id: { type: 'string' },
|
|
73
|
+
answer: { type: 'string' },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'list_projects',
|
|
79
|
+
description: 'List all Constructor projects for the authenticated account.',
|
|
80
|
+
inputSchema: { type: 'object', required: [], properties: {} },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'list_jobs',
|
|
84
|
+
description: 'List jobs for a specific Constructor project.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
required: ['project_id'],
|
|
88
|
+
properties: { project_id: { type: 'string' } },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'create_project',
|
|
93
|
+
description: 'Create an empty Constructor project without triggering a build. Returns project_id. Pass it to create_job when ready to build.',
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
required: ['name'],
|
|
97
|
+
properties: {
|
|
98
|
+
name: { type: 'string' },
|
|
99
|
+
description: { type: 'string' },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
107
|
+
const { name, arguments: args = {} } = req.params;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
let result: unknown;
|
|
111
|
+
|
|
112
|
+
switch (name) {
|
|
113
|
+
case 'create_job': {
|
|
114
|
+
const res = await fetch(`${API_BASE}/v1/jobs`, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: auth(),
|
|
117
|
+
body: JSON.stringify(args),
|
|
118
|
+
});
|
|
119
|
+
result = await res.json();
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'get_job': {
|
|
123
|
+
const res = await fetch(`${API_BASE}/v1/jobs/${args.job_id}`, { headers: auth() });
|
|
124
|
+
result = await res.json();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'get_project': {
|
|
128
|
+
const res = await fetch(`${API_BASE}/v1/projects/${args.project_id}`, { headers: auth() });
|
|
129
|
+
result = await res.json();
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case 'respond_input': {
|
|
133
|
+
const res = await fetch(`${API_BASE}/v1/jobs/${args.job_id}/input`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: auth(),
|
|
136
|
+
body: JSON.stringify({ answer: args.answer }),
|
|
137
|
+
});
|
|
138
|
+
result = await res.json();
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'list_projects': {
|
|
142
|
+
const res = await fetch(`${API_BASE}/v1/projects`, { headers: auth() });
|
|
143
|
+
result = await res.json();
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'list_jobs': {
|
|
147
|
+
const res = await fetch(`${API_BASE}/v1/projects/${args.project_id}/jobs`, { headers: auth() });
|
|
148
|
+
result = await res.json();
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case 'create_project': {
|
|
152
|
+
const res = await fetch(`${API_BASE}/v1/projects`, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: auth(),
|
|
155
|
+
body: JSON.stringify(args),
|
|
156
|
+
});
|
|
157
|
+
result = await res.json();
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
default:
|
|
161
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
165
|
+
} catch (err: any) {
|
|
166
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const transport = new StdioServerTransport();
|
|
171
|
+
server.connect(transport).catch((err) => {
|
|
172
|
+
process.stderr.write(`MCP server error: ${err.message}\n`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
});
|