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.
Files changed (3) hide show
  1. package/README.md +114 -0
  2. package/index.js +257 -0
  3. 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
+ }