Deployment Modes
flux7-mesh supports different configurations depending on who connects and how.
Configuration matrix
| # | Setup | Transport | Who launches mesh | Supervisor | Status |
|---|---|---|---|---|---|
| 1 | Solo dev + Claude/Cursor | MCP stdio | Claude spawns it | None | Works |
| 2 | Solo dev + Claude + supervisor | MCP stdio + HTTP | Claude spawns it | Passive (poll :9090) | Works while Claude runs |
| 3 | Supervisor standalone (no Claude) | HTTP | mesh7 serve / systemd |
Active | Works |
| 4 | External agent (LangChain, script) | HTTP | Manual or supervisor | Optional | Works |
| 5 | Claude + external agent | MCP stdio + HTTP | Claude spawns it | Optional | Works |
| 6 | Claude + supervisor (active spawn) | MCP stdio + HTTP | Both try to spawn | Active | Obsolete (sup7 never spawns) |
| 7 | 2 Claude sessions | MCP stdio × 2 | First spawns, second proxies | - | Works (v0.9.3+) |
| 8 | Managed Agents (cloud) | MCP Streamable HTTP | Manual / deploy | Optional | Works |
| 9 | Agent SDK + hooks | HTTP /decide |
Manual (mesh7 serve) |
None | Works |
Configs 1–5 and 8–9 work out of the box. Config 7 resolves itself through the --mcp auto-proxy (v0.9.3+). Config 6 is obsolete: sup7 no longer spawns anything, so the conflict it described cannot occur.
For anything beyond one developer at one terminal, run mesh7 serve under systemd. Every config below then collapses into the same shape: one owner for the mesh, clients that attach.
Config 1: Solo dev + Claude (most common)
The default. 90% of users. Zero setup.
Claude Code ──stdio──> flux7-mesh ──> filesystem, gmail, ollama...
│
:9090 HTTP (background, for mesh CLI / traces)
Claude launches mesh7 as an MCP subprocess. mesh7 launches upstream MCP servers, applies policies, records traces. When Claude quits, everything stops cleanly.
Setup: Just add flux7-mesh as an MCP server in Claude Code:
Config 2: Solo dev + Claude + supervisor (passive)
Claude manages flux7-mesh. Two layers of auto-resolve handle routine approvals before they reach a human.
Claude Code ──stdio──> flux7-mesh :9090 ──> tools
│
Level 1: built-in (mem7 lookup, ~100ms)
├── 3+ past approvals → auto-approve
└── else → escalate to Level 1+
│
Level 1+: supervisor (poll GET /approvals)
├── rules → approve/deny (0ms)
└── ollama → evaluate (~20s)
The built-in auto-approve (Level 1) fires before the approval queue — routine patterns never block. If it can't resolve, the external supervisor (Level 1+) evaluates with rules and LLM. If both escalate, the human decides.
Setup:
# Terminal 1: Claude (launches flux7-mesh automatically)
claude
# Terminal 2: Supervisor (flux7-supervisor / sup7)
sup7 -c sup7.yaml start
With supervisor.enabled: true in the flux7-mesh config, approval.resolve and approval.pending tools are hidden from Claude. Tool calls block until the supervisor resolves them.
Supervisor config (sup7.yaml):
mesh:
url: http://localhost:9090
agent_id: supervisor
evaluator:
provider: ollama # ollama | anthropic | claude-code
model: qwen3:14b
url: http://localhost:11434
confidence_threshold: 0.8
rules:
- name: project-writes
condition: "params.path starts_with project_dir"
action: approve
confidence: 0.9
sup7 never starts flux7-mesh. It attaches to one that is already listening, so the mesh's lifecycle is always someone else's: Claude here, systemd or mesh7 serve elsewhere.
Limitation: When Claude quits, flux7-mesh dies. The supervisor retries every poll_interval until Claude starts again.
Config 3: Supervisor standalone (no Claude)
For pipelines, overnight runs, CI/CD, batch jobs. No human at the keyboard: the supervisor resolves what it can and only genuine unknowns wait.
mesh7 serve (daemon, systemd) ──> tools
▲
│ poll → evaluate → resolve
│
sup7 (always alive) ──> decisions stored in mem7
Setup:
# The mesh must already be listening. sup7 does not start it.
mesh7 serve --config config.yaml # or: systemctl start mesh7
sup7 -c sup7.yaml status # check connectivity first
sup7 -c sup7.yaml start
Both ship a systemd unit, and sup7's declares After= and Wants=mesh7.service so the ordering is enforced by the init system rather than by whoever types the commands.
External agents connect via HTTP:
curl -X POST http://localhost:9090/tool/filesystem.write_file \
-H "Authorization: Bearer agent:my-script" \
-d '{"params":{"path":"/tmp/output.txt","content":"hello"}}'
Config 4: External agent only
Standard HTTP proxy mode. No MCP, no supervisor.
Setup:
Any HTTP client can call POST /tool/{name}, query traces, manage approvals. Works with LangChain, CrewAI, custom scripts, cron jobs.
Config 5: Claude + external agent (the real mesh)
Claude and external agents share the same flux7-mesh instance. One set of policies, one trace store, one approval queue.
This works today. Claude spawns flux7-mesh, the external agent connects via HTTP to :9090. Both are governed by the same policies.
Add a supervisor and you get Config 2 with extra agents — everything goes through one mesh.
Config 8: Anthropic Managed Agents (cloud, MCP Streamable HTTP)
Cloud-hosted agents connect to your flux7-mesh over the internet via MCP Streamable HTTP.
Anthropic cloud
├── Managed Agent (coordinator)
│ ├── sub-agent: reviewer
│ ├── sub-agent: tester
│ └── all use mcp_toolset "mesh"
│
└── MCP connector ── POST /mcp ──> flux7-mesh (your server, public URL)
│
policies, traces, mem7
│
upstream MCP servers
Setup:
# Anthropic SDK (Python)
agent = client.beta.agents.create(
name="Governed Assistant",
model="claude-opus-4-7",
mcp_servers=[{
"type": "url",
"name": "mesh",
"url": "https://mesh.example.com/mcp",
}],
tools=[
{"type": "agent_toolset_20260401"},
{"type": "mcp_toolset", "mcp_server_name": "mesh",
"default_config": {"permission_policy": {"type": "always_allow"}}},
],
)
Auth via vault (static bearer):
vault = client.beta.vaults.create(display_name="mesh-credentials")
client.beta.vaults.credentials.create(
vault_id=vault.id,
display_name="flux7-mesh token",
auth={
"type": "static_bearer",
"mcp_server_url": "https://mesh.example.com/mcp",
"token": "agent:my-managed-agent",
},
)
flux7-mesh extracts the agent ID from Authorization: Bearer agent:<id> and applies per-agent policies.
Networking: flux7-mesh must be accessible from Anthropic's cloud. Options:
- Dev: Tailscale funnel or ngrok → localhost:9090
- Prod: deploy flux7-mesh on a VPS or cloud host
Permission policies: Set always_allow on the Managed Agent side — let flux7-mesh handle governance. Double-layer approval (Managed Agents always_ask + flux7-mesh human_approval) works but adds friction.
Config 9: Anthropic Agent SDK + hooks
Agent SDK agents governed by mesh7 via MeshHooks. No MCP — hooks call /decide directly.
Agent SDK agent
├── PreToolUse hook ── POST /decide ──> flux7-mesh :9090
│ │
│ policies, traces, grants
│ │
└── tool execution (local) ───────────────┘
Setup:
from mesh7 import MeshHooks
hooks = MeshHooks(agent="my-agent")
agent = Agent(
model="claude-sonnet-4-6",
tools=[...],
options=ClaudeAgentOptions(hooks=hooks.agent_sdk_hooks()),
)
Same YAML policies as every other mode. human_approval maps to Agent SDK's "ask" (terminal prompt). For the full approval workflow (webhooks, CLI, auto-approve), use Config 1 or 8 instead.
See examples/agent-sdk-hooks/ and sdk/python/README.md.
Contention over who owns the mesh
Config 6: Claude + supervisor (active spawn) — no longer possible
This described both Claude and the supervisor spawning flux7-mesh on :9090, the second losing the bind, and the supervisor's restart loop turning that into an infinite crash loop.
It cannot happen any more. sup7 has no process-spawning capability at all: it attaches to a mesh that is already listening and gives up if none is. There is no mesh_process setting to disable, because there is nothing left to disable. Documented here only because older notes still reference the workaround.
The general rule survives the scenario that produced it: give the mesh an owner that is neither of its clients. mesh7 serve under systemd, with Claude auto-proxying and sup7 polling.
Config 7: Two Claude sessions
Historically, two Claude Code sessions sharing an MCP config both spawned flux7-mesh, the second instance silently lost the bind on :9090, and traces and approvals ended up split across two isolated processes.
Since v0.9.3 this resolves itself. mesh7 --mcp probes GET /health on the configured port before initialising anything. If something answers, the process becomes a stdio-to-HTTP shuttle to that instance instead of standing up a second mesh.
Claude session 1 ──stdio──> flux7-mesh :9090 ← owns the mesh
Claude session 2 ──stdio──> shuttle ──POST /mcp──> :9090 ← same instance
Note that this works even with no mesh7 serve daemon: an embedded MCP-mode instance wires the Streamable HTTP handler too, so session 1's mesh already serves POST /mcp for session 2 to attach to. One registry, one trace store, one approval queue.
Two things to know:
- Lifetime still belongs to session 1. Quit it and session 2's shuttle reports
proxy: daemon unreachable. Runmesh7 servewhen you want the mesh to outlive every client. - Both sessions default to the same identity.
--mcp-agentdefaults toclaude, so unless you give each session a distinct value, policies and traces cannot tell them apart.
Detection:
Decision flow: which config to use
Do you use Claude/Cursor?
│
├─ Yes, just Claude ──────────────────────────→ Config 1 (embedded)
│
├─ Yes, Claude + auto-approve ────────────────→ Config 2 (passive supervisor)
│
├─ Yes, Claude + external agents ─────────────→ Config 5 (shared mesh)
│
└─ No
│
├─ Want auto-approve / overnight runs ───→ Config 3 (supervisor standalone)
│
├─ Just HTTP proxy ─────────────────────→ Config 4 (standalone)
│
└─ Anthropic Managed Agents (cloud) ─→ Config 8 (MCP Streamable HTTP)
Component lifecycle
| Component | Who starts it | Who stops it | Persists across sessions |
|---|---|---|---|
| Ollama | System daemon | System | Yes |
| mesh7 | Claude (config 1/2/5) or mesh7 serve (config 3, daemon) |
Dies with parent (embedded) or persistent (daemon) | Yes with daemon mode |
| Upstream MCP servers | flux7-mesh (subprocesses) | Die with flux7-mesh | No |
| Supervisor | User (terminal) | User (Ctrl+C) | Yes (as long as terminal lives) |
| Claude Code | User | User | No |
Daemon mode (v0.9.4+)
A single persistent mesh7 instance shared by everyone:
mesh7 serve (daemon, persistent)
┌─────────────────────────────────────┐
Claude ──connect──> │ │──> tools
Agent B ───HTTP───> │ registry · policy · approval │
Agent C ───HTTP───> │ trace · grants · rate limiting │
└──────────────┬──────────────────────┘
│
supervisor (poll)
Two subcommands:
mesh7 serve— run as a persistent daemon (HTTP + manages upstream MCP servers)mesh7 --mcp— auto-detects a running daemon and proxies to it (MCP stdio for Claude Code)
This settles Config 2's limitation (the mesh dies with Claude) and makes Config 7 durable rather than merely working. Every client uses the auto-proxy instead of spawning its own mesh7, and the daemon outlives all of them.
| Feature | Status |
|---|---|
| Config 1: Embedded MCP | Done |
| Config 2: Passive supervisor | Done |
| Config 3: Supervisor standalone | Done |
| Config 4: Standalone HTTP | Done |
| Config 5: Shared mesh | Done |
| Config 8: Managed Agents (MCP Streamable HTTP) | Done (v0.9.0) |
supervisor.enabled (hide approval tools) |
Done |
mesh7 serve (daemon) |
Done (v0.9.4) |
mesh7 --mcp (auto-proxy to daemon) |
Done (v0.9.3) |