Skip to content

Progressive Code-Mode MCP integration for Factory.ai Droid

License

Notifications You must be signed in to change notification settings

Gitmaxd/droid-mode

Repository files navigation

Droid Mode

Progressive MCP for AI Agents. Zero Context Bloat.

npm version License: MIT Node.js Status

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:

  1. Discover: List available servers (dm servers) - ~10 tokens
  2. Index: Browse tool names and descriptions (dm index) - ~50 tokens
  3. Hydrate: Load full schemas only when needed (dm hydrate) - zero tokens
  4. 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: true in mcp.json are fully accessible to Droid Mode. The disabled flag 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]
Loading

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 --all

This 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
Methodology
  • 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 servers
MCP 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 daemon

Progressive 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 call instead.

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.js
Found 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.json

Sandbox 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": true for 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.json or .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):

Click to expand AGENTS.md snippet
## 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.js

Use 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 call for 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": true prevents 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>

About

Progressive Code-Mode MCP integration for Factory.ai Droid

Resources

License

Stars

Watchers

Forks

Packages

No packages published