mcpbundles 1.0.0__tar.gz

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 (54) hide show
  1. mcpbundles-1.0.0/LICENSE +22 -0
  2. mcpbundles-1.0.0/MANIFEST.in +5 -0
  3. mcpbundles-1.0.0/MESSAGE_PROTOCOL.md +489 -0
  4. mcpbundles-1.0.0/PKG-INFO +276 -0
  5. mcpbundles-1.0.0/README.md +239 -0
  6. mcpbundles-1.0.0/mcpbundles/__init__.py +4 -0
  7. mcpbundles-1.0.0/mcpbundles/args.py +93 -0
  8. mcpbundles-1.0.0/mcpbundles/auth.py +257 -0
  9. mcpbundles-1.0.0/mcpbundles/cli.py +92 -0
  10. mcpbundles-1.0.0/mcpbundles/commands/__init__.py +1 -0
  11. mcpbundles-1.0.0/mcpbundles/commands/agent.py +384 -0
  12. mcpbundles-1.0.0/mcpbundles/commands/auth.py +77 -0
  13. mcpbundles-1.0.0/mcpbundles/commands/config_cmd.py +73 -0
  14. mcpbundles-1.0.0/mcpbundles/commands/conn.py +124 -0
  15. mcpbundles-1.0.0/mcpbundles/commands/discovery.py +192 -0
  16. mcpbundles-1.0.0/mcpbundles/commands/execution.py +218 -0
  17. mcpbundles-1.0.0/mcpbundles/commands/install_completion.py +147 -0
  18. mcpbundles-1.0.0/mcpbundles/commands/proxy_cmd.py +138 -0
  19. mcpbundles-1.0.0/mcpbundles/commands/shell_cmd.py +316 -0
  20. mcpbundles-1.0.0/mcpbundles/completion.py +99 -0
  21. mcpbundles-1.0.0/mcpbundles/config.py +210 -0
  22. mcpbundles-1.0.0/mcpbundles/connections.py +142 -0
  23. mcpbundles-1.0.0/mcpbundles/daemon.py +234 -0
  24. mcpbundles-1.0.0/mcpbundles/exit_codes.py +14 -0
  25. mcpbundles-1.0.0/mcpbundles/manifest.py +73 -0
  26. mcpbundles-1.0.0/mcpbundles/mcp_client.py +113 -0
  27. mcpbundles-1.0.0/mcpbundles/output.py +206 -0
  28. mcpbundles-1.0.0/mcpbundles/schema.py +90 -0
  29. mcpbundles-1.0.0/mcpbundles/service_manager.py +510 -0
  30. mcpbundles-1.0.0/mcpbundles/services/__init__.py +8 -0
  31. mcpbundles-1.0.0/mcpbundles/services/browser.py +165 -0
  32. mcpbundles-1.0.0/mcpbundles/services/claude_code.py +341 -0
  33. mcpbundles-1.0.0/mcpbundles/services/sqlite.py +156 -0
  34. mcpbundles-1.0.0/mcpbundles/tunnel.py +719 -0
  35. mcpbundles-1.0.0/mcpbundles.egg-info/PKG-INFO +276 -0
  36. mcpbundles-1.0.0/mcpbundles.egg-info/SOURCES.txt +52 -0
  37. mcpbundles-1.0.0/mcpbundles.egg-info/dependency_links.txt +1 -0
  38. mcpbundles-1.0.0/mcpbundles.egg-info/entry_points.txt +2 -0
  39. mcpbundles-1.0.0/mcpbundles.egg-info/requires.txt +17 -0
  40. mcpbundles-1.0.0/mcpbundles.egg-info/top_level.txt +1 -0
  41. mcpbundles-1.0.0/pyproject.toml +60 -0
  42. mcpbundles-1.0.0/setup.cfg +4 -0
  43. mcpbundles-1.0.0/tests/test_args.py +104 -0
  44. mcpbundles-1.0.0/tests/test_auth.py +129 -0
  45. mcpbundles-1.0.0/tests/test_browser_service.py +214 -0
  46. mcpbundles-1.0.0/tests/test_cli.py +143 -0
  47. mcpbundles-1.0.0/tests/test_config.py +151 -0
  48. mcpbundles-1.0.0/tests/test_connections.py +141 -0
  49. mcpbundles-1.0.0/tests/test_daemon.py +250 -0
  50. mcpbundles-1.0.0/tests/test_mcp_client.py +119 -0
  51. mcpbundles-1.0.0/tests/test_new_features.py +361 -0
  52. mcpbundles-1.0.0/tests/test_output.py +157 -0
  53. mcpbundles-1.0.0/tests/test_service_manager.py +268 -0
  54. mcpbundles-1.0.0/tests/test_tunnel.py +382 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tony Lewis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,5 @@
