moonbridge 0.5.2__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- moonbridge/__init__.py +2 -2
- moonbridge/adapters/base.py +30 -1
- moonbridge/adapters/codex.py +1 -0
- moonbridge/adapters/kimi.py +1 -0
- moonbridge/server.py +48 -152
- moonbridge/tools.py +334 -0
- {moonbridge-0.5.2.dist-info → moonbridge-0.6.0.dist-info}/METADATA +51 -19
- moonbridge-0.6.0.dist-info/RECORD +14 -0
- moonbridge-0.5.2.dist-info/RECORD +0 -13
- {moonbridge-0.5.2.dist-info → moonbridge-0.6.0.dist-info}/WHEEL +0 -0
- {moonbridge-0.5.2.dist-info → moonbridge-0.6.0.dist-info}/entry_points.txt +0 -0
- {moonbridge-0.5.2.dist-info → moonbridge-0.6.0.dist-info}/licenses/LICENSE +0 -0
moonbridge/__init__.py
CHANGED
moonbridge/adapters/base.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Protocol
|
|
2
|
+
from typing import Any, Protocol
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@dataclass(frozen=True)
|
|
@@ -18,6 +18,35 @@ class AdapterConfig:
|
|
|
18
18
|
default_timeout: int = 600
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class AgentResult:
|
|
23
|
+
"""Agent execution result."""
|
|
24
|
+
|
|
25
|
+
status: str
|
|
26
|
+
output: str
|
|
27
|
+
stderr: str | None
|
|
28
|
+
returncode: int
|
|
29
|
+
duration_ms: int
|
|
30
|
+
agent_index: int
|
|
31
|
+
message: str | None = None
|
|
32
|
+
raw: dict[str, Any] | None = None
|
|
33
|
+
|
|
34
|
+
def to_dict(self) -> dict[str, Any]:
|
|
35
|
+
payload: dict[str, Any] = {
|
|
36
|
+
"status": self.status,
|
|
37
|
+
"output": self.output,
|
|
38
|
+
"stderr": self.stderr,
|
|
39
|
+
"returncode": self.returncode,
|
|
40
|
+
"duration_ms": self.duration_ms,
|
|
41
|
+
"agent_index": self.agent_index,
|
|
42
|
+
}
|
|
43
|
+
if self.message is not None:
|
|
44
|
+
payload["message"] = self.message
|
|
45
|
+
if self.raw is not None:
|
|
46
|
+
payload["raw"] = self.raw
|
|
47
|
+
return payload
|
|
48
|
+
|
|
49
|
+
|
|
21
50
|
class CLIAdapter(Protocol):
|
|
22
51
|
"""Protocol for CLI backend adapters."""
|
|
23
52
|
|
moonbridge/adapters/codex.py
CHANGED
moonbridge/adapters/kimi.py
CHANGED
moonbridge/server.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""MCP server for spawning
|
|
1
|
+
"""MCP server for spawning AI coding agents."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -19,6 +19,8 @@ from mcp.server.stdio import stdio_server
|
|
|
19
19
|
from mcp.types import TextContent, Tool
|
|
20
20
|
|
|
21
21
|
from moonbridge.adapters import ADAPTER_REGISTRY, CLIAdapter, get_adapter
|
|
22
|
+
from moonbridge.adapters.base import AgentResult
|
|
23
|
+
from moonbridge.tools import build_tools
|
|
22
24
|
|
|
23
25
|
server = Server("moonbridge")
|
|
24
26
|
|
|
@@ -71,8 +73,20 @@ def _safe_env(adapter: CLIAdapter) -> dict[str, str]:
|
|
|
71
73
|
return env
|
|
72
74
|
|
|
73
75
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
+
def _resolve_timeout(adapter: CLIAdapter, timeout_seconds: int | None) -> int:
|
|
77
|
+
"""Resolve timeout: explicit > adapter-env > adapter-default > global."""
|
|
78
|
+
if timeout_seconds is not None:
|
|
79
|
+
value = int(timeout_seconds)
|
|
80
|
+
else:
|
|
81
|
+
# Check adapter-specific env var first
|
|
82
|
+
env_key = f"MOONBRIDGE_{adapter.config.name.upper()}_TIMEOUT"
|
|
83
|
+
if env_val := os.environ.get(env_key):
|
|
84
|
+
value = int(env_val)
|
|
85
|
+
elif adapter.config.default_timeout != 600:
|
|
86
|
+
# Use adapter default if explicitly set (not the base default)
|
|
87
|
+
value = adapter.config.default_timeout
|
|
88
|
+
else:
|
|
89
|
+
value = DEFAULT_TIMEOUT
|
|
76
90
|
if value < 30 or value > 3600:
|
|
77
91
|
raise ValueError("timeout_seconds must be between 30 and 3600")
|
|
78
92
|
return value
|
|
@@ -180,29 +194,6 @@ def _auth_error(stderr: str | None, adapter: CLIAdapter) -> bool:
|
|
|
180
194
|
return any(pattern in lowered for pattern in adapter.config.auth_patterns)
|
|
181
195
|
|
|
182
196
|
|
|
183
|
-
def _result(
|
|
184
|
-
*,
|
|
185
|
-
status: str,
|
|
186
|
-
output: str,
|
|
187
|
-
stderr: str | None,
|
|
188
|
-
returncode: int,
|
|
189
|
-
duration_ms: int,
|
|
190
|
-
agent_index: int,
|
|
191
|
-
message: str | None = None,
|
|
192
|
-
) -> dict[str, Any]:
|
|
193
|
-
payload: dict[str, Any] = {
|
|
194
|
-
"status": status,
|
|
195
|
-
"output": output,
|
|
196
|
-
"stderr": stderr,
|
|
197
|
-
"returncode": returncode,
|
|
198
|
-
"duration_ms": duration_ms,
|
|
199
|
-
"agent_index": agent_index,
|
|
200
|
-
}
|
|
201
|
-
if message is not None:
|
|
202
|
-
payload["message"] = message
|
|
203
|
-
return payload
|
|
204
|
-
|
|
205
|
-
|
|
206
197
|
def _run_cli_sync(
|
|
207
198
|
adapter: CLIAdapter,
|
|
208
199
|
prompt: str,
|
|
@@ -212,7 +203,7 @@ def _run_cli_sync(
|
|
|
212
203
|
agent_index: int,
|
|
213
204
|
model: str | None = None,
|
|
214
205
|
reasoning_effort: str | None = None,
|
|
215
|
-
) ->
|
|
206
|
+
) -> AgentResult:
|
|
216
207
|
start = time.monotonic()
|
|
217
208
|
cmd = adapter.build_command(prompt, thinking, model, reasoning_effort)
|
|
218
209
|
logger.debug("Spawning agent with prompt: %s...", prompt[:100])
|
|
@@ -229,7 +220,7 @@ def _run_cli_sync(
|
|
|
229
220
|
except FileNotFoundError:
|
|
230
221
|
duration_ms = int((time.monotonic() - start) * 1000)
|
|
231
222
|
logger.error("%s CLI not found or not executable", adapter.config.name)
|
|
232
|
-
return
|
|
223
|
+
return AgentResult(
|
|
233
224
|
status="error",
|
|
234
225
|
output="",
|
|
235
226
|
stderr=f"{adapter.config.name} CLI not found or not executable",
|
|
@@ -240,7 +231,7 @@ def _run_cli_sync(
|
|
|
240
231
|
except PermissionError as exc:
|
|
241
232
|
duration_ms = int((time.monotonic() - start) * 1000)
|
|
242
233
|
logger.error("Permission denied starting process: %s", exc)
|
|
243
|
-
return
|
|
234
|
+
return AgentResult(
|
|
244
235
|
status="error",
|
|
245
236
|
output="",
|
|
246
237
|
stderr=f"Permission denied: {exc}",
|
|
@@ -251,7 +242,7 @@ def _run_cli_sync(
|
|
|
251
242
|
except OSError as exc:
|
|
252
243
|
duration_ms = int((time.monotonic() - start) * 1000)
|
|
253
244
|
logger.error("Failed to start process: %s", exc)
|
|
254
|
-
return
|
|
245
|
+
return AgentResult(
|
|
255
246
|
status="error",
|
|
256
247
|
output="",
|
|
257
248
|
stderr=f"Failed to start process: {exc}",
|
|
@@ -266,7 +257,7 @@ def _run_cli_sync(
|
|
|
266
257
|
stderr_value = stderr or None
|
|
267
258
|
if _auth_error(stderr_value, adapter):
|
|
268
259
|
logger.info("Agent %s completed with status: auth_error", agent_index)
|
|
269
|
-
return
|
|
260
|
+
return AgentResult(
|
|
270
261
|
status="auth_error",
|
|
271
262
|
output=stdout,
|
|
272
263
|
stderr=stderr_value,
|
|
@@ -277,7 +268,7 @@ def _run_cli_sync(
|
|
|
277
268
|
)
|
|
278
269
|
status = "success" if proc.returncode == 0 else "error"
|
|
279
270
|
logger.info("Agent %s completed with status: %s", agent_index, status)
|
|
280
|
-
return
|
|
271
|
+
return AgentResult(
|
|
281
272
|
status=status,
|
|
282
273
|
output=stdout,
|
|
283
274
|
stderr=stderr_value,
|
|
@@ -289,7 +280,7 @@ def _run_cli_sync(
|
|
|
289
280
|
_terminate_process(proc)
|
|
290
281
|
duration_ms = int((time.monotonic() - start) * 1000)
|
|
291
282
|
logger.warning("Agent %s timed out after %s seconds", agent_index, timeout_seconds)
|
|
292
|
-
return
|
|
283
|
+
return AgentResult(
|
|
293
284
|
status="timeout",
|
|
294
285
|
output="",
|
|
295
286
|
stderr=None,
|
|
@@ -301,7 +292,7 @@ def _run_cli_sync(
|
|
|
301
292
|
_terminate_process(proc)
|
|
302
293
|
duration_ms = int((time.monotonic() - start) * 1000)
|
|
303
294
|
logger.error("Agent %s failed with error: %s", agent_index, exc)
|
|
304
|
-
return
|
|
295
|
+
return AgentResult(
|
|
305
296
|
status="error",
|
|
306
297
|
output="",
|
|
307
298
|
stderr=str(exc),
|
|
@@ -328,14 +319,18 @@ def _status_check(cwd: str, adapter: CLIAdapter) -> dict[str, Any]:
|
|
|
328
319
|
}
|
|
329
320
|
timeout = min(DEFAULT_TIMEOUT, 60)
|
|
330
321
|
result = _run_cli_sync(adapter, "status check", False, cwd, timeout, 0)
|
|
331
|
-
if result
|
|
322
|
+
if result.status == "auth_error":
|
|
332
323
|
return {"status": "auth_error", "message": adapter.config.auth_message}
|
|
333
|
-
if result
|
|
324
|
+
if result.status == "success":
|
|
334
325
|
return {
|
|
335
326
|
"status": "success",
|
|
336
327
|
"message": f"{adapter.config.name} CLI available and authenticated",
|
|
337
328
|
}
|
|
338
|
-
return {
|
|
329
|
+
return {
|
|
330
|
+
"status": "error",
|
|
331
|
+
"message": f"{adapter.config.name} CLI error",
|
|
332
|
+
"details": result.to_dict(),
|
|
333
|
+
}
|
|
339
334
|
|
|
340
335
|
|
|
341
336
|
def _adapter_info(cwd: str, adapter: CLIAdapter) -> dict[str, Any]:
|
|
@@ -344,7 +339,7 @@ def _adapter_info(cwd: str, adapter: CLIAdapter) -> dict[str, Any]:
|
|
|
344
339
|
if installed:
|
|
345
340
|
timeout = min(DEFAULT_TIMEOUT, 60)
|
|
346
341
|
result = _run_cli_sync(adapter, "status check", False, cwd, timeout, 0)
|
|
347
|
-
authenticated = result
|
|
342
|
+
authenticated = result.status == "success"
|
|
348
343
|
return {
|
|
349
344
|
"name": adapter.config.name,
|
|
350
345
|
"description": adapter.config.tool_description,
|
|
@@ -359,112 +354,13 @@ def _adapter_info(cwd: str, adapter: CLIAdapter) -> dict[str, Any]:
|
|
|
359
354
|
async def list_tools() -> list[Tool]:
|
|
360
355
|
adapter = get_adapter()
|
|
361
356
|
tool_desc = adapter.config.tool_description
|
|
362
|
-
parallel_desc = f"{tool_desc} Run multiple agents in parallel."
|
|
363
357
|
status_desc = f"Verify {adapter.config.name} CLI is installed and authenticated"
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
Tool(
|
|
371
|
-
name="spawn_agent",
|
|
372
|
-
description=tool_desc,
|
|
373
|
-
inputSchema={
|
|
374
|
-
"type": "object",
|
|
375
|
-
"properties": {
|
|
376
|
-
"prompt": {
|
|
377
|
-
"type": "string",
|
|
378
|
-
"description": "Instructions for the agent (task, context, constraints)",
|
|
379
|
-
},
|
|
380
|
-
"adapter": adapter_schema,
|
|
381
|
-
"thinking": {
|
|
382
|
-
"type": "boolean",
|
|
383
|
-
"description": "Enable extended reasoning mode for complex tasks",
|
|
384
|
-
"default": False,
|
|
385
|
-
},
|
|
386
|
-
"timeout_seconds": {
|
|
387
|
-
"type": "integer",
|
|
388
|
-
"description": "Max execution time (30-3600s)",
|
|
389
|
-
"default": DEFAULT_TIMEOUT,
|
|
390
|
-
"minimum": 30,
|
|
391
|
-
"maximum": 3600,
|
|
392
|
-
},
|
|
393
|
-
"model": {
|
|
394
|
-
"type": "string",
|
|
395
|
-
"description": (
|
|
396
|
-
"Model to use (e.g., 'gpt-5.2-codex', 'kimi-k2.5'). "
|
|
397
|
-
"Falls back to MOONBRIDGE_{ADAPTER}_MODEL or MOONBRIDGE_MODEL env vars."
|
|
398
|
-
),
|
|
399
|
-
},
|
|
400
|
-
"reasoning_effort": {
|
|
401
|
-
"type": "string",
|
|
402
|
-
"enum": ["low", "medium", "high", "xhigh"],
|
|
403
|
-
"description": (
|
|
404
|
-
"Reasoning effort for Codex (low, medium, high, xhigh). "
|
|
405
|
-
"Ignored for Kimi (use thinking instead)."
|
|
406
|
-
),
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
"required": ["prompt"],
|
|
410
|
-
},
|
|
411
|
-
),
|
|
412
|
-
Tool(
|
|
413
|
-
name="spawn_agents_parallel",
|
|
414
|
-
description=parallel_desc,
|
|
415
|
-
inputSchema={
|
|
416
|
-
"type": "object",
|
|
417
|
-
"properties": {
|
|
418
|
-
"agents": {
|
|
419
|
-
"type": "array",
|
|
420
|
-
"description": "List of agent specs with prompt and optional settings",
|
|
421
|
-
"items": {
|
|
422
|
-
"type": "object",
|
|
423
|
-
"properties": {
|
|
424
|
-
"prompt": {"type": "string"},
|
|
425
|
-
"adapter": adapter_schema,
|
|
426
|
-
"thinking": {"type": "boolean", "default": False},
|
|
427
|
-
"timeout_seconds": {
|
|
428
|
-
"type": "integer",
|
|
429
|
-
"description": "Max execution time (30-3600s)",
|
|
430
|
-
"default": DEFAULT_TIMEOUT,
|
|
431
|
-
"minimum": 30,
|
|
432
|
-
"maximum": 3600,
|
|
433
|
-
},
|
|
434
|
-
"model": {
|
|
435
|
-
"type": "string",
|
|
436
|
-
"description": (
|
|
437
|
-
"Model to use. Falls back to "
|
|
438
|
-
"MOONBRIDGE_{ADAPTER}_MODEL or MOONBRIDGE_MODEL env vars."
|
|
439
|
-
),
|
|
440
|
-
},
|
|
441
|
-
"reasoning_effort": {
|
|
442
|
-
"type": "string",
|
|
443
|
-
"enum": ["low", "medium", "high", "xhigh"],
|
|
444
|
-
"description": (
|
|
445
|
-
"Reasoning effort for Codex (low, medium, high, xhigh). "
|
|
446
|
-
"Ignored for Kimi."
|
|
447
|
-
),
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
"required": ["prompt"],
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
"required": ["agents"],
|
|
455
|
-
},
|
|
456
|
-
),
|
|
457
|
-
Tool(
|
|
458
|
-
name="list_adapters",
|
|
459
|
-
description="List available adapters and their status",
|
|
460
|
-
inputSchema={"type": "object", "properties": {}},
|
|
461
|
-
),
|
|
462
|
-
Tool(
|
|
463
|
-
name="check_status",
|
|
464
|
-
description=status_desc,
|
|
465
|
-
inputSchema={"type": "object", "properties": {}},
|
|
466
|
-
),
|
|
467
|
-
]
|
|
358
|
+
return build_tools(
|
|
359
|
+
adapter_names=tuple(ADAPTER_REGISTRY.keys()),
|
|
360
|
+
default_timeout=DEFAULT_TIMEOUT,
|
|
361
|
+
tool_description=tool_desc,
|
|
362
|
+
status_description=status_desc,
|
|
363
|
+
)
|
|
468
364
|
|
|
469
365
|
|
|
470
366
|
async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
|
|
@@ -475,7 +371,7 @@ async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]
|
|
|
475
371
|
adapter = get_adapter(arguments.get("adapter"))
|
|
476
372
|
prompt = _validate_prompt(arguments["prompt"])
|
|
477
373
|
thinking = _validate_thinking(adapter, bool(arguments.get("thinking", False)))
|
|
478
|
-
timeout_seconds =
|
|
374
|
+
timeout_seconds = _resolve_timeout(adapter, arguments.get("timeout_seconds"))
|
|
479
375
|
model = _resolve_model(adapter, arguments.get("model"))
|
|
480
376
|
reasoning_effort = arguments.get("reasoning_effort")
|
|
481
377
|
loop = asyncio.get_running_loop()
|
|
@@ -494,16 +390,16 @@ async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]
|
|
|
494
390
|
)
|
|
495
391
|
except asyncio.CancelledError:
|
|
496
392
|
return _json_text(
|
|
497
|
-
|
|
393
|
+
AgentResult(
|
|
498
394
|
status="cancelled",
|
|
499
395
|
output="",
|
|
500
396
|
stderr=None,
|
|
501
397
|
returncode=-1,
|
|
502
398
|
duration_ms=0,
|
|
503
399
|
agent_index=0,
|
|
504
|
-
)
|
|
400
|
+
).to_dict()
|
|
505
401
|
)
|
|
506
|
-
return _json_text(result)
|
|
402
|
+
return _json_text(result.to_dict())
|
|
507
403
|
|
|
508
404
|
if name == "spawn_agents_parallel":
|
|
509
405
|
agents = list(arguments["agents"])
|
|
@@ -525,7 +421,7 @@ async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]
|
|
|
525
421
|
prompt,
|
|
526
422
|
thinking,
|
|
527
423
|
cwd,
|
|
528
|
-
|
|
424
|
+
_resolve_timeout(adapter, spec.get("timeout_seconds")),
|
|
529
425
|
idx,
|
|
530
426
|
model,
|
|
531
427
|
reasoning_effort,
|
|
@@ -535,7 +431,7 @@ async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]
|
|
|
535
431
|
results = await asyncio.gather(*tasks)
|
|
536
432
|
except asyncio.CancelledError:
|
|
537
433
|
cancelled = [
|
|
538
|
-
|
|
434
|
+
AgentResult(
|
|
539
435
|
status="cancelled",
|
|
540
436
|
output="",
|
|
541
437
|
stderr=None,
|
|
@@ -545,9 +441,9 @@ async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]
|
|
|
545
441
|
)
|
|
546
442
|
for idx in range(len(agents))
|
|
547
443
|
]
|
|
548
|
-
return _json_text(cancelled)
|
|
549
|
-
results.sort(key=lambda item: item
|
|
550
|
-
return _json_text(results)
|
|
444
|
+
return _json_text([item.to_dict() for item in cancelled])
|
|
445
|
+
results.sort(key=lambda item: item.agent_index)
|
|
446
|
+
return _json_text([item.to_dict() for item in results])
|
|
551
447
|
|
|
552
448
|
if name == "list_adapters":
|
|
553
449
|
info = [_adapter_info(cwd, adapter) for adapter in ADAPTER_REGISTRY.values()]
|
moonbridge/tools.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""Tool schema definitions for Moonbridge MCP server.
|
|
2
|
+
|
|
3
|
+
This module provides dataclasses and functions for defining MCP tool schemas
|
|
4
|
+
in a reusable, type-safe manner.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from mcp.types import Tool
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class ParameterDef:
|
|
17
|
+
"""Definition for a JSON Schema parameter."""
|
|
18
|
+
|
|
19
|
+
type: str # "string", "integer", "boolean", "array"
|
|
20
|
+
description: str
|
|
21
|
+
default: Any = None
|
|
22
|
+
enum: tuple[str, ...] | None = None
|
|
23
|
+
minimum: int | None = None
|
|
24
|
+
maximum: int | None = None
|
|
25
|
+
items: dict[str, Any] | None = None # For array types
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class ToolDef:
|
|
30
|
+
"""Definition for an MCP tool."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
description_template: str # May contain {adapter} placeholder
|
|
34
|
+
parameters: tuple[tuple[str, ParameterDef], ...] # Ordered (name, param) pairs
|
|
35
|
+
required: tuple[str, ...] = ()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# =============================================================================
|
|
39
|
+
# Reusable parameter definitions
|
|
40
|
+
# =============================================================================
|
|
41
|
+
|
|
42
|
+
PROMPT_PARAM = ParameterDef(
|
|
43
|
+
type="string",
|
|
44
|
+
description="Instructions for the agent (task, context, constraints)",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Note: ADAPTER_PARAM enum is populated dynamically via build_adapter_param()
|
|
48
|
+
ADAPTER_PARAM_BASE = ParameterDef(
|
|
49
|
+
type="string",
|
|
50
|
+
description="Backend to use (kimi, codex). Defaults to MOONBRIDGE_ADAPTER env or kimi.",
|
|
51
|
+
# enum is set dynamically
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
THINKING_PARAM = ParameterDef(
|
|
55
|
+
type="boolean",
|
|
56
|
+
description="Enable extended reasoning mode for complex tasks",
|
|
57
|
+
default=False,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Note: TIMEOUT_PARAM default is populated dynamically
|
|
61
|
+
TIMEOUT_PARAM_BASE = ParameterDef(
|
|
62
|
+
type="integer",
|
|
63
|
+
description=(
|
|
64
|
+
"Max execution time (30-3600s). "
|
|
65
|
+
"Defaults: Codex=1800s (30min), Kimi=600s (10min). "
|
|
66
|
+
"Complex implementations may need full 30min+."
|
|
67
|
+
),
|
|
68
|
+
minimum=30,
|
|
69
|
+
maximum=3600,
|
|
70
|
+
# default is set dynamically
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
MODEL_PARAM = ParameterDef(
|
|
74
|
+
type="string",
|
|
75
|
+
description=(
|
|
76
|
+
"Model to use (e.g., 'gpt-5.2-codex', 'kimi-k2.5'). "
|
|
77
|
+
"Falls back to MOONBRIDGE_{ADAPTER}_MODEL or MOONBRIDGE_MODEL env vars."
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Shorter model description for nested items
|
|
82
|
+
MODEL_PARAM_SHORT = ParameterDef(
|
|
83
|
+
type="string",
|
|
84
|
+
description=(
|
|
85
|
+
"Model to use. Falls back to "
|
|
86
|
+
"MOONBRIDGE_{ADAPTER}_MODEL or MOONBRIDGE_MODEL env vars."
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
REASONING_EFFORT_PARAM = ParameterDef(
|
|
91
|
+
type="string",
|
|
92
|
+
description=(
|
|
93
|
+
"Reasoning effort for Codex (low, medium, high, xhigh). "
|
|
94
|
+
"Ignored for Kimi (use thinking instead)."
|
|
95
|
+
),
|
|
96
|
+
enum=("low", "medium", "high", "xhigh"),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Shorter reasoning_effort description for nested items
|
|
100
|
+
REASONING_EFFORT_PARAM_SHORT = ParameterDef(
|
|
101
|
+
type="string",
|
|
102
|
+
description=(
|
|
103
|
+
"Reasoning effort for Codex (low, medium, high, xhigh). "
|
|
104
|
+
"Ignored for Kimi."
|
|
105
|
+
),
|
|
106
|
+
enum=("low", "medium", "high", "xhigh"),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# =============================================================================
|
|
111
|
+
# Helper functions for dynamic parameter creation
|
|
112
|
+
# =============================================================================
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _build_adapter_param(adapter_names: tuple[str, ...]) -> ParameterDef:
|
|
116
|
+
"""Create adapter parameter with dynamic enum."""
|
|
117
|
+
return ParameterDef(
|
|
118
|
+
type="string",
|
|
119
|
+
description=ADAPTER_PARAM_BASE.description,
|
|
120
|
+
enum=adapter_names,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _build_timeout_param(default_timeout: int) -> ParameterDef:
|
|
125
|
+
"""Create timeout parameter with dynamic default.
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
ValueError: If default_timeout is outside the valid range.
|
|
129
|
+
"""
|
|
130
|
+
min_timeout = TIMEOUT_PARAM_BASE.minimum
|
|
131
|
+
max_timeout = TIMEOUT_PARAM_BASE.maximum
|
|
132
|
+
if min_timeout is not None and default_timeout < min_timeout:
|
|
133
|
+
raise ValueError(f"default_timeout must be >= {min_timeout}, got {default_timeout}")
|
|
134
|
+
if max_timeout is not None and default_timeout > max_timeout:
|
|
135
|
+
raise ValueError(f"default_timeout must be <= {max_timeout}, got {default_timeout}")
|
|
136
|
+
return ParameterDef(
|
|
137
|
+
type="integer",
|
|
138
|
+
description=TIMEOUT_PARAM_BASE.description,
|
|
139
|
+
default=default_timeout,
|
|
140
|
+
minimum=min_timeout,
|
|
141
|
+
maximum=max_timeout,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# =============================================================================
|
|
146
|
+
# Tool definitions
|
|
147
|
+
# =============================================================================
|
|
148
|
+
|
|
149
|
+
SPAWN_AGENT_TOOL = ToolDef(
|
|
150
|
+
name="spawn_agent",
|
|
151
|
+
description_template="{tool_description}",
|
|
152
|
+
parameters=(
|
|
153
|
+
("prompt", PROMPT_PARAM),
|
|
154
|
+
("adapter", ADAPTER_PARAM_BASE), # Will be replaced with dynamic version
|
|
155
|
+
("thinking", THINKING_PARAM),
|
|
156
|
+
("timeout_seconds", TIMEOUT_PARAM_BASE), # Will be replaced with dynamic version
|
|
157
|
+
("model", MODEL_PARAM),
|
|
158
|
+
("reasoning_effort", REASONING_EFFORT_PARAM),
|
|
159
|
+
),
|
|
160
|
+
required=("prompt",),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
SPAWN_AGENTS_PARALLEL_TOOL = ToolDef(
|
|
164
|
+
name="spawn_agents_parallel",
|
|
165
|
+
description_template="{tool_description} Run multiple agents in parallel.",
|
|
166
|
+
parameters=(), # Handled specially due to array items
|
|
167
|
+
required=("agents",),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
LIST_ADAPTERS_TOOL = ToolDef(
|
|
171
|
+
name="list_adapters",
|
|
172
|
+
description_template="List available adapters and their status",
|
|
173
|
+
parameters=(),
|
|
174
|
+
required=(),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
CHECK_STATUS_TOOL = ToolDef(
|
|
178
|
+
name="check_status",
|
|
179
|
+
description_template="{status_description}",
|
|
180
|
+
parameters=(),
|
|
181
|
+
required=(),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# =============================================================================
|
|
186
|
+
# Schema generation functions
|
|
187
|
+
# =============================================================================
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _param_to_schema(param: ParameterDef) -> dict[str, Any]:
|
|
191
|
+
"""Convert a ParameterDef to a JSON Schema dict."""
|
|
192
|
+
schema: dict[str, Any] = {"type": param.type}
|
|
193
|
+
|
|
194
|
+
if param.description:
|
|
195
|
+
schema["description"] = param.description
|
|
196
|
+
if param.default is not None:
|
|
197
|
+
schema["default"] = param.default
|
|
198
|
+
if param.enum is not None:
|
|
199
|
+
schema["enum"] = list(param.enum)
|
|
200
|
+
if param.minimum is not None:
|
|
201
|
+
schema["minimum"] = param.minimum
|
|
202
|
+
if param.maximum is not None:
|
|
203
|
+
schema["maximum"] = param.maximum
|
|
204
|
+
if param.items is not None:
|
|
205
|
+
schema["items"] = param.items
|
|
206
|
+
|
|
207
|
+
return schema
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def build_input_schema(
|
|
211
|
+
tool: ToolDef,
|
|
212
|
+
adapter_names: tuple[str, ...],
|
|
213
|
+
default_timeout: int,
|
|
214
|
+
) -> dict[str, Any]:
|
|
215
|
+
"""Convert ToolDef to MCP inputSchema dict.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
tool: The tool definition to convert.
|
|
219
|
+
adapter_names: Tuple of available adapter names for enum.
|
|
220
|
+
default_timeout: Default timeout value for timeout parameters.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
A JSON Schema dict suitable for MCP Tool.inputSchema.
|
|
224
|
+
"""
|
|
225
|
+
properties: dict[str, Any] = {}
|
|
226
|
+
|
|
227
|
+
for name, param in tool.parameters:
|
|
228
|
+
# Handle dynamic parameters
|
|
229
|
+
if name == "adapter":
|
|
230
|
+
param = _build_adapter_param(adapter_names)
|
|
231
|
+
elif name == "timeout_seconds":
|
|
232
|
+
param = _build_timeout_param(default_timeout)
|
|
233
|
+
|
|
234
|
+
properties[name] = _param_to_schema(param)
|
|
235
|
+
|
|
236
|
+
schema: dict[str, Any] = {
|
|
237
|
+
"type": "object",
|
|
238
|
+
"properties": properties,
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if tool.required:
|
|
242
|
+
schema["required"] = list(tool.required)
|
|
243
|
+
|
|
244
|
+
return schema
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _build_agents_array_schema(
|
|
248
|
+
adapter_names: tuple[str, ...],
|
|
249
|
+
default_timeout: int,
|
|
250
|
+
) -> dict[str, Any]:
|
|
251
|
+
"""Build the schema for the agents array in spawn_agents_parallel."""
|
|
252
|
+
adapter_schema = _param_to_schema(_build_adapter_param(adapter_names))
|
|
253
|
+
timeout_schema = _param_to_schema(_build_timeout_param(default_timeout))
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
"type": "array",
|
|
257
|
+
"description": "List of agent specs with prompt and optional settings",
|
|
258
|
+
"items": {
|
|
259
|
+
"type": "object",
|
|
260
|
+
"properties": {
|
|
261
|
+
"prompt": {"type": "string"},
|
|
262
|
+
"adapter": adapter_schema,
|
|
263
|
+
"thinking": {"type": "boolean", "default": False},
|
|
264
|
+
"timeout_seconds": timeout_schema,
|
|
265
|
+
"model": _param_to_schema(MODEL_PARAM_SHORT),
|
|
266
|
+
"reasoning_effort": _param_to_schema(REASONING_EFFORT_PARAM_SHORT),
|
|
267
|
+
},
|
|
268
|
+
"required": ["prompt"],
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def build_tools(
|
|
274
|
+
adapter_names: tuple[str, ...],
|
|
275
|
+
default_timeout: int,
|
|
276
|
+
tool_description: str,
|
|
277
|
+
status_description: str,
|
|
278
|
+
) -> list[Tool]:
|
|
279
|
+
"""Build all MCP Tool objects from definitions.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
adapter_names: Tuple of available adapter names.
|
|
283
|
+
default_timeout: Default timeout value in seconds.
|
|
284
|
+
tool_description: Description for the spawn_agent tool.
|
|
285
|
+
status_description: Description for the check_status tool.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
List of MCP Tool objects ready for registration.
|
|
289
|
+
"""
|
|
290
|
+
# spawn_agent
|
|
291
|
+
spawn_agent_schema = build_input_schema(
|
|
292
|
+
SPAWN_AGENT_TOOL, adapter_names, default_timeout
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# spawn_agents_parallel (special handling for array)
|
|
296
|
+
parallel_schema: dict[str, Any] = {
|
|
297
|
+
"type": "object",
|
|
298
|
+
"properties": {
|
|
299
|
+
"agents": _build_agents_array_schema(adapter_names, default_timeout),
|
|
300
|
+
},
|
|
301
|
+
"required": ["agents"],
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# list_adapters
|
|
305
|
+
list_adapters_schema: dict[str, Any] = {"type": "object", "properties": {}}
|
|
306
|
+
|
|
307
|
+
# check_status
|
|
308
|
+
check_status_schema: dict[str, Any] = {"type": "object", "properties": {}}
|
|
309
|
+
|
|
310
|
+
return [
|
|
311
|
+
Tool(
|
|
312
|
+
name="spawn_agent",
|
|
313
|
+
description=tool_description,
|
|
314
|
+
inputSchema=spawn_agent_schema,
|
|
315
|
+
),
|
|
316
|
+
Tool(
|
|
317
|
+
name="spawn_agents_parallel",
|
|
318
|
+
description=f"{tool_description} Run multiple agents in parallel.",
|
|
319
|
+
inputSchema=parallel_schema,
|
|
320
|
+
),
|
|
321
|
+
Tool(
|
|
322
|
+
name="list_adapters",
|
|
323
|
+
description="List available adapters and their status",
|
|
324
|
+
inputSchema=list_adapters_schema,
|
|
325
|
+
),
|
|
326
|
+
Tool(
|
|
327
|
+
name="check_status",
|
|
328
|
+
description=status_description,
|
|
329
|
+
inputSchema=check_status_schema,
|
|
330
|
+
),
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
__all__ = ["build_tools"]
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moonbridge
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: MCP server for spawning Kimi
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: MCP server for spawning AI coding agents (Kimi, Codex, and more)
|
|
5
5
|
Project-URL: Homepage, https://github.com/misty-step/moonbridge
|
|
6
6
|
Project-URL: Repository, https://github.com/misty-step/moonbridge
|
|
7
7
|
Project-URL: Issues, https://github.com/misty-step/moonbridge/issues
|
|
8
8
|
Author-email: Phaedrus <hello@mistystep.io>
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Keywords: agent,ai,claude,kimi,mcp
|
|
11
|
+
Keywords: agent,ai,claude,codex,kimi,mcp
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Environment :: Console
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
**Your MCP client just got a team.**
|
|
33
33
|
|
|
34
|
-
Spawn
|
|
34
|
+
Spawn AI coding agents from Claude Code, Cursor, or any MCP client. Run 10 approaches in parallel for a fraction of the cost.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
uvx moonbridge
|
|
@@ -39,10 +39,12 @@ uvx moonbridge
|
|
|
39
39
|
|
|
40
40
|
## Quick Start
|
|
41
41
|
|
|
42
|
-
1. **Install
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
1. **Install at least one supported CLI:**
|
|
43
|
+
|
|
44
|
+
| Adapter | Install | Authenticate |
|
|
45
|
+
|---------|---------|--------------|
|
|
46
|
+
| Kimi (default) | `uv tool install --python 3.13 kimi-cli` | `kimi login` |
|
|
47
|
+
| Codex | `npm install -g @openai/codex` | Set `OPENAI_API_KEY` |
|
|
46
48
|
|
|
47
49
|
2. **Add to MCP config** (`~/.mcp.json`):
|
|
48
50
|
```json
|
|
@@ -94,7 +96,8 @@ export MOONBRIDGE_SKIP_UPDATE_CHECK=1
|
|
|
94
96
|
|------|----------|
|
|
95
97
|
| `spawn_agent` | Single task: "Write tests for auth.ts" |
|
|
96
98
|
| `spawn_agents_parallel` | Go wide: 10 agents, 10 approaches, pick the best |
|
|
97
|
-
| `check_status` | Verify
|
|
99
|
+
| `check_status` | Verify the configured CLI is installed and authenticated |
|
|
100
|
+
| `list_adapters` | Show available adapters and their status |
|
|
98
101
|
|
|
99
102
|
### Example: Parallel Exploration
|
|
100
103
|
|
|
@@ -117,7 +120,10 @@ Three approaches. One request. You choose the winner.
|
|
|
117
120
|
| Parameter | Type | Required | Description |
|
|
118
121
|
|-----------|------|----------|-------------|
|
|
119
122
|
| `prompt` | string | Yes | Task description for the agent |
|
|
120
|
-
| `
|
|
123
|
+
| `adapter` | string | No | Backend to use: `kimi`, `codex` (default: `kimi`) |
|
|
124
|
+
| `model` | string | No | Model override (e.g., `gpt-5.2-codex`) |
|
|
125
|
+
| `thinking` | boolean | No | Enable reasoning mode (Kimi only) |
|
|
126
|
+
| `reasoning_effort` | string | No | Reasoning budget: `low`, `medium`, `high`, `xhigh` (Codex only) |
|
|
121
127
|
| `timeout_seconds` | integer | No | Override default timeout (30-3600) |
|
|
122
128
|
|
|
123
129
|
**`spawn_agents_parallel`**
|
|
@@ -126,7 +132,10 @@ Three approaches. One request. You choose the winner.
|
|
|
126
132
|
|-----------|------|----------|-------------|
|
|
127
133
|
| `agents` | array | Yes | List of agent configs (max 10) |
|
|
128
134
|
| `agents[].prompt` | string | Yes | Task for this agent |
|
|
129
|
-
| `agents[].
|
|
135
|
+
| `agents[].adapter` | string | No | Backend for this agent |
|
|
136
|
+
| `agents[].model` | string | No | Model override for this agent |
|
|
137
|
+
| `agents[].thinking` | boolean | No | Enable reasoning (Kimi only) |
|
|
138
|
+
| `agents[].reasoning_effort` | string | No | Reasoning budget (Codex only) |
|
|
130
139
|
| `agents[].timeout_seconds` | integer | No | Timeout for this agent |
|
|
131
140
|
|
|
132
141
|
## Response Format
|
|
@@ -136,7 +145,7 @@ All tools return JSON with these fields:
|
|
|
136
145
|
| Field | Type | Description |
|
|
137
146
|
|-------|------|-------------|
|
|
138
147
|
| `status` | string | `success`, `error`, `timeout`, `auth_error`, or `cancelled` |
|
|
139
|
-
| `output` | string | stdout from
|
|
148
|
+
| `output` | string | stdout from the agent |
|
|
140
149
|
| `stderr` | string\|null | stderr if any |
|
|
141
150
|
| `returncode` | int | Process exit code (-1 for timeout/error) |
|
|
142
151
|
| `duration_ms` | int | Execution time in milliseconds |
|
|
@@ -158,37 +167,60 @@ All tools return JSON with these fields:
|
|
|
158
167
|
|
|
159
168
|
## Troubleshooting
|
|
160
169
|
|
|
161
|
-
### "
|
|
170
|
+
### "CLI not found"
|
|
162
171
|
|
|
163
|
-
Install the
|
|
172
|
+
Install the CLI for your chosen adapter:
|
|
164
173
|
|
|
165
174
|
```bash
|
|
175
|
+
# Kimi
|
|
166
176
|
uv tool install --python 3.13 kimi-cli
|
|
167
177
|
which kimi
|
|
178
|
+
|
|
179
|
+
# Codex
|
|
180
|
+
npm install -g @openai/codex
|
|
181
|
+
which codex
|
|
168
182
|
```
|
|
169
183
|
|
|
170
184
|
### "auth_error" responses
|
|
171
185
|
|
|
172
|
-
Authenticate with
|
|
186
|
+
Authenticate with your chosen CLI:
|
|
173
187
|
|
|
174
188
|
```bash
|
|
189
|
+
# Kimi
|
|
175
190
|
kimi login
|
|
191
|
+
|
|
192
|
+
# Codex
|
|
193
|
+
export OPENAI_API_KEY=sk-...
|
|
176
194
|
```
|
|
177
195
|
|
|
178
196
|
### Timeout errors
|
|
179
197
|
|
|
180
|
-
|
|
198
|
+
Adapters have sensible defaults: Codex=1800s (30min), Kimi=600s (10min).
|
|
199
|
+
|
|
200
|
+
For exceptionally long tasks, override explicitly:
|
|
181
201
|
|
|
182
202
|
```json
|
|
183
|
-
{"prompt": "...", "timeout_seconds":
|
|
203
|
+
{"prompt": "...", "timeout_seconds": 3600}
|
|
184
204
|
```
|
|
185
205
|
|
|
186
|
-
Or set
|
|
206
|
+
Or set per-adapter defaults via environment:
|
|
187
207
|
|
|
188
208
|
```bash
|
|
189
|
-
export
|
|
209
|
+
export MOONBRIDGE_CODEX_TIMEOUT=2400 # 40 minutes
|
|
210
|
+
export MOONBRIDGE_KIMI_TIMEOUT=900 # 15 minutes
|
|
190
211
|
```
|
|
191
212
|
|
|
213
|
+
## Timeout Best Practices
|
|
214
|
+
|
|
215
|
+
| Task Type | Recommended |
|
|
216
|
+
|-----------|-------------|
|
|
217
|
+
| Quick query, status | 60-180s |
|
|
218
|
+
| Simple edits | 300-600s |
|
|
219
|
+
| Feature implementation | 1200-1800s |
|
|
220
|
+
| Large refactor | 1800-3600s |
|
|
221
|
+
|
|
222
|
+
Priority resolution: explicit param > adapter env > adapter default > global env > 600s fallback
|
|
223
|
+
|
|
192
224
|
### "MOONBRIDGE_ALLOWED_DIRS is not set" warning
|
|
193
225
|
|
|
194
226
|
By default, Moonbridge warns at startup if no directory restrictions are configured. This is expected for local development. For shared/production environments, set allowed directories:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
moonbridge/__init__.py,sha256=K5-NRJiYdFbIITQmjIg3_Fkef0UEWhe2hqO3rJ0MZII,198
|
|
2
|
+
moonbridge/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
|
+
moonbridge/server.py,sha256=o-u0J-HsVoa7sxaweWPHo_iHDXJfao16eDFe_9bm6RA,17071
|
|
4
|
+
moonbridge/tools.py,sha256=uw338Dilrto2t5dL9XbK4O31-JdB7Vh9RqCXHg20gHI,10126
|
|
5
|
+
moonbridge/version_check.py,sha256=VQueK0O_b-2Xc-XjupJsoW3Zs1Kce5q_BgqBhANGXN8,4579
|
|
6
|
+
moonbridge/adapters/__init__.py,sha256=w3pLvjtC2XnUhf9UzNmniQB3oq4rG8gorSH0tWR-BEE,988
|
|
7
|
+
moonbridge/adapters/base.py,sha256=REoEsAcqEvyVQpTgz6ytd9ioxag--nnvX90YBXMQG8Y,1716
|
|
8
|
+
moonbridge/adapters/codex.py,sha256=GtU4CrJ4zt0WDcKKaOeN7gH4JFIBAo3L7KAZ99zRjiY,2935
|
|
9
|
+
moonbridge/adapters/kimi.py,sha256=ejCxG2OGr0Qr4n0psL6p96_mMJ3lLKMbGcNYWkuC0uA,2189
|
|
10
|
+
moonbridge-0.6.0.dist-info/METADATA,sha256=nJndtrv2GuSSPeNrV6ryJa6-82viUtankQXduB6Nkn4,7298
|
|
11
|
+
moonbridge-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
moonbridge-0.6.0.dist-info/entry_points.txt,sha256=kgL38HQy3adncDQl_o5sdtPRog56zKdHk6pKKzyR6Ww,54
|
|
13
|
+
moonbridge-0.6.0.dist-info/licenses/LICENSE,sha256=7WMSJoybL2cUot_wb9GUrw5mzfFmtrDzqlMS9ZE709g,1065
|
|
14
|
+
moonbridge-0.6.0.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
moonbridge/__init__.py,sha256=x3eYCVqjhWKoPmGJvOV3IALPoS1DFO-iZRECuVbNgtQ,198
|
|
2
|
-
moonbridge/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
|
-
moonbridge/server.py,sha256=rP3c0hcuUDxC5QqPNWboJZYnMS4hZw6oqkzmg-N0WwM,21194
|
|
4
|
-
moonbridge/version_check.py,sha256=VQueK0O_b-2Xc-XjupJsoW3Zs1Kce5q_BgqBhANGXN8,4579
|
|
5
|
-
moonbridge/adapters/__init__.py,sha256=w3pLvjtC2XnUhf9UzNmniQB3oq4rG8gorSH0tWR-BEE,988
|
|
6
|
-
moonbridge/adapters/base.py,sha256=bj_Ms55h2lwDmEO0CZ1RFSAA9IHgNbX2LI1xgQEftLY,942
|
|
7
|
-
moonbridge/adapters/codex.py,sha256=JTt9B3eXqset6ZrwwlnHzcno5PMdrjY2GdLSNPYkowQ,2873
|
|
8
|
-
moonbridge/adapters/kimi.py,sha256=75QFPTMVpgbgkVGv8GEpIYY1zrIOZ0kJ-aCgd8Tx0TA,2129
|
|
9
|
-
moonbridge-0.5.2.dist-info/METADATA,sha256=aaN1N00Q5oY2bE6faQWW163_goAPVMQDtEEWTlkUnBE,5984
|
|
10
|
-
moonbridge-0.5.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
11
|
-
moonbridge-0.5.2.dist-info/entry_points.txt,sha256=kgL38HQy3adncDQl_o5sdtPRog56zKdHk6pKKzyR6Ww,54
|
|
12
|
-
moonbridge-0.5.2.dist-info/licenses/LICENSE,sha256=7WMSJoybL2cUot_wb9GUrw5mzfFmtrDzqlMS9ZE709g,1065
|
|
13
|
-
moonbridge-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|