Install
$ agentstack add mcp-jhonsferg-local-swarm-mcp Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
🐝 local-swarm-mcp
An MCP server that delegates mechanical, low-judgment tasks to local or remote OpenAI-compatible inference backends (llama.cpp's llama-server, Ollama, vLLM, or any hosted provider exposing the same API shape), and gives an MCP client full control over that delegated work: fire-and-forget background tasks, multi-turn sessions, a persistent scratch store, and context-budgeting helpers - the same primitives an agent orchestration system offers for its own subagents, backed by a model running on your own hardware. 🖥️⚡
🧠 Why
Judgment-heavy work (architecture decisions, deciding whether a finding is a false positive, writing meaningful tests) needs a strong model. Mechanical work (boilerplate generation, log summarization, formatting, repetitive transforms) doesn't. This server lets an MCP client offload the latter to whatever hardware you already have running a local model, keeping its own token budget for the former - and treat that offloaded work like its own background agents rather than a single blocking request/response call. 💸
🗺️ Architecture
flowchart LR
subgraph Client["🤖 MCP Client"]
A["AI Agent(e.g. Claude Code)"]
end
subgraph Server["🐝 local-swarm-mcp"]
direction TB
B["MCP Tools(stdio / HTTP)"]
C["Backend Registry"]
D["Task Registry(async + agent loop)"]
E["Session Registry(multi-turn)"]
F["Scratch Store(bbolt)"]
M["Downstream MCPclient manager"]
B --> C
B --> D
B --> E
B --> F
D --> M
end
subgraph Backends["⚙️ Inference Backends"]
G["llama.cppllama-server"]
H["Ollama"]
I["vLLM /remote provider"]
end
subgraph Downstream["🧩 Downstream MCP servers"]
J["codebase-memory-mcp"]
K["...other configured servers"]
end
A |"MCP protocol"| B
C -->|"OpenAI-compatible API"| G
C -->|"OpenAI-compatible API"| H
C -->|"OpenAI-compatible API"| I
M |"stdio MCP client"| J
M |"stdio MCP client"| K
Every backend just speaks the OpenAI /v1/chat/completions + /v1/models shape, so the server never hardcodes vLLM/llama.cpp/Ollama-specific logic. Adding a new provider is a config-file edit, not a code change. The downstream MCP client manager lets a spawn_agent_task agent call other MCP servers' tools autonomously, in a loop, the same way a host application calls its own tools - see [Tool-using agents](#-tool-using-agents-autonomous-like-a-host-applications-own-agents).
🤝 Expected client behavior: check availability, then prefer this over native agents
This server is meant to sit alongside, not replace, an MCP client's own native agent/subagent capability.
sequenceDiagram
participant Agent as 🤖 AI Agent
participant MCP as 🐝 local-swarm-mcp
participant LLM as 🧩 Local Model
Agent->>MCP: health_check()
alt backend healthy
Agent->>MCP: classify_task_risk(description)
MCP-->>Agent: risk = low
Agent->>MCP: spawn_task(prompt)
MCP-->>Agent: task_id
MCP->>LLM: POST /v1/chat/completions
LLM-->>MCP: completion
Agent->>MCP: wait_task(task_id)
MCP-->>Agent: result ✅
else backend unreachable
Agent->>Agent: fall back to native agent 🔁
end
- At the start of a task that could be delegated, call
list_backends
and/or health_check to confirm at least one backend is actually reachable right now - a configured backend whose llama-server/Ollama process isn't running, or a remote HTTP transport instance that's offline, should not silently block work.
- If a backend is healthy, prefer
spawn_task/create_session
over the client's own native subagent mechanism for mechanical, low-judgment work (use classify_task_risk first if unsure whether a task qualifies) - this is what actually saves tokens/cost, since the work runs on hardware you already own instead of the client's own paid model.
- If no backend is healthy, fall back to the client's native agents
for that work rather than failing the task - this server is a cost-saving optimization, not a hard dependency.
Nothing in the server enforces this - it's a client-side policy this project is designed to support. See a specific client's own configuration (rules file, memory, system prompt, etc.) for how it's told to apply it.
⚙️ Prerequisites
1. An OpenAI-compatible inference backend, running somewhere reachable. local-swarm-mcp does not embed or bundle an inference engine itself - it's a thin client in front of one. Pick whichever fits your hardware; both were verified end-to-end against this server during development.
🦙 Ollama (easiest - recommended if you just want something working)
Ollama bundles model management and an OpenAI-compatible API, and auto-detects your GPU backend (CUDA/ROCm/Metal/CPU) with no manual backend selection.
Windows:
winget install --id Ollama.Ollama -e
macOS: download from ollama.com/download, or brew install ollama. Linux:
curl -fsSL https://ollama.com/install.sh | sh
Then pull a small model and start serving (Ollama also auto-starts as a background service on Windows/macOS after install - ollama serve is only needed if it isn't already running):
ollama pull qwen2.5-coder:1.5b # ~1GB, comfortable on 4-6GB VRAM laptops
ollama serve # if not already running as a service
Verify it's up:
curl http://localhost:11434/v1/models
Ollama's OpenAI-compatible endpoint is http://localhost:11434/v1.
🔧 llama.cpp (better for mixed NVIDIA/AMD hardware)
More manual, but its Vulkan backend runs on both NVIDIA and AMD GPUs without depending on ROCm's maturity - useful if you have, say, an NVIDIA laptop and an AMD desktop and want one build that works on both.
# build from source (https://github.com/ggml-org/llama.cpp), or grab a
# release binary for your platform, then:
llama-server -m /path/to/model.gguf --host 0.0.0.0 --port 8080
Its OpenAI-compatible endpoint is http://:8080/v1.
🌐 Anything else
vLLM, or any hosted provider with an OpenAI-compatible /v1/chat/completions endpoint, also work - just point a backend entry at it.
2. local-swarm-mcp itself - either grab a release binary or build from source. See [Installing](#-installing) below.
📦 Installing
Linux / macOS
curl -fsSL https://raw.githubusercontent.com/jhonsferg/local-swarm-mcp/main/install/install.sh | sh
Windows
irm https://raw.githubusercontent.com/jhonsferg/local-swarm-mcp/main/install/install.ps1 | iex
Both scripts detect your OS/arch, download the matching release archive and its checksums.txt, verify the SHA-256 checksum, and install local-swarm-mcp (.exe on Windows) to ~/.local/bin (Linux/macOS) or %USERPROFILE%\.local\bin (Windows). Set LSM_INSTALL_DIR/LSM_VERSION ($env:LSM_INSTALL_DIR/$env:LSM_VERSION on Windows) before piping to override the install location or pin a specific version instead of latest.
Releases are built by goreleaser for {linux,darwin,windows} x {amd64,arm64} and published automatically on every merge to main whose commits since the last tag warrant a version bump (Conventional Commits: feat: -> minor, fix:/perf:/refactor: -> patch, feat!:/BREAKING CHANGE -> major). See releases.
From source
go build -o local-swarm-mcp ./cmd/local-swarm-mcp
Requires Go 1.26 or newer.
🛠️ Configuring backends
There's no config file to maintain: run as the persistent daemon and add backends via the CLI or the web UI, both of which take effect immediately with no restart.
# Start the daemon once, with the dashboard on
local-swarm-mcp -transport http -insecure-no-auth -ui
# Then, from the CLI (a separate terminal, or a script) - or from the
# dashboard's "Register host" form instead:
local-swarm-mcp -register-host -name local-llama -host-base-url http://localhost:11434
-register-host either talks to the daemon you just started (if one's already running at -http-addr) or, if none is running yet, becomes the daemon itself - see [Host discovery](#-host-discovery---add-inference-hardware-without-editing-config-or-restarting) below for the full mechanics. A single ad-hoc backend from flags alone (no daemon, no registration) still works too, e.g. for a quick one-off -transport stdio run:
local-swarm-mcp -backend-name local-llama -backend-url http://localhost:8080/v1 -backend-model qwen2.5-coder
Flags that matter most
| Flag | Default | Purpose | |---|---|---| | -transport | stdio | "stdio" (spawned as a local subprocess) or "http" (the persistent daemon) | | -ui | false | Serve the embedded dashboard at / - only meaningful for -transport=http | | -http-addr | :8090 | Listen address when -transport=http, e.g. :8090 or 0.0.0.0:8090 | | -api-key | (none) | Bearer token HTTP clients must present when -transport=http; required unless -insecure-no-auth is set | | -insecure-no-auth | false | Allow -transport=http with no -api-key - only on a trusted, isolated network | | -register-host | false | Register an inference host for background discovery - see -name/-host-base-url/-host-api-key | | -backend-name/-backend-url/-backend-model/-backend-key | (none) | A single ad-hoc backend, for a quick run without the daemon | | -store-path | /local-swarm-mcp/scratch.db | Override the scratch-store file location |
Run local-swarm-mcp -h for the full, grouped flag reference (host discovery, downstream MCP servers, storage locations, and more).
Legacy: YAML/JSON config file
A -config path/to/config.yaml (or .json) file with backends: and mcp_servers: lists still works and is still auto-detected at /local-swarm-mcp/config.yaml if present - this predates host discovery and dynamic downstream MCP server registration, and is no longer the recommended way to configure anything covered by those features. It remains useful for exactly one thing: a fixed, version- controllable starting point loaded once at daemon startup (or for a -transport stdio run where a persistent daemon isn't wanted at all).
backends:
- name: local-llama
base_url: http://localhost:8080/v1
model: qwen2.5-coder
mcp_servers:
- name: codebase-memory-mcp
command: /path/to/codebase-memory-mcp
store_path: C:\Users\you\.cache\local-swarm-mcp\scratch.db
⚠️ This file auto-loads even if you never pass -config - the flag's default value is that same ` path, so if it exists from an earlier setup, its backends: list gets loaded every time and sits alongside anything registered dynamically, permanently, since a static entry is never touched by the discovery poller (it isn't the poller's to manage). If you've moved to host discovery and still see an old model that no longer exists on some host, this file - not a bug in discovery - is almost certainly why: either delete it, or empty out its backends:` list, and restart the daemon.
🔌 Registering with an MCP client
💻 Local (stdio) - the common case
Add an entry to your client's MCP config (e.g. Claude Code's .mcp.json) pointing command at the built binary:
{
"mcpServers": {
"local-swarm-mcp": {
"command": "/path/to/local-swarm-mcp"
}
}
}
Keep this path unversioned (local-swarm-mcp, not local-swarm-mcp-v2): rebuilding to the same fixed path means your client's config never needs to change and you never need to restart it just to pick up a new build. Windows won't let you overwrite a .exe its own running process has open, but it will let you rename one out of the way first - so on Windows, rebuild like this instead of building straight over it:
Rename-Item local-swarm-mcp.exe local-swarm-mcp.exe.old -ErrorAction SilentlyContinue
go build -o local-swarm-mcp.exe ./cmd/local-swarm-mcp
The already-running process keeps working fine off the renamed file (Windows processes hold an open handle, not a path) until it's next restarted, at which point it picks up the new build from the same stable path - no config edit required.
🌍 Remote (HTTP) - running on a separate GPU machine
If your inference hardware lives on a different machine than your MCP client (e.g. a DGX Spark, or any other PC with a GPU on your network), run local-swarm-mcp there instead, then register that machine's own local inference server as a host once it's up (from another machine, or from the dashboard):
local-swarm-mcp -transport http -http-addr 0.0.0.0:8090 -api-key -ui
Then point your MCP client at it over HTTP (exact config syntax depends on your client - check whether it supports a url + headers style MCP entry, e.g.):
{
"mcpServers": {
"local-swarm-mcp": {
"url": "http://gpu-host:8090/mcp",
"headers": { "Authorization": "Bearer " }
}
}
}
🔒 -api-key is required unless you pass -insecure-no-auth - anyone who can reach the port can otherwise spawn tasks and read/write the scratch store, so treat it like any other network-exposed service. -insecure-no-auth is only reasonable on a network you fully trust and isolate (e.g. a home LAN with no other untrusted devices).
🛰️ Host discovery - add inference hardware without editing config or restarting
Once running with -transport http, local-swarm-mcp becomes a small service-discovery daemon for your inference hosts: register a host once (a desktop GPU, a DGX Spark, an AMD AI box - anything speaking Ollama's API) and it's polled in the background forever after. New models pulled on that host show up automatically as / backends - no YAML/JSON edit, no client restart.
local-swarm-mcp -register-host -name rx9070 -host-base-url http://192.168.18.29:11434
- If a daemon is already running at the default address, this is a thin
HTTP client: it forwards the registration and exits.
- If no daemon is running yet, this invocation becomes the daemon (same
process, same leader-election mechanism -transport http normally goes through) and keeps the registration.
Exactly one process ever wins: every invocation probes -http-addr first, and if nothing answers there, falls back to a PID lockfile (/local-swarm-mcp/daemon.lock) with a health check as a second, independent confirmation - a stale lock from a crashed daemon is reclaimed automatically (its PID is checked for liveness), but a live PID that isn't actually answering as a real local-swarm-mcp daemon is reported as a genuine conflict rather than silently overridden.
The same operations are also available as MCP tools (register_backend_host, unregister_backend_host, list_backend_hosts) so an agent - or you, through your MCP client - can manage hosts directly over the existing connection. list_backend_hosts reports whether each host is currently reachable (up), so you (or an agent orchestrating work) can tell a host that's merely registered but powered off from one that's actually available before dispatching a task to it.
🔌 Downstream MCP servers, dynamically
The mcp_servers: config-file list works the same way it always has, but it's legacy now: register_downstream_mcp_server (and its unregister_downstream_mcp_server / list_downstream_mcp_servers counterparts) registers a server like codebase-memory-mcp at runtime - spawned and connected immediately, persisted so it reconnects automatically across daemon restarts, no config file or restart needed. The same three operations are also exposed over the admin HTTP surface (POST /admin/register-mcp-server, POST /admin/unregister-mcp-server, `GET /admin/
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jhonsferg
- Source: jhonsferg/local-swarm-mcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.