1
+ include README.md
2
+ include LICENSE
3
+ include MESSAGE_PROTOCOL.md
4
+ recursive-include mcpbundles *.py
5
+
@@ -0,0 +1,489 @@
1
+ # MCPBundles Desktop Proxy - Message Protocol
2
+
3
+ ## Overview
4
+
5
+ The proxy is a **passive, message-driven agent** that responds to commands from the backend through a WebSocket tunnel. All intelligence lives in the web app.
6
+
7
+ ## Message Types
8
+
9
+ ### 1. Service Discovery
10
+
11
+ **Web → Proxy:**
12
+ ```json
13
+ {
14
+ "type": "service_discovery",
15
+ "ports": [5432, 5433, 6379, 3306, 27017, 9200, 9223]
16
+ }
17
+ ```
18
+
19
+ **Proxy → Web:**
20
+ ```json
21
+ {
22
+ "type": "discovery_result",
23
+ "ports": {
24
+ "5432": {
25
+ "available": true,
26
+ "type": "postgresql"
27
+ },
28
+ "5433": {
29
+ "available": true,
30
+ "type": "postgresql"
31
+ },
32
+ "6379": {
33
+ "available": false,
34
+ "type": null
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ **Action:** Proxy scans specified ports on localhost and reports which are listening.
41
+
42
+ ---
43
+
44
+ ### 2. Service Verification
45
+
46
+ **Web → Proxy:**
47
+ ```json
48
+ {
49
+ "type": "service_verify",
50
+ "target": "localhost:5432"
51
+ }
52
+ ```
53
+
54
+ **Proxy → Web:**
55
+ ```json
56
+ {
57
+ "type": "verify_result",
58
+ "verified": true,
59
+ "target": "localhost:5432",
60
+ "type": "postgresql"
61
+ }
62
+ ```
63
+
64
+ Or if failed:
65
+ ```json
66
+ {
67
+ "type": "verify_result",
68
+ "verified": false,
69
+ "target": "localhost:5432",
70
+ "error": "Connection refused"
71
+ }
72
+ ```
73
+
74
+ **Action:** Proxy attempts actual connection to verify service is reachable.
75
+
76
+ ---
77
+
78
+ ### 3. Start Service
79
+
80
+ **Web → Proxy:**
81
+ ```json
82
+ {
83
+ "type": "service_start",
84
+ "service": "browser",
85
+ "config": {
86
+ "headless": false,
87
+ "port": 9223
88
+ }
89
+ }
90
+ ```
91
+
92
+ **Proxy → Web:**
93
+ ```json
94
+ {
95
+ "type": "service_started",
96
+ "started": true,
97
+ "service": "browser",
98
+ "port": 9223,
99
+ "mode": "visible",
100
+ "ws_endpoint": "ws://127.0.0.1:9223/abc123def456"
101
+ }
102
+ ```
103
+
104
+ Or if failed:
105
+ ```json
106
+ {
107
+ "type": "service_started",
108
+ "started": false,
109
+ "error": "Playwright not installed. Install with: pip install mcpbundles[browser]"
110
+ }
111
+ ```
112
+
113
+ **Important:** The `ws_endpoint` field contains the full WebSocket URL for connecting to the browser via Playwright's `connect()` method. The backend must:
114
+ 1. Create a TCP tunnel to `localhost:9223`
115
+ 2. Rewrite the WebSocket URL to use the tunnel endpoint (preserving the path)
116
+ 3. Use `playwright.chromium.connect(ws_endpoint=rewritten_url)` instead of `connect_over_cdp()`
117
+
118
+ **Action:** Proxy starts the requested service (currently only browser supported) in server mode.
119
+
120
+ ---
121
+
122
+ ### 4. Stop Service
123
+
124
+ **Web → Proxy:**
125
+ ```json
126
+ {
127
+ "type": "service_stop",
128
+ "service": "browser"
129
+ }
130
+ ```
131
+
132
+ **Proxy → Web:**
133
+ ```json
134
+ {
135
+ "type": "service_stopped",
136
+ "stopped": true,
137
+ "service": "browser"
138
+ }
139
+ ```
140
+
141
+ **Action:** Proxy stops the running service.
142
+
143
+ ---
144
+
145
+ ### 5. Update Service Configuration
146
+
147
+ **Web → Proxy:**
148
+ ```json
149
+ {
150
+ "type": "service_update",
151
+ "service": "browser",
152
+ "config": {
153
+ "headless": true
154
+ }
155
+ }
156
+ ```
157
+
158
+ **Proxy → Web:**
159
+ ```json
160
+ {
161
+ "type": "service_updated",
162
+ "updated": true,
163
+ "service": "browser",
164
+ "mode": "hidden"
165
+ }
166
+ ```
167
+
168
+ **Action:** Proxy updates service configuration (may restart service if needed).
169
+
170
+ ---
171
+
172
+ ### 6. HTTP Request Forwarding (Existing)
173
+
174
+ **Web → Proxy:**
175
+ ```json
176
+ {
177
+ "type": "http_request",
178
+ "request_id": "req_abc123",
179
+ "method": "POST",
180
+ "path": "/api/users",
181
+ "target": "localhost:5432",
182
+ "headers": {"Content-Type": "application/json"},
183
+ "body": "{\"name\": \"John\"}"
184
+ }
185
+ ```
186
+
187
+ **Proxy → Web:**
188
+ ```json
189
+ {
190
+ "type": "http_response",
191
+ "request_id": "req_abc123",
192
+ "status": 200,
193
+ "headers": {"Content-Type": "application/json"},
194
+ "body": "{\"id\": 123, \"name\": \"John\"}"
195
+ }
196
+ ```
197
+
198
+ **Action:** Proxy forwards HTTP request to localhost service and returns response.
199
+
200
+ ---
201
+
202
+ ### 7. Status Request
203
+
204
+ **Web → Proxy:**
205
+ ```json
206
+ {
207
+ "type": "status_request"
208
+ }
209
+ ```
210
+
211
+ **Proxy → Web:**
212
+ ```json
213
+ {
214
+ "type": "status_update",
215
+ "services": {
216
+ "browser": {
217
+ "enabled": true,
218
+ "headless": false,
219
+ "port": 9223,
220
+ "mode": "visible"
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ **Action:** Proxy reports current status of all services.
227
+
228
+ ---
229
+
230
+ ### 8. TCP Tunnel Open
231
+
232
+ **Web → Proxy:**
233
+ ```json
234
+ {
235
+ "type": "tunnel_open",
236
+ "tunnel_id": "tun_abc123",
237
+ "target": "localhost:5432"
238
+ }
239
+ ```
240
+
241
+ **Proxy → Web (Success):**
242
+ ```json
243
+ {
244
+ "type": "tunnel_ready",
245
+ "tunnel_id": "tun_abc123",
246
+ "target": "localhost:5432"
247
+ }
248
+ ```
249
+
250
+ **Proxy → Web (Error):**
251
+ ```json
252
+ {
253
+ "type": "tunnel_error",
254
+ "tunnel_id": "tun_abc123",
255
+ "error": "Browser service not running on localhost:9223. Start it first with service_start message.",
256
+ "error_code": "CONNECTION_REFUSED",
257
+ "target": "localhost:9223",
258
+ "suggestion": "Send: {\"type\":\"service_start\",\"service\":\"browser\",\"config\":{\"headless\":false,\"port\":9223}}"
259
+ }
260
+ ```
261
+
262
+ **Error Codes:**
263
+ - `SERVICE_NOT_RUNNING` - Service needs to be started first (proactive check)
264
+ - `CONNECTION_REFUSED` - Connection refused (port not listening)
265
+ - `CONNECTION_TIMEOUT` - Connection timeout (service not responding)
266
+ - `INVALID_TARGET` - Invalid target format
267
+ - `INVALID_HOST` - Non-localhost target (security)
268
+ - `INVALID_PORT` - Invalid port number
269
+ - `TUNNEL_OPEN_FAILED` - Other tunnel open errors
270
+
271
+ **Action:** Proxy opens a TCP connection to the target and prepares to forward raw bytes bidirectionally.
272
+
273
+ ---
274
+
275
+ ### 9. TCP Tunnel Data
276
+
277
+ **Web → Proxy:**
278
+ ```json
279
+ {
280
+ "type": "tunnel_data",
281
+ "tunnel_id": "tun_abc123",
282
+ "data": "SGVsbG8sIFdvcmxkIQ=="
283
+ }
284
+ ```
285
+
286
+ **Proxy → Web:**
287
+ ```json
288
+ {
289
+ "type": "tunnel_data",
290
+ "tunnel_id": "tun_abc123",
291
+ "data": "UE9ORw=="
292
+ }
293
+ ```
294
+
295
+ **Action:** Forwards base64-encoded raw bytes through the tunnel. Data from Web is written to TCP socket, data from TCP socket is sent to Web.
296
+
297
+ ---
298
+
299
+ ### 10. TCP Tunnel Close
300
+
301
+ **Web → Proxy:**
302
+ ```json
303
+ {
304
+ "type": "tunnel_close",
305
+ "tunnel_id": "tun_abc123"
306
+ }
307
+ ```
308
+
309
+ **Proxy → Web:**
310
+ ```json
311
+ {
312
+ "type": "tunnel_closed",
313
+ "tunnel_id": "tun_abc123"
314
+ }
315
+ ```
316
+
317
+ **Action:** Closes the TCP connection and cleans up tunnel resources.
318
+
319
+ ---
320
+
321
+ ## Service Types Supported
322
+
323
+ ### Browser Service
324
+ - **Port:** 9223 (default)
325
+ - **States:** Started/Stopped
326
+ - **Modes:** Visible (headed) / Hidden (headless)
327
+ - **Connection:** Playwright browser server mode (WebSocket)
328
+ - **Requires:** `pip install mcpbundles[browser]` + `playwright install chromium`
329
+ - **Backend connects via:** `playwright.chromium.connect(ws_endpoint=...)` (NOT `connect_over_cdp`)
330
+
331
+ ### SQLite Service
332
+ - **Port:** 9999 (default)
333
+ - **States:** Started/Stopped
334
+ - **Connection:** HTTP API for SQLite database file access
335
+ - **Endpoints:** `POST /query` - Execute SQL queries on local database files
336
+ - **Requires:** `pip install mcpbundles`
337
+
338
+ ### Claude Code Service
339
+ - **Port:** 9300 (default)
340
+ - **States:** Started/Stopped
341
+ - **Connection:** HTTP API for Claude Code CLI execution
342
+ - **Endpoints:**
343
+ - `POST /run` - Execute Claude Code CLI with prompt in workFolder
344
+ - `POST /validate` - Validate Claude CLI works and workFolder is accessible
345
+ - **Requires:** Claude CLI installed (`npm install -g @anthropic-ai/claude-cli` or `~/.claude/local/claude`)
346
+ - **Config:** `{"port": 9300}` (optional port override)
347
+
348
+ ### Database Services (Discovery Only)
349
+ Proxy can discover and verify, but doesn't manage lifecycle:
350
+ - PostgreSQL (5432, 5433)
351
+ - MySQL (3306)
352
+ - Redis (6379)
353
+ - MongoDB (27017)
354
+ - Elasticsearch (9200)
355
+
356
+ ### TCP Tunneling (Protocol Agnostic)
357
+ Proxy can forward raw TCP connections for ANY service:
358
+ - **Fully transparent** - Protocol-agnostic byte forwarding
359
+ - **Works with any TCP service** - PostgreSQL, Redis, MongoDB, MySQL, etc.
360
+ - **Bidirectional** - Data flows both directions through WebSocket
361
+ - **No drivers needed** - Proxy just forwards bytes
362
+ - **Security** - Only localhost targets allowed
363
+
364
+ **Use case:** Cloud MCP servers can connect to local databases as if they were remote services. For example, asyncpg running in the cloud can connect through the tunnel and communicate directly with local PostgreSQL using native wire protocol.
365
+
366
+ ---
367
+
368
+ ## Port Detection
369
+
370
+ Proxy automatically detects service type by port:
371
+
372
+ | Port | Service Type |
373
+ |------|-------------|
374
+ | 5432, 5433 | postgresql |
375
+ | 3306 | mysql |
376
+ | 6379 | redis |
377
+ | 27017 | mongodb |
378
+ | 9200 | elasticsearch |
379
+ | 9223 | browser |
380
+ | 9300 | claude_code |
381
+ | 9999 | sqlite |
382
+ | other | unknown |
383
+
384
+ ---
385
+
386
+ ## Error Handling
387
+
388
+ All operations return error information if they fail:
389
+
390
+ ```json
391
+ {
392
+ "type": "..._result",
393
+ "error": "Human-readable error message"
394
+ }
395
+ ```
396
+
397
+ Common errors:
398
+ - "Connection timeout"
399
+ - "Connection refused"
400
+ - "Service manager not available"
401
+ - "Playwright not installed..."
402
+ - "Unknown service: {name}"
403
+
404
+ ---
405
+
406
+ ## User Experience Flow
407
+
408
+ 1. **User goes to MCPBundles.com → Local Services**
409
+ 2. **Web sends:** `service_discovery` with common ports
410
+ 3. **Proxy responds:** Which ports are listening
411
+ 4. **Web UI shows:** "PostgreSQL found on localhost:5432"
412
+ 5. **User clicks [+ Add Service]**
413
+ 6. **Web sends:** `service_verify` for localhost:5432
414
+ 7. **Proxy responds:** Verified ✓
415
+ 8. **Web automatically creates Provider** with base_url
416
+ 9. **Done - User never touched terminal**
417
+
418
+ ---
419
+
420
+ ## Browser Service Flow
421
+
422
+ 1. **User clicks [Start Browser] with "Visible" mode**
423
+ 2. **Web sends:** `service_start` with `headless: false`
424
+ 3. **Proxy starts browser** in server mode - Window pops up on user's screen
425
+ 4. **Proxy responds:** Started ✓, mode: visible, `ws_endpoint: ws://127.0.0.1:9223/...`
426
+ 5. **Web creates TCP tunnel** to `localhost:9223`
427
+ 6. **Web rewrites WebSocket URL** to use tunnel endpoint (preserving path)
428
+ 7. **Web connects:** `playwright.chromium.connect(ws_endpoint=tunneled_url)`
429
+ 8. **Browser automation works** through WebSocket tunnel
430
+ 9. **User toggles to "Hidden"**
431
+ 10. **Web sends:** `service_update` with `headless: true`
432
+ 11. **Proxy restarts browser** in headless mode
433
+ 12. **Browser window disappears**
434
+
435
+ ---
436
+
437
+ ## Implementation Notes
438
+
439
+ ### Proxy Side (Local)
440
+ - Completely passive - only responds to messages
441
+ - No configuration files needed
442
+ - No UI - pure message handler
443
+ - All logic in service_manager.py and tunnel.py
444
+ - **Never needs updates for new features** - just responds to new messages
445
+
446
+ ### Backend Side (Cloud)
447
+ - Active intelligence - decides what to discover/verify/start
448
+ - Maintains state of user's services
449
+ - Auto-creates Providers when services verified
450
+ - Shows UI for service management
451
+ - **All new features added here**
452
+
453
+ ---
454
+
455
+ ## Adding New Services
456
+
457
+ To add support for a new service (e.g., Redis management):
458
+
459
+ **Proxy:** No changes needed - already handles start/stop/update for any service name
460
+
461
+ **Backend:**
462
+ 1. Add UI for Redis service
463
+ 2. Send appropriate messages:
464
+ - `service_start` with `service: "redis"`
465
+ - `service_stop` with `service: "redis"`
466
+
467
+ **That's it.** The protocol is generic - proxy just executes commands.
468
+
469
+ ---
470
+
471
+ ## Security
472
+
473
+ - **Discovery:** Only scans localhost, never remote hosts
474
+ - **Verification:** Only connects to localhost
475
+ - **HTTP Forwarding:** Validates target starts with "localhost:" or "127.0.0.1:"
476
+ - **Services:** Browser is sandboxed, only accesses what user's browser can access
477
+
478
+ ---
479
+
480
+ ## Future Extensions
481
+
482
+ Easy to add without proxy changes:
483
+ - `service_restart` - Restart a service
484
+ - `service_logs` - Get logs from a service
485
+ - `service_health` - Health check endpoint
486
+ - `batch_discover` - Discover multiple ranges at once
487
+
488
+ Just send new message types - proxy handles them.
489
+