babelpad-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/README.md +114 -0
- package/index.js +257 -0
- package/package.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Babelpad MCP Server
|
|
2
|
+
|
|
3
|
+
The Model Context Protocol (MCP) server for [babelpad.dev](https://babelpad.dev) enables AI assistants (such as Cursor, Windsurf, or Claude Desktop) to interact directly with the Babelpad execution engine.
|
|
4
|
+
|
|
5
|
+
With this server, your AI assistant can:
|
|
6
|
+
1. **Generate coding exercises/fixes** and output a direct, clickable markdown link (e.g., `[Open in Babelpad](url)`) that opens the Babelpad editor pre-populated with code and multiple files.
|
|
7
|
+
2. **Execute code** on your behalf in the Babelpad container sandbox using your API Key and return the output (`stdout`, `stderr`, execution time, and exit status).
|
|
8
|
+
3. **Inspect supported environments** and file extensions.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Installation & Setup
|
|
13
|
+
|
|
14
|
+
You can run the MCP server directly using `node` or via `npx` (if built/published).
|
|
15
|
+
|
|
16
|
+
### Prerequisite
|
|
17
|
+
Ensure you install the dependencies inside the `mcp-server` directory:
|
|
18
|
+
```bash
|
|
19
|
+
cd mcp-server
|
|
20
|
+
npm install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Client Integration Configurations
|
|
26
|
+
|
|
27
|
+
Depending on where your editor/client runs, configure the server in your client's settings.
|
|
28
|
+
|
|
29
|
+
### 1. Cursor / Windsurf (Windows Native Node)
|
|
30
|
+
If you have Node.js installed on your Windows machine, add a new MCP server in your editor settings (e.g. **Settings -> Features -> MCP** in Cursor):
|
|
31
|
+
|
|
32
|
+
* **Name**: `Babelpad`
|
|
33
|
+
* **Type**: `command`
|
|
34
|
+
* **Command**:
|
|
35
|
+
```bash
|
|
36
|
+
node C:/path/to/babelpad/mcp-server/index.js
|
|
37
|
+
```
|
|
38
|
+
* **Environment Variables**:
|
|
39
|
+
* `BABELPAD_API_KEY`: `bpk_your_actual_api_key_here` (Get this from your [Babelpad Profile Developer settings](https://babelpad.dev/profile))
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
### 2. Cursor / Windsurf (Running inside WSL)
|
|
44
|
+
If your editor connects to a WSL workspace, or you want to run the Node.js server inside the Ubuntu WSL environment, configure the command as:
|
|
45
|
+
|
|
46
|
+
* **Name**: `Babelpad`
|
|
47
|
+
* **Type**: `command`
|
|
48
|
+
* **Command**:
|
|
49
|
+
```bash
|
|
50
|
+
wsl sh -c "export BABELPAD_API_KEY=bpk_your_actual_api_key_here && node /home/parrot/babelpad/mcp-server/index.js"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### 3. Claude Desktop (Windows)
|
|
56
|
+
To register the server globally in your Claude Desktop client, edit your configuration file:
|
|
57
|
+
* Path: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
58
|
+
|
|
59
|
+
Add the following config:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"babelpad": {
|
|
65
|
+
"command": "node",
|
|
66
|
+
"args": ["C:/path/to/babelpad/mcp-server/index.js"],
|
|
67
|
+
"env": {
|
|
68
|
+
"BABELPAD_API_KEY": "bpk_your_actual_api_key_here"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If you prefer to run it inside WSL from your Windows Claude Desktop client:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"babelpad": {
|
|
81
|
+
"command": "wsl",
|
|
82
|
+
"args": [
|
|
83
|
+
"sh",
|
|
84
|
+
"-c",
|
|
85
|
+
"export BABELPAD_API_KEY=bpk_your_actual_api_key_here && node /home/parrot/babelpad/mcp-server/index.js"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Exposed Tools
|
|
95
|
+
|
|
96
|
+
### 1. `create_editor_url`
|
|
97
|
+
Generates a stateless, compressed `babelpad.dev/editor` URL preloaded with code/files.
|
|
98
|
+
* **Arguments**:
|
|
99
|
+
- `language` (string, required): `python`, `javascript`, `rust`, `c`, or `go`.
|
|
100
|
+
- `code` (string, optional): Single-file program content.
|
|
101
|
+
- `files` (array, optional): List of `{ name, content }` objects for multi-file configurations.
|
|
102
|
+
- `interpreter` (boolean, optional): If `true`, opens in Esoteric Custom Interpreter mode.
|
|
103
|
+
- `title` (string, optional): A custom title for the script tab.
|
|
104
|
+
|
|
105
|
+
### 2. `execute_code`
|
|
106
|
+
Runs code immediately in the Babelpad container sandbox.
|
|
107
|
+
* **Arguments**:
|
|
108
|
+
- `language` (string, required): Runtime language.
|
|
109
|
+
- `files` (array, required): Program file(s) to compile and run.
|
|
110
|
+
- `stdin` (string, optional): Program inputs.
|
|
111
|
+
* **Requirement**: Requires `BABELPAD_API_KEY` configured in the server's environment.
|
|
112
|
+
|
|
113
|
+
### 3. `get_supported_languages`
|
|
114
|
+
Lists all supported languages, runtimes, versions, and entry file specifications.
|
package/index.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import LZString from "lz-string";
|
|
10
|
+
|
|
11
|
+
// ── Constants & Helpers ──────────────────────────────────────────────────────
|
|
12
|
+
const extMap = {
|
|
13
|
+
python: ".py",
|
|
14
|
+
javascript: ".js",
|
|
15
|
+
rust: ".rs",
|
|
16
|
+
c: ".c",
|
|
17
|
+
go: ".go"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function getFilenameForLanguage(lang) {
|
|
21
|
+
return "main" + (extMap[lang.toLowerCase()] || ".txt");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const SUPPORTED_LANGUAGES = [
|
|
25
|
+
{ id: "python", name: "Python 3", extension: ".py" },
|
|
26
|
+
{ id: "javascript", name: "JavaScript (Node.js)", extension: ".js" },
|
|
27
|
+
{ id: "rust", name: "Rust", extension: ".rs" },
|
|
28
|
+
{ id: "c", name: "C (GCC)", extension: ".c" },
|
|
29
|
+
{ id: "go", name: "Go", extension: ".go" }
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// Initialize Server
|
|
33
|
+
const server = new Server(
|
|
34
|
+
{
|
|
35
|
+
name: "babelpad-mcp",
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
capabilities: {
|
|
40
|
+
tools: {},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Register Tool List Handler
|
|
46
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
47
|
+
return {
|
|
48
|
+
tools: [
|
|
49
|
+
{
|
|
50
|
+
name: "create_editor_url",
|
|
51
|
+
description: "Generates a stateless, shareable Babelpad.dev URL pre-populated with code and files. The LLM can use this to present a clickable link (e.g. [Open Code in Babelpad](url)) to the user for exercises, templates, or fixes.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
language: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "The programming language identifier (e.g. 'python', 'javascript', 'rust', 'c', 'go').",
|
|
58
|
+
enum: ["python", "javascript", "rust", "c", "go"]
|
|
59
|
+
},
|
|
60
|
+
code: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "The code content for single-file scripts."
|
|
63
|
+
},
|
|
64
|
+
files: {
|
|
65
|
+
type: "array",
|
|
66
|
+
description: "An array of files for multi-file scripts. Each file must be an object with 'name' and 'content'.",
|
|
67
|
+
items: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
name: { type: "string", description: "The filename (e.g. 'helper.py')." },
|
|
71
|
+
content: { type: "string", description: "The file code content." }
|
|
72
|
+
},
|
|
73
|
+
required: ["name", "content"]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
interpreter: {
|
|
77
|
+
type: "boolean",
|
|
78
|
+
description: "Set to true to launch the editor in Esoteric Interpreter Mode."
|
|
79
|
+
},
|
|
80
|
+
title: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Optional custom title for the script when opened in the editor."
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
required: ["language"]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "execute_code",
|
|
90
|
+
description: "Runs code immediately in the secure Babelpad sandbox using the user's API Key and returns stdout, stderr, and exit status. Use this to verify that generated code works or run programs.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
language: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "The programming language identifier.",
|
|
97
|
+
enum: ["python", "javascript", "rust", "c", "go"]
|
|
98
|
+
},
|
|
99
|
+
files: {
|
|
100
|
+
type: "array",
|
|
101
|
+
description: "An array of file objects to execute. There must be at least one file, and the entry point must match the standard entry point for the language (e.g., 'main.py' for Python).",
|
|
102
|
+
items: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
name: { type: "string" },
|
|
106
|
+
content: { type: "string" }
|
|
107
|
+
},
|
|
108
|
+
required: ["name", "content"]
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
stdin: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Optional standard input to feed to the running program."
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
required: ["language", "files"]
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "get_supported_languages",
|
|
121
|
+
description: "Lists all programming languages and runtimes currently supported by Babelpad.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Register Tool Execution Handler
|
|
132
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
133
|
+
const { name, arguments: args } = request.params;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
if (name === "create_editor_url") {
|
|
137
|
+
const { language, code, files, interpreter = false, title } = args;
|
|
138
|
+
const langLower = language.toLowerCase();
|
|
139
|
+
let compressedPayload = "";
|
|
140
|
+
|
|
141
|
+
if (files && Array.isArray(files) && files.length > 0) {
|
|
142
|
+
// Optimization: single file default naming
|
|
143
|
+
const defaultName = getFilenameForLanguage(langLower);
|
|
144
|
+
if (files.length === 1 && files[0].name.trim() === defaultName) {
|
|
145
|
+
compressedPayload = LZString.compressToEncodedURIComponent(files[0].content);
|
|
146
|
+
} else {
|
|
147
|
+
const compact = files.map(f => ({
|
|
148
|
+
n: f.name.trim(),
|
|
149
|
+
c: f.content
|
|
150
|
+
}));
|
|
151
|
+
compressedPayload = LZString.compressToEncodedURIComponent(JSON.stringify(compact));
|
|
152
|
+
}
|
|
153
|
+
} else if (typeof code === "string") {
|
|
154
|
+
compressedPayload = LZString.compressToEncodedURIComponent(code);
|
|
155
|
+
} else {
|
|
156
|
+
throw new Error("Either 'code' (string) or 'files' (array) must be provided.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const urlObj = new URL(`https://babelpad.dev/editor/${encodeURIComponent(langLower)}`);
|
|
160
|
+
urlObj.searchParams.set("code", compressedPayload);
|
|
161
|
+
if (interpreter) urlObj.searchParams.set("interpreter", "true");
|
|
162
|
+
if (title && title.trim() !== "") urlObj.searchParams.set("title", title.trim());
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: JSON.stringify({
|
|
169
|
+
success: true,
|
|
170
|
+
url: urlObj.toString(),
|
|
171
|
+
message: `URL generated successfully. You can display it in markdown as [Open Code in Babelpad](${urlObj.toString()})`
|
|
172
|
+
}, null, 2)
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
} else if (name === "execute_code") {
|
|
178
|
+
const { language, files, stdin = "" } = args;
|
|
179
|
+
const apiKey = process.env.BABELPAD_API_KEY;
|
|
180
|
+
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
return {
|
|
183
|
+
isError: true,
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: "Error: The BABELPAD_API_KEY environment variable is not set. The user must provide their API Key (generated in https://babelpad.dev/profile) to execute code."
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const response = await fetch("https://babelpad.dev/api/v1/run", {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: {
|
|
196
|
+
"Content-Type": "application/json",
|
|
197
|
+
"X-API-Key": apiKey.trim()
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
language,
|
|
201
|
+
files,
|
|
202
|
+
stdin
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const data = await response.json();
|
|
207
|
+
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
return {
|
|
210
|
+
isError: true,
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: `API request failed with status ${response.status}: ${data.error || JSON.stringify(data)}`
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: JSON.stringify(data, null, 2)
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
} else if (name === "get_supported_languages") {
|
|
230
|
+
return {
|
|
231
|
+
content: [
|
|
232
|
+
{
|
|
233
|
+
type: "text",
|
|
234
|
+
text: JSON.stringify({ languages: SUPPORTED_LANGUAGES }, null, 2)
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return {
|
|
243
|
+
isError: true,
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: error instanceof Error ? error.message : String(error)
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Run stdio transport
|
|
255
|
+
const transport = new StdioServerTransport();
|
|
256
|
+
await server.connect(transport);
|
|
257
|
+
console.error("Babelpad MCP Server running on stdio");
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "babelpad-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for babelpad.dev",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"babelpad-mcp": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
12
|
+
"lz-string": "^1.5.0"
|
|
13
|
+
}
|
|
14
|
+
}
|