greed-compute-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.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # greed-compute-mcp
2
+
3
+ MCP server that gives LLMs stateful Python execution. Connect it to Claude, GPT, or any MCP-compatible agent and they can create sessions, run code, checkpoint state, fork workers, and run parallel MapReduce — all through natural tool calls.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install -g greed-compute-mcp
9
+ ```
10
+
11
+ Set your API key:
12
+
13
+ ```bash
14
+ export GREED_API_KEY=gc_your_key_here
15
+ ```
16
+
17
+ ## Usage with Claude Desktop
18
+
19
+ Add to your `claude_desktop_config.json`:
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "greed-compute": {
25
+ "command": "greed-compute-mcp",
26
+ "env": {
27
+ "GREED_API_KEY": "gc_your_key_here"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Usage with Claude Code
35
+
36
+ ```bash
37
+ claude mcp add greed-compute -- greed-compute-mcp
38
+ ```
39
+
40
+ Set the env var in your shell or `.env`.
41
+
42
+ ## Available Tools
43
+
44
+ | Tool | What it does |
45
+ |------|-------------|
46
+ | `create_session` | Spin up a Python interpreter (blank, data-science, ML, or scraping template) |
47
+ | `execute_code` | Run Python in a session. State persists between calls |
48
+ | `session_status` | Check TTL and activity |
49
+ | `terminate_session` | Kill a session |
50
+ | `install_packages` | pip install into a running session |
51
+ | `create_checkpoint` | Snapshot all interpreter state |
52
+ | `restore_checkpoint` | Load a checkpoint into a session |
53
+ | `list_checkpoints` | See all saved checkpoints |
54
+ | `delete_checkpoint` | Free checkpoint storage |
55
+ | `create_swarm` | Parallel MapReduce across N workers |
56
+ | `get_swarm` | Poll swarm progress |
57
+ | `create_workspace` | Persistent shared environment |
58
+ | `execute_in_workspace` | Run code in shared workspace |
59
+ | `list_workspaces` | See your workspaces |
60
+ | `upload_file` | Push files into a session |
61
+ | `download_file` | Pull files out of a session |
62
+ | `get_usage` | Check plan limits and usage |
63
+
64
+ ## Environment Variables
65
+
66
+ | Variable | Required | Default |
67
+ |----------|----------|---------|
68
+ | `GREED_API_KEY` | Yes | — |
69
+ | `GREED_COMPUTE_URL` | No | `https://compute.deep-ml.com` |
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { z } from 'zod';
5
+ const BASE = process.env.GREED_COMPUTE_URL || 'https://compute.deep-ml.com';
6
+ const API_KEY = process.env.GREED_API_KEY || '';
7
+ if (!API_KEY) {
8
+ console.error('greed-compute-mcp: set GREED_API_KEY environment variable');
9
+ process.exit(1);
10
+ }
11
+ // ── HTTP helper ──────────────────────────────────────────────────────────────
12
+ async function api(method, path, body) {
13
+ const res = await fetch(`${BASE}/v1${path}`, {
14
+ method,
15
+ headers: {
16
+ 'Content-Type': 'application/json',
17
+ 'X-API-Key': API_KEY,
18
+ },
19
+ body: body ? JSON.stringify(body) : undefined,
20
+ });
21
+ const text = await res.text();
22
+ try {
23
+ return JSON.parse(text);
24
+ }
25
+ catch {
26
+ return text;
27
+ }
28
+ }
29
+ function text(data) {
30
+ return { content: [{ type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }] };
31
+ }
32
+ // ── Server ───────────────────────────────────────────────────────────────────
33
+ const server = new McpServer({
34
+ name: 'greed-compute',
35
+ version: '0.1.0',
36
+ });
37
+ // ── Session tools ────────────────────────────────────────────────────────────
38
+ server.tool('create_session', 'Create a stateful Python session. State persists between execute calls.', {
39
+ template: z.enum(['blank', 'data-science', 'machine-learning', 'web-scraping']).default('blank').describe('Pre-loaded library template'),
40
+ ttl_seconds: z.number().optional().describe('Session lifetime in seconds (default 120)'),
41
+ }, async ({ template, ttl_seconds }) => {
42
+ const result = await api('POST', '/session/create', { template, ttl_seconds });
43
+ return text(result);
44
+ });
45
+ server.tool('execute_code', 'Run Python code in a session. Variables persist between calls. Returns stdout, result, errors, and plots.', {
46
+ session_id: z.string().describe('Session ID from create_session'),
47
+ code: z.string().describe('Python code to execute'),
48
+ }, async ({ session_id, code }) => {
49
+ const result = await api('POST', `/session/${session_id}/execute`, { code });
50
+ return text(result);
51
+ });
52
+ server.tool('session_status', 'Check if a session is alive and how much TTL remains.', {
53
+ session_id: z.string().describe('Session ID'),
54
+ }, async ({ session_id }) => {
55
+ const result = await api('GET', `/session/${session_id}/status`);
56
+ return text(result);
57
+ });
58
+ server.tool('terminate_session', 'Kill a session and free its resources.', {
59
+ session_id: z.string().describe('Session ID'),
60
+ }, async ({ session_id }) => {
61
+ const result = await api('DELETE', `/session/${session_id}`);
62
+ return text(result);
63
+ });
64
+ server.tool('install_packages', 'Install pip packages into a running session without restarting it.', {
65
+ session_id: z.string().describe('Session ID'),
66
+ packages: z.array(z.string()).describe('Package names to install'),
67
+ }, async ({ session_id, packages }) => {
68
+ const result = await api('POST', `/session/${session_id}/install`, { packages });
69
+ return text(result);
70
+ });
71
+ // ── Checkpoint tools ─────────────────────────────────────────────────────────
72
+ server.tool('create_checkpoint', 'Snapshot the entire Python interpreter state (all variables, imports, models). Can be restored later or forked into multiple sessions.', {
73
+ session_id: z.string().describe('Session ID to checkpoint'),
74
+ name: z.string().optional().describe('Human-readable name for the checkpoint'),
75
+ }, async ({ session_id, name }) => {
76
+ const result = await api('POST', `/session/${session_id}/checkpoint`, { name });
77
+ return text(result);
78
+ });
79
+ server.tool('restore_checkpoint', 'Load a previously saved checkpoint into a running session. All variables are restored.', {
80
+ session_id: z.string().describe('Session to restore into'),
81
+ checkpoint_id: z.string().describe('Checkpoint ID to restore'),
82
+ }, async ({ session_id, checkpoint_id }) => {
83
+ const result = await api('POST', `/session/${session_id}/restore/${checkpoint_id}`);
84
+ return text(result);
85
+ });
86
+ server.tool('list_checkpoints', 'List all saved checkpoints for your API key.', {}, async () => {
87
+ const result = await api('GET', '/checkpoints');
88
+ return text(result);
89
+ });
90
+ server.tool('delete_checkpoint', 'Delete a checkpoint and free the storage it uses.', {
91
+ checkpoint_id: z.string().describe('Checkpoint ID to delete'),
92
+ }, async ({ checkpoint_id }) => {
93
+ const result = await api('DELETE', `/checkpoints/${checkpoint_id}`);
94
+ return text(result);
95
+ });
96
+ // ── Swarm tools ──────────────────────────────────────────────────────────────
97
+ server.tool('create_swarm', 'Run parallel MapReduce across N workers. Setup code runs once and is cloned to all workers. Each worker processes one data partition.', {
98
+ template_code: z.string().optional().describe('Setup code that runs once and gets cloned to all workers (imports, model loading)'),
99
+ map_fn: z.string().describe('Code each worker runs. Has access to `partition` variable.'),
100
+ data: z.array(z.any()).describe('Array of items, one per worker. Each item is injected as `partition`.'),
101
+ reduce_fn: z.string().optional().describe('Code to combine results. Has access to `results` list.'),
102
+ webhook_url: z.string().optional().describe('URL to POST results when swarm finishes'),
103
+ }, async ({ template_code, map_fn, data, reduce_fn, webhook_url }) => {
104
+ const result = await api('POST', '/swarm', { template_code, map_fn, data, reduce_fn, webhook_url });
105
+ return text(result);
106
+ });
107
+ server.tool('get_swarm', 'Check swarm progress and get results.', {
108
+ swarm_id: z.string().describe('Swarm ID'),
109
+ }, async ({ swarm_id }) => {
110
+ const result = await api('GET', `/swarm/${swarm_id}`);
111
+ return text(result);
112
+ });
113
+ // ── Workspace tools ──────────────────────────────────────────────────────────
114
+ server.tool('create_workspace', 'Create a persistent shared Python environment. Multiple API keys can execute in the same workspace. State auto-saves.', {
115
+ name: z.string().describe('Workspace name'),
116
+ }, async ({ name }) => {
117
+ const result = await api('POST', '/workspaces', { name });
118
+ return text(result);
119
+ });
120
+ server.tool('execute_in_workspace', 'Run code in a shared workspace. All members see the same state.', {
121
+ workspace_id: z.string().describe('Workspace ID'),
122
+ code: z.string().describe('Python code to execute'),
123
+ }, async ({ workspace_id, code }) => {
124
+ const result = await api('POST', `/workspaces/${workspace_id}/execute`, { code });
125
+ return text(result);
126
+ });
127
+ server.tool('list_workspaces', 'List all workspaces you own or have access to.', {}, async () => {
128
+ const result = await api('GET', '/workspaces');
129
+ return text(result);
130
+ });
131
+ // ── File tools ───────────────────────────────────────────────────────────────
132
+ server.tool('upload_file', 'Upload a file to a session workspace. Your code can then read it by filename.', {
133
+ session_id: z.string().describe('Session ID'),
134
+ filename: z.string().describe('Filename in the workspace'),
135
+ content_base64: z.string().describe('File content, base64 encoded'),
136
+ }, async ({ session_id, filename, content_base64 }) => {
137
+ const result = await api('POST', `/session/${session_id}/files`, { filename, content: content_base64 });
138
+ return text(result);
139
+ });
140
+ server.tool('download_file', 'Download a file from a session workspace. Returns base64 encoded content.', {
141
+ session_id: z.string().describe('Session ID'),
142
+ filename: z.string().describe('Filename to download'),
143
+ }, async ({ session_id, filename }) => {
144
+ const result = await api('GET', `/session/${session_id}/output/${filename}`);
145
+ return text(result);
146
+ });
147
+ // ── Usage tool ───────────────────────────────────────────────────────────────
148
+ server.tool('get_usage', 'Check your current API usage, plan limits, and billing status.', {}, async () => {
149
+ const result = await api('GET', '/usage');
150
+ return text(result);
151
+ });
152
+ // ── Start ────────────────────────────────────────────────────────────────────
153
+ async function main() {
154
+ const transport = new StdioServerTransport();
155
+ await server.connect(transport);
156
+ console.error('greed-compute MCP server running on stdio');
157
+ }
158
+ main().catch((err) => {
159
+ console.error('Fatal:', err);
160
+ process.exit(1);
161
+ });
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "greed-compute-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for greed-compute — gives LLMs stateful Python execution",
5
+ "bin": {
6
+ "greed-compute-mcp": "./dist/index.js"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "keywords": ["mcp", "greed-compute", "python", "ai-agents", "code-execution"],
16
+ "author": "Aditya Khalkar",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.12.1"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "^5.7.0",
23
+ "@types/node": "^22.0.0"
24
+ }
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
5
+ import { z } from 'zod'
6
+
7
+ const BASE = process.env.GREED_COMPUTE_URL || 'https://compute.deep-ml.com'
8
+ const API_KEY = process.env.GREED_API_KEY || ''
9
+
10
+ if (!API_KEY) {
11
+ console.error('greed-compute-mcp: set GREED_API_KEY environment variable')
12
+ process.exit(1)
13
+ }
14
+
15
+ // ── HTTP helper ──────────────────────────────────────────────────────────────
16
+
17
+ async function api(method: string, path: string, body?: unknown): Promise<unknown> {
18
+ const res = await fetch(`${BASE}/v1${path}`, {
19
+ method,
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ 'X-API-Key': API_KEY,
23
+ },
24
+ body: body ? JSON.stringify(body) : undefined,
25
+ })
26
+ const text = await res.text()
27
+ try { return JSON.parse(text) } catch { return text }
28
+ }
29
+
30
+ function text(data: unknown): { content: { type: 'text'; text: string }[] } {
31
+ return { content: [{ type: 'text' as const, text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }] }
32
+ }
33
+
34
+ // ── Server ───────────────────────────────────────────────────────────────────
35
+
36
+ const server = new McpServer({
37
+ name: 'greed-compute',
38
+ version: '0.1.0',
39
+ })
40
+
41
+ // ── Session tools ────────────────────────────────────────────────────────────
42
+
43
+ server.tool(
44
+ 'create_session',
45
+ 'Create a stateful Python session. State persists between execute calls.',
46
+ {
47
+ template: z.enum(['blank', 'data-science', 'machine-learning', 'web-scraping']).default('blank').describe('Pre-loaded library template'),
48
+ ttl_seconds: z.number().optional().describe('Session lifetime in seconds (default 120)'),
49
+ },
50
+ async ({ template, ttl_seconds }) => {
51
+ const result = await api('POST', '/session/create', { template, ttl_seconds })
52
+ return text(result)
53
+ }
54
+ )
55
+
56
+ server.tool(
57
+ 'execute_code',
58
+ 'Run Python code in a session. Variables persist between calls. Returns stdout, result, errors, and plots.',
59
+ {
60
+ session_id: z.string().describe('Session ID from create_session'),
61
+ code: z.string().describe('Python code to execute'),
62
+ },
63
+ async ({ session_id, code }) => {
64
+ const result = await api('POST', `/session/${session_id}/execute`, { code })
65
+ return text(result)
66
+ }
67
+ )
68
+
69
+ server.tool(
70
+ 'session_status',
71
+ 'Check if a session is alive and how much TTL remains.',
72
+ {
73
+ session_id: z.string().describe('Session ID'),
74
+ },
75
+ async ({ session_id }) => {
76
+ const result = await api('GET', `/session/${session_id}/status`)
77
+ return text(result)
78
+ }
79
+ )
80
+
81
+ server.tool(
82
+ 'terminate_session',
83
+ 'Kill a session and free its resources.',
84
+ {
85
+ session_id: z.string().describe('Session ID'),
86
+ },
87
+ async ({ session_id }) => {
88
+ const result = await api('DELETE', `/session/${session_id}`)
89
+ return text(result)
90
+ }
91
+ )
92
+
93
+ server.tool(
94
+ 'install_packages',
95
+ 'Install pip packages into a running session without restarting it.',
96
+ {
97
+ session_id: z.string().describe('Session ID'),
98
+ packages: z.array(z.string()).describe('Package names to install'),
99
+ },
100
+ async ({ session_id, packages }) => {
101
+ const result = await api('POST', `/session/${session_id}/install`, { packages })
102
+ return text(result)
103
+ }
104
+ )
105
+
106
+ // ── Checkpoint tools ─────────────────────────────────────────────────────────
107
+
108
+ server.tool(
109
+ 'create_checkpoint',
110
+ 'Snapshot the entire Python interpreter state (all variables, imports, models). Can be restored later or forked into multiple sessions.',
111
+ {
112
+ session_id: z.string().describe('Session ID to checkpoint'),
113
+ name: z.string().optional().describe('Human-readable name for the checkpoint'),
114
+ },
115
+ async ({ session_id, name }) => {
116
+ const result = await api('POST', `/session/${session_id}/checkpoint`, { name })
117
+ return text(result)
118
+ }
119
+ )
120
+
121
+ server.tool(
122
+ 'restore_checkpoint',
123
+ 'Load a previously saved checkpoint into a running session. All variables are restored.',
124
+ {
125
+ session_id: z.string().describe('Session to restore into'),
126
+ checkpoint_id: z.string().describe('Checkpoint ID to restore'),
127
+ },
128
+ async ({ session_id, checkpoint_id }) => {
129
+ const result = await api('POST', `/session/${session_id}/restore/${checkpoint_id}`)
130
+ return text(result)
131
+ }
132
+ )
133
+
134
+ server.tool(
135
+ 'list_checkpoints',
136
+ 'List all saved checkpoints for your API key.',
137
+ {},
138
+ async () => {
139
+ const result = await api('GET', '/checkpoints')
140
+ return text(result)
141
+ }
142
+ )
143
+
144
+ server.tool(
145
+ 'delete_checkpoint',
146
+ 'Delete a checkpoint and free the storage it uses.',
147
+ {
148
+ checkpoint_id: z.string().describe('Checkpoint ID to delete'),
149
+ },
150
+ async ({ checkpoint_id }) => {
151
+ const result = await api('DELETE', `/checkpoints/${checkpoint_id}`)
152
+ return text(result)
153
+ }
154
+ )
155
+
156
+ // ── Swarm tools ──────────────────────────────────────────────────────────────
157
+
158
+ server.tool(
159
+ 'create_swarm',
160
+ 'Run parallel MapReduce across N workers. Setup code runs once and is cloned to all workers. Each worker processes one data partition.',
161
+ {
162
+ template_code: z.string().optional().describe('Setup code that runs once and gets cloned to all workers (imports, model loading)'),
163
+ map_fn: z.string().describe('Code each worker runs. Has access to `partition` variable.'),
164
+ data: z.array(z.any()).describe('Array of items, one per worker. Each item is injected as `partition`.'),
165
+ reduce_fn: z.string().optional().describe('Code to combine results. Has access to `results` list.'),
166
+ webhook_url: z.string().optional().describe('URL to POST results when swarm finishes'),
167
+ },
168
+ async ({ template_code, map_fn, data, reduce_fn, webhook_url }) => {
169
+ const result = await api('POST', '/swarm', { template_code, map_fn, data, reduce_fn, webhook_url })
170
+ return text(result)
171
+ }
172
+ )
173
+
174
+ server.tool(
175
+ 'get_swarm',
176
+ 'Check swarm progress and get results.',
177
+ {
178
+ swarm_id: z.string().describe('Swarm ID'),
179
+ },
180
+ async ({ swarm_id }) => {
181
+ const result = await api('GET', `/swarm/${swarm_id}`)
182
+ return text(result)
183
+ }
184
+ )
185
+
186
+ // ── Workspace tools ──────────────────────────────────────────────────────────
187
+
188
+ server.tool(
189
+ 'create_workspace',
190
+ 'Create a persistent shared Python environment. Multiple API keys can execute in the same workspace. State auto-saves.',
191
+ {
192
+ name: z.string().describe('Workspace name'),
193
+ },
194
+ async ({ name }) => {
195
+ const result = await api('POST', '/workspaces', { name })
196
+ return text(result)
197
+ }
198
+ )
199
+
200
+ server.tool(
201
+ 'execute_in_workspace',
202
+ 'Run code in a shared workspace. All members see the same state.',
203
+ {
204
+ workspace_id: z.string().describe('Workspace ID'),
205
+ code: z.string().describe('Python code to execute'),
206
+ },
207
+ async ({ workspace_id, code }) => {
208
+ const result = await api('POST', `/workspaces/${workspace_id}/execute`, { code })
209
+ return text(result)
210
+ }
211
+ )
212
+
213
+ server.tool(
214
+ 'list_workspaces',
215
+ 'List all workspaces you own or have access to.',
216
+ {},
217
+ async () => {
218
+ const result = await api('GET', '/workspaces')
219
+ return text(result)
220
+ }
221
+ )
222
+
223
+ // ── File tools ───────────────────────────────────────────────────────────────
224
+
225
+ server.tool(
226
+ 'upload_file',
227
+ 'Upload a file to a session workspace. Your code can then read it by filename.',
228
+ {
229
+ session_id: z.string().describe('Session ID'),
230
+ filename: z.string().describe('Filename in the workspace'),
231
+ content_base64: z.string().describe('File content, base64 encoded'),
232
+ },
233
+ async ({ session_id, filename, content_base64 }) => {
234
+ const result = await api('POST', `/session/${session_id}/files`, { filename, content: content_base64 })
235
+ return text(result)
236
+ }
237
+ )
238
+
239
+ server.tool(
240
+ 'download_file',
241
+ 'Download a file from a session workspace. Returns base64 encoded content.',
242
+ {
243
+ session_id: z.string().describe('Session ID'),
244
+ filename: z.string().describe('Filename to download'),
245
+ },
246
+ async ({ session_id, filename }) => {
247
+ const result = await api('GET', `/session/${session_id}/output/${filename}`)
248
+ return text(result)
249
+ }
250
+ )
251
+
252
+ // ── Usage tool ───────────────────────────────────────────────────────────────
253
+
254
+ server.tool(
255
+ 'get_usage',
256
+ 'Check your current API usage, plan limits, and billing status.',
257
+ {},
258
+ async () => {
259
+ const result = await api('GET', '/usage')
260
+ return text(result)
261
+ }
262
+ )
263
+
264
+ // ── Start ────────────────────────────────────────────────────────────────────
265
+
266
+ async function main() {
267
+ const transport = new StdioServerTransport()
268
+ await server.connect(transport)
269
+ console.error('greed-compute MCP server running on stdio')
270
+ }
271
+
272
+ main().catch((err) => {
273
+ console.error('Fatal:', err)
274
+ process.exit(1)
275
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true
12
+ },
13
+ "include": ["src/**/*"]
14
+ }