dangler MCP server
Commit Nov 10, 2026Acts as an MCP pre-loader that manages a fleet of MCP servers by lazy-loading tools and processes only when they are needed by the model.
Also known as ophiocus/dangler
- Stars
- 0
- Tools
- —
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Nov 10, 2026
- License
- MIT
What is dangler MCP?
dangler acts as an MCP pre-loader that manages a fleet of MCP servers by lazy-loading tools and processes only when they are needed by the model.
Summary generated from the project's README
About dangler MCP server
Managing a large collection of MCP servers can quickly exhaust model context windows and system resources. Dangler solves this by acting as a pre-loader that sits between your MCP client, such as Claude Desktop or Claude Code, and your entire fleet of servers. Instead of loading every tool schema at once, it advertises capabilities on demand, spawning downstream processes only when a tool is actually called and reaping idle ones automatically to save memory.
Users configure their fleet in a TOML file, defining either stdio child processes or hosted HTTP endpoints. The system supports various authentication methods, including OAuth 2.1 and static bearer tokens via header files. It also manages environment variables through dotenv-shaped files to keep secrets secure. By using five specialized meta-tools like search_tools and load_server, the model can discover and interact with specific capabilities without the overhead of a massive, permanent toolset.
What you can do with it
- 1Reduce context window usageA developer can prevent thousands of unnecessary tokens from being consumed by only loading tool schemas when they are needed.
- 2Search across multiple serversAn AI client can find specific functionality by searching through the cached schemas of the entire server fleet.
- 3Automate process cleanupYou can set a timeout to automatically shut down servers that are not being used to free up system resources.
Written from the project's README. Every use case is checked against a quote from it.
README
View on GitHubDangler is an MCP pre-loader. It sits between an MCP client (Claude Code, Claude Desktop, anything that speaks stdio MCP) and a fleet of MCP servers, and dangles their tools in front of the model — advertising what's available without paying for any of it until it's actually used.
Why
Every MCP server you register costs you twice before you've called a single tool:
- Context — every tool schema from every server is injected into the model's context up front. Ten servers × thirty tools each and you've burned thousands of tokens on schemas the conversation will never touch.
- Processes — stdio servers are spawned at client startup and sit resident whether used or not.
Dangler replaces all of that with one registration and five small meta-tools. The model discovers capability on demand, downstream servers spawn on first touch, and idle ones are reaped automatically.
| Meta-tool | What it does |
|---|---|
list_servers |
The configured fleet: name, warm/cold status, cached tool count |
search_tools {query} |
Search tool names/descriptions across the fleet's cached schemas |
load_server {name} |
Spawn the server if cold, return its full tool schemas — the dangle |
call_tool {server, tool, arguments} |
Proxy a call to a downstream tool (lazy-spawns) |
drop_server {name} |
Reap a downstream server's process now |
Quick start
git clone https://github.com/ophiocus/dangler.git
cd dangler
cargo build --release
Describe your fleet in dangler.toml (start from
dangler.example.toml):
idle_timeout_secs = 600 # reap warm children after 10 min unused (0 = never)
[servers.everything] # the MCP reference server — good first smoke test
command = "npx"
args = ["-y", "@modelcontextprotocol/server-everything"]
[servers.mongodb]
command = "C:/Program Files/nodejs/npx.cmd" # Windows: the .cmd shim, not bare `npx`
args = ["-y", "mongodb-mcp-server@latest", "--readOnly"]
identity = "Atlas as the scoped mcp_readonly user"
setup_hint = "write MDB_MCP_CONNECTION_STRING=… into ~/.mongodb/credentials"
env_file = "C:/Users/you/.mongodb/credentials" # secrets by reference, never inline
env_file is a dotenv-shaped file of KEY=VALUE lines merged into the child's
environment; the inline [servers.<name>.env] map wins on a clash, so one shared
file can be overridden per server. Comments and blank lines are ignored, and a
missing file fails the spawn with the file named and the setup_hint attached,
rather than starting a server with no credentials.
A fleet entry is either a stdio child (command) or a hosted HTTP endpoint
(url) — never both:
[servers.mybus] # static bearer
url = "https://api.example.com/mcp"
header_file = "C:/Users/you/.dangler/mybus.headers" # one `Authorization: Bearer …` line
identity = "the shared event bus, account 'you'"
[servers.hosted] # endpoint that answers 401 with a challenge
url = "https://mcp.vendor.com/mcp"
auth = "oauth" # then: dangler auth hosted
header_file is to HTTP what env_file is to stdio: Header: value lines, merged
under the inline [servers.<name>.headers] map, so the token stays out of
dangler.toml. With auth = "oauth" dangler runs the OAuth 2.1 dance itself —
discovery, dynamic registration, PKCE, refresh — and keeps the tokens in
~/.dangler/oauth/<server>.json. Consent is the operator's to give, once, in their
own browser: dangler auth <server> (loopback redirect on port 8899), never
something a session mints for itself.
⚠️ Keep credentials out of
dangler.toml— reference them withenv_file. The config still tends to accumulate paths and identities; this repo gitignores it.
Nothing is fixed at startup. dangler re-reads dangler.toml when it changes
(checked on every call, and forced by a call that names an entry it doesn't know),
installs a new entry's skill at that moment, and keeps the last good config if the
file stops parsing (list_servers shows config_error). A warm child whose entry
changed is respawned from the new spec once idle. Credential files are watched
too: when env_file, header_file, an OAuth grant or a path listed in the entry's
watch changes, the idle child is respawned on its next call, so a re-minted token
needs no restart:
[servers.mail]
command = "some-mcp-server"
watch = ["C:/Users/you/.secrets/mail-token.json"] # read once at startup by the child
Details: docs/architecture.md → "Nothing fixed at startup".
Then register dangler as the only MCP server your client needs:
claude mcp add dangler -e DANGLER_CONFIG=/path/to/dangler.toml -- /path/to/dangler
Optionally pre-index the whole fleet so search_tools works before anything has spawned:
dangler warm # spawns each server once, harvests schemas into ~/.dangler/cache.json, reaps
Every fleet entry carries a skill, and dangler installs it. Hidden schemas have a
blind spot — nothing tells a fresh session when to reach for a server — so each entry
ships a SKILL.md (frontmatter name + description, then the craft: which tool for
which need, the rules learned in use). At every start dangler copies them into the
client's skills directory (~/.claude/skills/<name>/SKILL.md, override with skills_dir
or DANGLER_SKILLS_DIR), prunes the copies of entries that left the fleet, and disables
any entry without one (listed as disabled, with the path it was expected at). The
skill is found at skill = "<path>", else the --directory of a uv run command, else
<extensions_dir>/<server>/SKILL.md. Copies carry a marker line and only marked files
are ever overwritten, so your own skills are safe.
dangler skills # install/refresh every entry's skill, print the table, exit 1 if any entry is disabled
How it works
upstream (one registration) downstream (the fleet, lazy)
┌────────────┐ MCP over stdio ┌─────────┐ spawn on first touch
│ MCP client │ ─────────────────────────▶ │ dangler │ ───▶ mongodb-mcp-server
│ (Claude) │ ◀───────────────────────── │ │ ───▶ any stdio MCP server
└────────────┘ 5 meta-tools, tiny schema└─────────┘ ───▶ …reaped when idle
Dangler is an MCP server and client at once (built on the official Rust SDK,
rmcp). Schemas harvested from each server persist in
~/.dangler/cache.json, so cross-fleet search works from a cold start; idle children are
reaped on a 30-second scan (per-server idle_timeout_secs override; in-flight requests
are never interrupted). Design details, battle-scars, and the roadmap live in
docs/architecture.md.
Windows without Node?
Servers can be launched through any bridge command. Example — running a Node-based MCP
server inside WSL from a Windows host (env vars cross via WSLENV):
[servers.mongodb]
command = "wsl"
args = ["-d", "Ubuntu-24.04", "--", "bash", "-lc", "npx -y mongodb-mcp-server@latest --readOnly"]
[servers.mongodb.env]
MDB_MCP_CONNECTION_STRING = "mongodb+srv://…"
WSLENV = "MDB_MCP_CONNECTION_STRING"
Extensions — first-party fleet servers
extensions/ holds MCP servers we own, built to be danglered: hand-written tool
schemas, stderr-only logging, and provisioning so lazy that dangler warm can
index them with nothing configured at all. They have no runtime coupling to
dangler — each is a plain stdio MCP server that happens to live here.
| Extension | Server | What it fronts |
|---|---|---|
extensions/godaddy |
dangler-godaddy |
GoDaddy REST APIs: domain portfolio, DNS record CRUD, subscriptions, availability — plus a raw_api escape hatch reaching every other endpoint (certificates, orders, agreements, aftermarket, …) |
extensions/google |
gws-mcp |
Google Docs, Sheets and Slides with full read/write, Drive (read; folders, uploads, moves for an account granted Drive write), and Gmail drafts with links stored exactly as given (plus sending a checked draft on the owner's word), for several Google accounts picked per call, on your own OAuth desktop client — files edited in place rather than recreated |
extensions/ytdl |
dangler-ytdl |
A bundled yt-dlp / ffmpeg / deno toolkit for local personal archiving: video, MP3 audio, transcripts |
extensions/comfy |
comfy-mcp |
One local ComfyUI service through the AIProd image gateway — identical tools on the GPU seat and on a remote seat, which reaches it over an event bus's piped transport |
extensions/myevery |
(hosted url entry) |
No code — the folder carries the bus's SKILL.md, because a wrapper wears a name tag like every other entry |
Each folder carries a SKILL.md next to its code: the name tag dangler installs into the
client at start (see above), so pulling this repo is also how the skill reaches a machine.
Language is not part of the contract. The Rust ones are Cargo workspace members:
cargo build --release --workspace # builds dangler + every Rust extension
extensions/google and extensions/comfy are Python and bring their own runner
(uv), so they are not workspace members and cargo build does not touch them.
Each extension documents its own provisioning in its README; the commented
blocks in dangler.example.toml show the fleet wiring for
every one, including identity and setup_hint.
Current limits
- Stdio upstream only — register it in Claude Code/Desktop or any stdio MCP client; no Streamable HTTP endpoint yet, so it can't be a claude.ai custom connector.
- Schema search is cached-substring, not semantic; run
dangler warmafter changing the fleet so search sees everything.
Both are on the roadmap. Downstream is no
longer stdio-only: hosted MCP endpoints are fronted natively since 2026-09-21
(streamable HTTP, static bearer or OAuth) — see the url entries above.
License
MIT © Carlos Santana
dangler MCP questions
How does dangler save context and resources?
It prevents all tool schemas from being injected into the model's context upfront and avoids spawning all stdio servers at startup, instead spawning them on first touch.
What happens to idle MCP servers?
Idle downstream servers are automatically reaped after a period of inactivity to save resources.