droid-mode
v1.0.0
Published
Progressive Code-Mode MCP integration for Factory.ai Droid - access MCP tools without context bloat
Maintainers
Readme
Progressive MCP for AI Agents. Zero Context Bloat.
Access MCP tools on-demand without loading schemas into your context window.
Quick Start · Architecture · Benchmarks · Commands
A Personal Note
I spend most of my time in Factory.ai's Droid CLI, and I'm a big believer in MCP - it's the right abstraction for giving AI agents access to external tools. But I kept bumping into an interesting constraint. Every MCP server adds its tool schemas to the context window, which means adding servers costs tokens whether you use those tools or not. I wanted ten servers available; I didn't want to pay for ten servers in every prompt. This seemed like a solvable inefficiency, and I was curious enough to dig in.
Droid Mode is what emerged: a Skill that provides daemon-backed MCP access with progressive disclosure. Built on 28 years of Linux system administration experience, it treats MCP servers as infrastructure - persistent connections, lazy schema loading, code-mode execution outside the context window. The result is zero token overhead and 13% better latency than native MCP. Per-project daemon isolation handles governance, and everything is traced for accountability. It's experimental research software, but I've tested it thoroughly and use it daily. I'm sharing it as an early AI explorer who believes we're just scratching the surface of what performant agent architectures can look like.
- GitMaxd
The Problem
When you configure MCP servers, all tool definitions are loaded into your context window at startup (source). This is standard behavior for MCP clients - the model needs to "see" available tools to use them. The result is a compounding cost:
| Configuration | Token Overhead | Impact | |---------------|----------------|--------| | 1 server (22 tools) | ~2,400 tokens | Acceptable | | 3 servers (~60 tools) | ~7,200 tokens | Noticeable | | 5 servers (~100 tools) | ~12,000 tokens | Significant |
Those tokens are consumed before your agent writes a single line. On an 8K context window, a single server claims ~30% of your budget; five servers would exceed it entirely. Even on 200K windows, ~12,000 tokens represents ~6% overhead, and it compounds across every message in a conversation.
The Solution
Droid Mode introduces progressive disclosure for MCP tools:
- Discover: List available servers (
dm servers) - ~10 tokens - Index: Browse tool names and descriptions (
dm index) - ~50 tokens - Hydrate: Load full schemas only when needed (
dm hydrate) - zero tokens - Execute: Run tools via daemon or workflow (
dm call,dm run) - zero tokens
Code-Mode Execution: Steps 3-4 run as shell commands, not LLM tool calls. The LLM never sees the schemas or results unless you explicitly include them. This is why token cost is zero: operations happen outside the context window entirely.
Key Insight: Servers with
disabled: trueinmcp.jsonare fully accessible to Droid Mode. Thedisabledflag tells Factory.ai Droid "don't inject these tools into context", but Droid Mode connects directly, bypassing context injection entirely.
Token Efficiency
| Scenario | Native MCP | Droid Mode | Savings | |----------|------------|------------|---------| | 3 tools used | ~330 tokens | 0 tokens | 100% | | 10 tools | ~1,100 tokens | 0 tokens | 100% | | 22 tools (full server) | ~2,400 tokens | 0 tokens | 100% | | 5 servers (~100 tools) | ~12,000 tokens | 0 tokens | 100% |
Tools are loaded only when called. Your context window stays clean.
Architecture
Droid Mode uses a daemon architecture to maintain persistent MCP connections, eliminating the overhead of spawning new processes for each tool call.
flowchart LR
A[AI Agent] --> B[dm CLI]
B --> C[Unix Socket]
C --> D[Droid Mode Daemon]
D --> E[Connection Pool]
E --> F1[MCP Server 1]
E --> F2[MCP Server 2]
E --> F3[MCP Server N]How the Daemon Works
| Component | Purpose |
|-----------|---------|
| Per-Project Sockets | Each project gets its own daemon at ~/.factory/run/dm-daemon-<hash>.sock |
| Connection Pool | Lazy-initialized clients with automatic lifecycle management |
| Auto-Warm | Pre-connects frequently used servers on daemon start |
| Idle Pruning | Closes unused connections after 10 minutes (configurable) |
The daemon starts automatically on first dm call. Without it, each call spawns a fresh MCP process (~2.5s average). With the daemon, calls reuse pooled connections (~680ms average).
Multi-Project Isolation
When working across multiple projects, each project automatically gets its own daemon:
# Project A - starts daemon bound to Project A's config
cd ~/projects/frontend && dm daemon start
# Socket: ~/.factory/run/dm-daemon-a1b2c3d4.sock
# Project B - starts separate daemon with Project B's config
cd ~/projects/backend && dm daemon start
# Socket: ~/.factory/run/dm-daemon-e5f6g7h8.sock
# List all running daemons
dm daemon status --allThis prevents configuration bleed between projects and ensures governance compliance when projects have different MCP server access policies.
Performance Benchmarks
Independent benchmarks comparing Droid Mode against native MCP (direct stdio).
| Configuration | Per-Tool Latency | 10-Tool Total | vs. Native | |---------------|------------------|---------------|------------| | Droid Mode (Daemon) | 678ms | 6.8s | 13% faster | | Native MCP | 777ms | 7.8s | baseline | | Droid Mode (No Daemon) | 2,488ms | 24.9s | 220% slower |
- Hardware: macOS Darwin 25.2.0
- MCP Server: Context Repo MCP (
context-repo-mcp) - Protocol: MCP 2025-06-18
- Runs: 5 iterations averaged
- Date: January 2026
Single-tool breakdown (5 runs):
| Run | No Daemon | Daemon | Native MCP | |-----|-----------|--------|------------| | 1 | 2,948ms | 845ms | 866ms | | 2 | 2,442ms | 610ms | 789ms | | 3 | 2,300ms | 613ms | 798ms | | 4 | 2,415ms | 706ms | 742ms | | 5 | 2,334ms | 617ms | 690ms | | Avg | 2,488ms | 678ms | 777ms |
Key finding: The daemon maintains persistent connections, beating native MCP by ~13% while eliminating all schema overhead from your context window.
Quick Start
# 1. Initialize Droid Mode in your project
npx droid-mode init✓ Initialized successfully 12 files created
QUICK START
1. Discover MCP servers dm servers
2. Hydrate a tool dm hydrate <tool> --server <name>
3. Call the tool dm call <tool> --server <name># 2. Discover available MCP servers
dm serversMCP Servers (from ~/.factory/mcp.json)
┌─────────────────┬───────┬──────────────────┐
│ Name │ Type │ Status │
├─────────────────┼───────┼──────────────────┤
│ context-repo │ stdio │ disabled (good!) │
│ convex │ stdio │ disabled (good!) │
└─────────────────┴───────┴──────────────────┘# 3. Call a tool
dm call list_collections --server context-repo{
"collections": [
{ "id": "docs", "name": "Documentation", "count": 42 },
{ "id": "code", "name": "Code Samples", "count": 18 }
]
}The daemon starts automatically on first call. For manual control:
dm daemon start # Start daemon
dm daemon status # Check connections
dm daemon stop # Stop daemonProgressive Disclosure Model
| Level | Command | What You Get | Token Cost |
|-------|---------|--------------|------------|
| 1 | dm servers | List of configured MCP servers | ~10 |
| 2 | dm index --server X | Tool names, descriptions, required params | ~50 |
| 3 | dm search "query" --server X | Filtered tools matching keyword | ~20 |
| 4 | dm hydrate tool --server X | Full JSON schema + TypeScript types | on-demand |
| 5 | dm call tool --server X | Execute tool directly (primary) | 0 |
| 6 | dm run --workflow file.js --server X | Multi-tool workflow (advanced) | 0 |
Command Reference
Discovery
| Command | Description |
|---------|-------------|
| dm servers | List all MCP servers from mcp.json |
| dm index --server <name> | List tools with required parameters |
| dm search "<query>" --server <name> | Search tools by keyword |
| dm hydrate <tools...> --server <name> | Get full schemas + generate TypeScript types |
Execution
| Command | Description |
|---------|-------------|
| dm call <tool> --server <name> | Primary: Call a single tool (interactive use) |
| dm run --workflow <file> --tools <a,b> --server <name> | Advanced: Multi-tool workflow automation |
Daemon
| Command | Description |
|---------|-------------|
| dm daemon start | Start background daemon for current project |
| dm daemon stop | Stop current project's daemon |
| dm daemon status | Show current project's daemon status |
| dm daemon status --all | List all running daemons across projects |
| dm daemon list | Alias for status --all |
| dm daemon warm [server] | Pre-warm server connection(s) |
| dm call ... --no-daemon | Bypass daemon for single call |
Diagnostics
| Command | Description |
|---------|-------------|
| dm doctor --server <name> | Diagnose connection issues |
| dm config <server> <key> <value> | Configure server settings |
Direct Tool Calls (Primary)
For most use cases, use dm call to execute tools directly:
# Hydrate once (caches schema)
dm hydrate list_collections --server context-repo
# Call directly - results returned as JSON
dm call list_collections --server context-repo
dm call list_collections --server context-repo --args '{"limit": 5}'No workflow file needed. This is the preferred method for interactive use and single-tool operations.
Workflows (Advanced)
Note: Workflows are for multi-tool orchestration with loops, conditionals, or pre-programmed patterns. For simple single-tool calls, use
dm callinstead.
Workflows let you run procedural logic across multiple MCP tools in a sandboxed environment.
// my-workflow.js
workflow = async () => {
const collections = await t.listCollections({})
log("Found", collections.length, "collections")
for (const col of collections.slice(0, 3)) {
const docs = await t.listDocuments({ collection: col.id })
log(` ${col.name}: ${docs.length} documents`)
}
return { success: true, count: collections.length }
}dm run --server context-repo \
--tools list_collections,list_documents \
--workflow my-workflow.jsFound 5 collections
Documentation: 42 documents
Code Samples: 18 documents
Architecture: 7 documents
Workflow completed in 1.2s
Result: { success: true, count: 5 }
Trace: .factory/droid-mode/runs/context-repo/20260103T142531/run.jsonSandbox Security
Workflows execute in a restricted VM context:
- Blocked:
require,import,fetch,process,eval - Allowed:
t.*(tool calls),log(),sleep(),assert() - Traced: Every tool call is logged with timing and result hash
Configuration
Recommended mcp.json Setup
{
"mcpServers": {
"context-repo": {
"type": "stdio",
"command": "npx",
"args": ["-y", "context-repo-mcp"],
"disabled": true
},
"another-server": {
"type": "stdio",
"command": "node",
"args": ["./path/to/server.js"],
"disabled": true
}
}
}Set
"disabled": truefor all MCP servers you want to access via Droid Mode. They remain fully functional, just not injected into your context window.
Configuration Locations
- Project:
.factory/mcp.json - User:
~/.factory/mcp.json(user config takes precedence)
For more information on Factory.ai Droid and MCP configuration, see the Factory.ai documentation.
Artifacts
All outputs are written to .factory/droid-mode/:
| Path | Contents |
|------|----------|
| cache/<server>/tools.json | Cached tool inventory |
| hydrated/<server>/<timestamp>/schemas.json | Full JSON schemas |
| hydrated/<server>/<timestamp>/types.d.ts | Generated TypeScript types |
| runs/<server>/<timestamp>/run.json | Workflow execution trace |
Requirements & Limitations
Requirements
- Node.js >= 18
- Factory.ai Droid CLI
- MCP servers configured in
~/.factory/mcp.jsonor.factory/mcp.json
Current Limitations
- Windows: Daemon mode uses Unix sockets (
/tmp/dm-daemon.sock). Windows support is not yet implemented. - HTTP Transport: Exists in code but documentation pending.
- Hooks: PreToolUse hooks exist in
examples/hooks/but are not yet documented.
Recommended: AGENTS.md Snippet
For best results with AI agents, add this snippet to your project's AGENTS.md file (suggested location: after your Build & Test section):
## Droid Mode (MCP Tool Access)
Access MCP tools without context bloat. Hydrate schemas on-demand, call tools directly.
### Commands
```bash
# 1. Discover
dm servers
dm index --server <name>
# 2. Prepare (one-time per tool)
dm hydrate <tool> --server <name>
# 3. Execute (primary - direct call)
dm call <tool> --server <name>
dm call <tool> --server <name> --args '{"key": "value"}'
# 4. Advanced: Multi-tool workflows (for complex automation only)
dm run --server <name> --tools a,b --workflow file.jsUse dm call for everyday tool calls. Use dm run only for multi-tool orchestration.
Path Resolution
Try workspace first: ./.factory/skills/droid-mode/bin/dm
Fallback to personal: ~/.factory/skills/droid-mode/bin/dm
Gotchas
- Use
dm callfor single tools - Don't create workflow files for simple calls - Keep commands minimal - No timing, progress bars, or diagnostics unless requested
- macOS compatibility - Avoid GNU-specific flags (
date +%s%3N,sed -i,grep -P) - Disabled servers work -
"disabled": trueprevents context bloat but servers remain accessible
</details>
This helps AI agents use droid-mode correctly by documenting naming conventions, sandbox restrictions, and common pitfalls.
---
## Experimental Status
> **⚠️ This is experimental software (v0.0.x)**
>
> Droid Mode is under active development to improve MCP usability in Factory.ai Droid. The API may change between versions.
>
> **Feedback welcome!** Open an issue on GitHub or reach out on X.
---
## Design Philosophy
> Treat MCP as infrastructure, Skills as capability boundaries, and code as a reasoning amplifier, not as authority.
Inspired by [Cloudflare's Code Mode](https://blog.cloudflare.com/code-mode/) concept, adapted for Factory.ai's Skill architecture.
---
## License
MIT
## Author
[GitMaxd](https://github.com/Gitmaxd) · [@gitmaxd](https://x.com/gitmaxd)
---
<div align="center">
**[GitHub](https://github.com/Gitmaxd/droid-mode)** · **[npm](https://www.npmjs.com/package/droid-mode)** · **[Issues](https://github.com/Gitmaxd/droid-mode/issues)**
</div>