syndic-mcp
v0.3.0
Published
MCP server for orchestrating external AI CLI engines with sentinel-file completion detection
Maintainers
Readme
syndic-mcp
An MCP server that lets any MCP-capable AI host (Claude Code, Cursor, Cline, etc.) spawn and orchestrate external AI CLI engines — Codex, Gemini CLI, Claude Code, and OpenCode — as subagents for parallel or delegated task execution.
Tasks run in their own processes. Completion is detected via a sentinel file written by the engine, so the orchestrator never polls or blocks unnecessarily.
How it works
- The orchestrating AI calls
syndic_runwith an engine, a prompt, and an optional working directory. - syndic-mcp writes the full task (prompt + structured completion protocol) to a
.syndic/<id>.promptfile in the working directory. - The chosen CLI engine is spawned via
cmd.exe /cand instructed to read and execute that file. - When the engine finishes, it writes its results to
.syndic/<id>.output.mdand a structured sentinel to.syndic/<id>.md. - syndic-mcp detects the sentinel (via
fs.watch+ process-exit fallback), reads the output, and marks the task complete. - The orchestrating AI polls with
syndic_statusor useswait: truefor synchronous execution.
Prerequisites
Install the CLI engines you intend to use:
| Engine | Install |
|--------|---------|
| Codex CLI | npm install -g @openai/codex |
| Gemini CLI | npm install -g @google/gemini-cli |
| Claude Code | npm install -g @anthropic-ai/claude-code |
| OpenCode | npm install -g @anthropic-ai/opencode |
Node.js >= 18 required.
Installation
npm install -g syndic-mcpOr run directly without installing:
npx syndic-mcpMCP Configuration
Add to your MCP host's config (e.g. claude_desktop_config.json, .claude.json, mcp.json):
{
"mcpServers": {
"syndic": {
"command": "syndic-mcp"
}
}
}Or with npx:
{
"mcpServers": {
"syndic": {
"command": "npx",
"args": ["syndic-mcp"]
}
}
}Tools
syndic_run
Spawn an external AI CLI engine to execute a task.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| engine | "codex" \| "gemini" \| "claude" \| "opencode" | Yes | Which CLI engine to invoke |
| prompt | string | Yes | Self-contained task prompt (≥10 chars, ≤200,000 chars). The engine has no context beyond this string. |
| cwd | string | No | Absolute working directory path. Defaults to the server's cwd. |
| timeout_ms | number | No | Timeout in ms. Range: 10,000–3,600,000. Default: 1,800,000 (30 min). |
| wait | boolean | No | If true, block until the task completes or times out. Default: false (returns task_id immediately). |
| yolo | boolean | No | If true, run engine without guardrails. Default: false (safe mode). See Safe mode vs YOLO mode. |
Async response (wait: false):
{
"task_id": "abc1234567",
"status": "running",
"engine": "codex",
"message": "Task spawned. Use syndic_status to check progress."
}Sync response (wait: true):
{
"task_id": "abc1234567",
"status": "completed",
"engine": "gemini",
"duration_ms": 12400,
"output_content": "## Output\n...",
"result": "---\nstatus: completed\n---\n\n## Summary\n...",
"error": null
}syndic_status
Check the status of a task by its ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| task_id | string | Yes | ID returned by syndic_run |
While running, returns a tail of stdout for progress visibility. When complete, returns the full output and sentinel content.
Task statuses: running | completed | failed | cancelled | timed_out
syndic_cancel
Kill a running task immediately.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| task_id | string | Yes | ID of the task to cancel |
Safe mode vs YOLO mode
Each engine supports two permission levels, selected by the yolo parameter:
| Engine | Safe (yolo: false, default) | YOLO (yolo: true) |
|--------|-------------------------------|---------------------|
| Codex | exec --full-auto (workspace-write sandbox, on-request approvals) | exec --dangerously-bypass-approvals-and-sandbox (no sandbox, no approvals) |
| Gemini | --approval-mode=auto_edit (auto-approves file writes only) | --yolo (auto-approves all tools including shell commands) |
| Claude | --dangerously-skip-permissions | --dangerously-skip-permissions (same — no safer option exists) |
| OpenCode | run --dangerously-skip-permissions (permissions via config file) | run --dangerously-skip-permissions (same — config file determines policy) |
Safe mode is sufficient for read-only analysis tasks. Use YOLO mode when the engine needs unrestricted shell access or you're running in an externally sandboxed environment.
Completion protocol
syndic-mcp instructs each engine to write two files when it finishes:
.syndic/<id>.output.md — findings, results, or generated content:
## Output
(engine's findings/results).syndic/<id>.md — structured sentinel (read by syndic-mcp):
---
status: completed
---
## Summary
(what was accomplished)
## Files Changed
(list of changed files, or "None")
## Issues
(problems encountered, or "None")If the engine hits an unrecoverable error it uses status: failed in the sentinel. syndic-mcp falls back to process exit code if no sentinel is written.
Working directory and file isolation
Each task creates files under .syndic/ in the working directory:
<cwd>/
└── .syndic/
├── <id>.prompt # task instructions (written by syndic-mcp)
├── <id>.output.md # results (written by engine)
└── <id>.md # completion sentinel (written by engine)You can safely add .syndic/ to .gitignore.
Example: parallel code review
syndic_run(engine="codex", prompt="Review src/auth.ts for security issues. List findings in the output file.", cwd="/my/project")
syndic_run(engine="gemini", prompt="Review src/auth.ts for performance issues. List findings in the output file.", cwd="/my/project")Poll both with syndic_status until complete, then compare results.
Platform notes
- Windows only in the current release. Engines are spawned via
cmd.exe /cto handle.cmdshims (npm global installs on Windows). MSYS2_ARG_CONV_EXCL=*is set in the child environment to prevent MSYS2/Git Bash path mangling.
License
MIT
