synapse is the Rust MCP and CLI server for local Synapse workflows. It is a
full-parity Rust port of synapse-mcp, exposing two MCP tools:
fluxfor Docker daemon, container, host, and Compose operations.scoutfor SSH/local filesystem, process, ZFS, log, transfer, and allowlisted command operations.
The binary is named synapse. HTTP MCP defaults to 127.0.0.1:40080, and the
REST compatibility endpoint is POST /v1/synapse.
| Fact | Value |
|---|---|
| Remote | git@github.com:dinglebear-ai/synapse.git |
| Cargo workspace | 2 members: . (crate synapse) and xtask |
| Edition / MSRV | 2024 / rust-version = 1.97.1 (rust-toolchain.toml pins channel 1.97.1) |
| MCP framework | rmcp = "=3.0.0-beta.2" via [workspace.dependencies]; verify against Cargo.lock |
| Binary | synapse ([[bin]], autobins = false) |
| Service port | 40080 |
| Tools | two: flux and scout |
| Action surface | 59 operations in OPERATION_SPECS — 14 REST-reachable, 45 MCP-only |
Cargo, package, plugin, registry, and Compose metadata use the canonical
dinglebear-ai/synapse repository and GHCR namespaces.
| File | Role |
|---|---|
src/app.rs |
SynapseService facade over FluxService and ScoutService; keep it thin. |
src/flux_service.rs |
Flux domain root: shared host resolution, Docker cache, Compose discovery, help. |
src/flux_service/docker_driver.rs |
Flux Docker driver methods. |
src/flux_service/container_driver.rs |
Flux container driver methods. |
src/flux_service/host_driver.rs |
Flux host inspection driver methods. |
src/flux_service/compose_driver.rs |
Flux Compose driver methods. |
src/flux_service/docker.rs |
Pure Docker helper functions and validation. |
src/flux_service/container_read.rs |
Pure container read helpers. |
src/flux_service/container_lifecycle.rs |
Pure container lifecycle helpers. |
src/flux_service/host.rs |
Pure host exec helpers. |
src/flux_service/compose_ops.rs |
Pure Compose command-building/result helpers. |
src/scout_service.rs |
Scout domain root and shared helpers. |
src/scout_service/exec.rs |
Scout exec/emit/beam implementations. |
src/scout_service/fs.rs |
Scout filesystem (peek/find/delta) implementations. |
src/scout_service/logs.rs |
Scout log retrieval (syslog/journal/dmesg/auth) implementations. |
src/scout_service/proc.rs |
Scout process/disk (ps/df) implementations. |
src/scout_service/zfs.rs |
Scout ZFS introspection (pools/datasets/snapshots) implementations. |
src/actions.rs |
Top-level action metadata, scope constants, SynapseAction enum, shared param helpers. |
src/actions/operations.rs |
Canonical OPERATION_SPECS registry (59 entries) — name, tool, action, subaction, scope, destructive flag, transport, required params. Source of truth for MCP, REST, schemas, and docs. |
src/actions/rest.rs |
REST-reachable subset of the operation registry. |
src/actions/dispatch.rs |
execute_service_action dispatch, error-type helpers. |
src/actions/flux.rs |
Typed flux argument structs and from_flux_args parser. |
src/actions/scout.rs |
Typed scout argument structs and from_scout_args parser. |
src/mcp/tools.rs |
MCP shim: parse JSON args, call service, return Value. |
src/mcp/schemas.rs |
MCP tool JSON schema derived from action metadata. |
src/mcp/help.rs |
Topic index and dispatch for src/mcp/help_topics.rs. |
src/mcp/help_topics.rs |
Full topic help text for every shipped action/subaction. |
src/mcp/rmcp_server.rs |
ServerHandler impl: tools/resources/prompts and scope checks. |
src/mcp/resources.rs |
MCP resource definitions (synapse://hosts, synapse://compose/projects, etc.). |
src/mcp/prompts.rs |
MCP prompt definitions. |
src/mcp/response.rs |
MCP response shaping helpers. |
src/mcp/transport.rs |
Streamable HTTP transport wiring and session lifecycle. |
src/server.rs and src/server/routes.rs |
HTTP server state, auth policy, Axum routes. |
src/api.rs |
REST compatibility handlers for /v1/synapse, /health, /status. |
src/config.rs |
Config, McpConfig, AuthConfig, dotenv/env/config loading. |
src/cli.rs |
CLI entry: mode dispatch, global flags, top-level help. |
src/cli/flux.rs |
CLI flux subcommand parsing and dispatch. |
src/cli/scout.rs |
CLI scout subcommand parsing and dispatch. |
src/cli/doctor.rs |
Pre-flight checks: env, connectivity, config validation. |
src/cli/setup.rs |
Interactive first-run / plugin setup wizard. |
src/cli/watch.rs |
Polls /health and emits state-change lines for plugin monitor. |
src/cli/help.rs |
CLI help text rendering. |
src/docker_client.rs |
Docker client module entry: re-exports, mode dispatch. |
src/docker_client/traits.rs |
DockerClient and DockerImageClient async trait definitions. |
src/docker_client/bollard_client.rs |
Bollard-backed implementation of Docker traits. |
src/docker_client/cache.rs |
Per-host Docker client cache with transport-death eviction. |
src/docker_client/mock.rs |
MockDockerClient for unit tests. |
src/ssh.rs |
SSH execution/session pool and forwarded Docker socket support. |
src/ssh/ |
SSH pool, config, executor, transport implementations. |
src/fanout.rs |
Cross-host request fan-out with concurrency cap, timeout, and partial-failure handling. |
src/elicitation_gate.rs |
Confirmer trait + MCP/Cli/NoConfirm/DenyConfirm implementations. |
src/cache.rs |
Generic TTL-keyed async cache shared by Docker client and Compose discovery. |
src/formatters.rs |
Response formatting helpers (table, JSON, markdown). |
src/formatters/ |
Per-format and per-action formatter implementations. |
src/logging.rs |
Tracing subscriber setup, log format selection, color policy. |
src/logging/aurora.rs |
Aurora-themed tracing formatter. |
src/logging/formatter.rs |
Generic tracing event formatter. |
src/color_policy.rs |
NO_COLOR/FORCE_COLOR detection and terminal color capability. |
src/scaffold.rs |
First-run directory/config scaffolding for bare-metal installs. |
src/synapse.rs |
Cross-cutting types and traits shared by flux and scout. |
src/synapse/command_policy.rs |
Security-critical: ALLOWED_READ_COMMANDS (18 commands), EXEC_DENYLIST, and validate_command. Any change here must be mirrored in src/mcp/schemas.rs, src/mcp/help_topics.rs, docs/API.md, README.md, and the plugin SKILL. |
src/secure_path.rs |
Read-root confinement and sensitive/symlink path rejection. |
src/activity.rs |
In-memory activity ring backing the synapse://activity resource. |
src/compose.rs |
Compose project discovery and caching logic. |
src/scout.rs |
Scout domain types and shared SSH execution helpers. |
src/docker.rs |
Docker domain types and shared container/image helpers. |
src/token_limit.rs and src/runtime_budget.rs |
Response byte caps and operation deadlines. |
src/host_config.rs |
Shared host topology loading from SYNAPSE_HOSTS_CONFIG, SYNAPSE_CONFIG_FILE, and ~/.ssh/config. |
src/web.rs |
Optional static web UI: asset serving and SPA fallback. |
src/main.rs |
Mode dispatch: HTTP server, stdio MCP, CLI. |
src/lib.rs |
Public API plus testing helpers for integration tests. |
tests/cli_parse.rs |
CLI argument parsing tests (plus cli_parse_{docker,container,flux,scout}.rs). |
tests/tool_dispatch.rs |
MCP tool dispatch tests using loopback state. |
tests/api_routes.rs |
REST route/auth tests. |
tests/parity.rs |
Asserts the action surface matches the synapse-mcp INVENTORY table. |
tests/plugin_contract.rs |
Plugin manifest/monitor invariants and setup plugin-hook JSON contract. |
tests/docker_client.rs, tests/ssh_pool.rs |
Docker client and SSH pool integration tests. |
tests/template_invariants.rs |
Repo-shape invariants inherited from the scaffold. |
src/mcp/tools.rs, src/api.rs, and src/cli.rs contain no business logic.
They only parse their input format, call the relevant service method, and render
or return the result. Put validation, filtering, mutation sequencing, fanout,
Docker/SSH behavior, and response shaping in the domain modules.
Production Rust modules are checked by scripts/check-rust-module-size.sh via
lefthook, just module-size-check, and CI:
- Soft advisory: 400 real-code lines.
- Hard failure: 1000 real-code lines.
- Test sidecars (
*_tests.rs) andtests/are exempt.
Use sibling modules (foo.rs plus foo/ children) and never create mod.rs.
xtask and the pattern checker enforce this.
SynapseService must remain a thin facade. Add flux behavior to
FluxService or its focused submodules. Add scout behavior to ScoutService
or its focused submodules.
- Update the relevant domain service:
- Flux:
src/flux_service/* - Scout:
src/scout_service/*
- Flux:
- Update typed action parsing and scope semantics in
src/actions/. - Update MCP dispatch in
src/mcp/tools.rs. - Update CLI parsing and run dispatch in
src/cli.rsorsrc/cli/flux.rs/src/cli/scout.rs. - Update MCP schema parameters in
src/mcp/schemas.rs. - Update topic help in
src/mcp/help.rs. - Update docs that list actions:
README.md,docs/API.md,docs/MCP_SCHEMA.md, andplugins/synapse/skills/synapse/. - Add tests:
- parser coverage in
tests/cli_parse.rs - MCP dispatch coverage in
tests/tool_dispatch.rs - service/helper coverage in the nearest sibling
*_tests.rs
- parser coverage in
- Add a
CHANGELOG.mdentry when the change is user-visible.
The help map is manual. If a new action lacks a src/mcp/help.rs topic, live
help calls will drift even when schemas compile.
| Policy | When | Effect |
|---|---|---|
AuthPolicy::LoopbackDev |
loopback bind or loopback no-auth | No auth middleware; scopes bypassed. |
AuthPolicy::TrustedGatewayUnscoped |
SYNAPSE_NOAUTH=true on a gateway-isolated non-loopback deployment |
Gateway owns auth/authz; local scopes bypassed. |
AuthPolicy::Mounted { auth_state: None } |
default non-loopback bearer mode | Static bearer token required. |
AuthPolicy::Mounted { auth_state: Some(_) } |
SYNAPSE_MCP_AUTH_MODE=oauth |
Google OAuth plus RS256 JWT issuance. |
Scopes are synapse:read and synapse:write; write satisfies read. Public
help actions require no scope. Unknown actions fail closed.
Destructive operations use the Confirmer gate. MCP uses elicitation, CLI prints
a warning and proceeds, and REST denies confirmation-gated actions unless
SYNAPSE_MCP_ALLOW_DESTRUCTIVE=true substitutes NoConfirm. Startup refuses
that override on non-loopback binds.
| Variable | Default | Description |
|---|---|---|
SYNAPSE_MCP_HOST |
127.0.0.1 |
HTTP bind host. |
SYNAPSE_MCP_PORT |
40080 |
HTTP bind port. |
SYNAPSE_MCP_SERVER_NAME |
synapse |
MCP server name. |
SYNAPSE_MCP_NO_AUTH |
false |
Disable auth for loopback dev only. |
SYNAPSE_NOAUTH |
false |
Trusted gateway mode; upstream owns auth/authz and must be the only network peer. |
SYNAPSE_MCP_ALLOW_DESTRUCTIVE |
false |
Skip destructive confirmation prompts; loopback only. |
SYNAPSE_MCP_MAX_CONCURRENCY |
50 |
Global concurrency cap on /mcp and /v1/synapse; excess requests receive HTTP 429 with Retry-After. 0 = disable. /health//ready//status exempt. |
SYNAPSE_MCP_TOKEN |
unset | Static bearer token. |
SYNAPSE_MCP_ALLOWED_HOSTS |
unset | Extra accepted Host header values. |
SYNAPSE_MCP_ALLOWED_ORIGINS |
unset | Extra CORS origins. |
SYNAPSE_MCP_PUBLIC_URL |
unset | Public URL for OAuth metadata. |
SYNAPSE_MCP_AUTH_MODE |
bearer |
bearer or oauth. |
SYNAPSE_MCP_GOOGLE_CLIENT_ID |
unset | Google OAuth client id. |
SYNAPSE_MCP_GOOGLE_CLIENT_SECRET |
unset | Google OAuth secret. |
SYNAPSE_MCP_AUTH_ADMIN_EMAIL |
unset | Bootstrap OAuth admin email. |
SYNAPSE_MCP_AUTH_SQLITE_PATH |
/data/auth.db |
OAuth session/client database path. |
SYNAPSE_MCP_AUTH_KEY_PATH |
/data/auth-jwt.pem |
OAuth JWT signing key path. |
SYNAPSE_MCP_AUTH_ACCESS_TOKEN_TTL_SECS |
3600 |
OAuth access-token TTL in seconds. |
SYNAPSE_MCP_AUTH_REFRESH_TOKEN_TTL_SECS |
2592000 |
OAuth refresh-token TTL in seconds (30 days). |
SYNAPSE_MCP_AUTH_CODE_TTL_SECS |
300 |
OAuth authorization-code TTL in seconds. |
SYNAPSE_MCP_AUTH_REGISTER_REQUESTS_PER_MINUTE |
10 |
OAuth dynamic-registration rate limit. |
SYNAPSE_MCP_AUTH_AUTHORIZE_REQUESTS_PER_MINUTE |
60 |
OAuth authorization rate limit. |
SYNAPSE_MCP_AUTH_DISABLE_STATIC_TOKEN_WITH_OAUTH |
true |
Disable static bearer tokens when OAuth is active. |
SYNAPSE_MCP_AUTH_ALLOWED_REDIRECT_URIS |
unset | Extra OAuth redirect URI patterns (comma-separated). |
SYNAPSE_HOSTS_CONFIG |
unset | Inline host topology JSON. |
SYNAPSE_CONFIG_FILE |
unset | Host config file path. |
SYNAPSE_HOME |
platform appdata | Appdata root; defaults to ~/.synapse outside containers and /data in containers. |
DOCKER_GID |
unset | Host docker group id; required when the Docker socket is mounted in Docker. |
DOCKER_NETWORK |
mcp |
Docker network name for the production compose stack. |
SYNAPSE_VERSION |
latest |
Image tag used by docker-compose.prod.yml. |
SYNAPSE_MCP_HOST_PORT |
40080 |
Host port published to the container's MCP port. |
RUST_LOG |
info |
Tracing filter. |
NO_COLOR |
unset | Disable ANSI color in console output when set. |
FORCE_COLOR |
unset | Force ANSI color even when stderr is not a TTY. |
See .env.example, config.example.toml, docs/CONFIG.md, and docs/ENV.md
for the full runtime contract.
cargo build --release
cargo test --locked
cargo clippy --locked -- -D warnings
cargo fmt --check
just dev
just test
just lint
just fmt
just gen-token
just healthcargo-llvm-cov coverage currently needs the direct-rustc workaround on this
host when the local sccache-wrapper mishandles --check-cfg. Resolve the
toolchain rather than hardcoding a version — rust-toolchain.toml pins channel
1.97.1, so a literal path pinned to some other release will silently be wrong:
env -u RUSTC_WRAPPER \
RUSTC="$(rustup which rustc)" \
LLVM_PROFILE_FILE=target/llvm-cov/profiles/%p-%m.profraw \
cargo llvm-cov --locked --workspace --lcov --output-path target/llvm-cov/lcov.infoCoverage runs drop *.profraw files in the repo root; delete them afterwards so
they do not get committed.
src/lib.rs exports testing::loopback_state() and testing::bearer_state(token)
behind features = ["test-support"] or cfg(test). Use these in integration
tests to build AppState without real credentials.
Prefer sibling sidecar tests for production modules (src/foo_tests.rs or
src/foo/bar_tests.rs). Integration tests belong in tests/.
Every production MCP action must also be reachable from the CLI, except protocol concepts such as MCP resources/prompts and elicitation-only flows.
The action surface is 59 operations — count them with
grep -c 'operation!(' src/actions/operations.rs, never by hand from this list.
Breakdown: flux docker 9, flux container 14, flux host 9, flux compose
10, scout simple 9, scout zfs 3, scout logs 4, plus one shared help
(= 59). By transport: 14 Rest, 45 McpOnly. Current production action
families:
flux docker:info,df,images,networks,volumes,pull,build,rmi,pruneflux container:list,inspect,logs,stats,top,search,start,stop,restart,pause,resume,pull,recreate,execflux host:status,info,uptime,resources,services,network,mounts,ports,doctorflux compose:list,status,up,down,restart,recreate,logs,build,pull,refreshscout:nodes,peek,find,ps,df,delta,exec,emit,beam,zfs,logsflux helpandscout help
Run cargo test --locked --test parity after action-surface changes.
Plugin manifests (.claude-plugin/plugin.json, .codex-plugin/plugin.json,
gemini-extension.json) do not contain a version field. The marketplace
derives versions from git commits. Do not add version or run version-bump
scripts against plugin manifests.
plugins/synapse/ ships no lifecycle hooks: no hooks/ directory, no
hooks key in any manifest. scripts/validate-plugin-layout.sh and
tests/plugin_contract.rs both assert this. Do not reintroduce either.
The removed SessionStart/ConfigChange hook translated CLAUDE_PLUGIN_OPTION_*
settings into SYNAPSE_* env vars, created SYNAPSE_HOME mode 700, wrote and
repaired .env, validated auth and port, and refreshed ~/.local/bin/synapse.
All of that behavior still lives in the binary (src/cli/setup.rs) — only the
automatic invocation is gone. Manual equivalent:
synapse setup install # refresh ~/.local/bin/synapse
synapse setup plugin-hook # check, then repair on blocking failures
synapse setup plugin-hook --no-repair # audit only; never mutates appdataExport the SYNAPSE_* variables yourself, or put them in ~/.synapse/.env,
before running these. plugins/README.md holds the plugin-option→env-var
mapping. Client mode — connecting to a server running elsewhere — needs none of
this; mcp.json substitutes the plugin settings directly.
monitors/monitors.json now invokes synapse watch from PATH instead of a
hooks/watch.sh wrapper. The wrapper used to exit 0 when the binary was
missing; a direct invocation surfaces that as a monitor error instead.
- Stdio mode lowers log verbosity so JSON-RPC on stdout is not corrupted.
helpis public; all non-help actions require at leastsynapse:read.watch,serve,mcp,doctor, andsetupare CLI infrastructure, not MCP production actions.scout execuses an allowlist and execvp semantics; no shell metacharacters.container exectakes an argv array and runs inside the target container.- Read operations may fan out when
hostis omitted; destructive operations require explicit targets and confirmation.
This project uses bd (beads) for issue tracking. Run bd prime to see full workflow context and commands.
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work
bd close <id> # Complete work- Use
bdfor ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists - Run
bd primefor detailed command reference and session close protocol - Use
bd rememberfor persistent knowledge — do NOT use MEMORY.md files
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd dolt push git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds