Loading
Personal Project
Claude Versatile

Claude Versatile is a multi-model AI orchestration framework that lets Claude Code act as the primary controller while delegating tasks to external AI models (such as OpenAI Codex or Grok) via the Model Context Protocol (MCP). The framework routes specialized tasks like code review or search to secondary models without losing the unified command-line workflow.

Codex delegation demo

0:00
/0:00
User delegating a code analysis task to OpenAI Codex through Claude Code, where Claude assembles context, routes the request via MCP, and presents the result

Two-layer architecture

The framework splits work across two execution layers based on task complexity. Layer 1 handles single-shot API calls for code review, web search, or generation. Layer 2 runs an autonomous agent in a worker process with its own reasoning loop for multi-step inspection tasks.

flowchart TD
    subgraph Claude["Claude Code (Orchestrator)"]
        CC[Claude Code CLI]
        SK[Skills Layer]
    end

    subgraph L1["Layer 1: Direct API Calls"]
        MCP1[codex MCP Server]
        MCP2[grok MCP Server]
        MCP3[future providers...]
    end

    subgraph L2["Layer 2: Agent Delegation"]
        AMCP[agent MCP Server]
        subgraph Worker["Agent Worker Process"]
            P[Planner]
            CM[Context Manager]
            subgraph TR["ToolRegistry (Plugin)"]
                FS[filesystem/]
                CR[core/]
                GK[grok/]
                CX[codex/]
            end
        end
    end

    subgraph Models["External AI Models"]
        GPT[OpenAI GPT-5.4]
        GRK[Grok-4]
        GMN[Gemini...]
    end

    CC --> SK
    SK --> MCP1
    SK --> MCP2
    SK --> MCP3
    CC --> AMCP
    AMCP --> Worker
    MCP1 --> GPT
    MCP2 --> GRK
    MCP3 --> GMN
    P --> CM
    CM --> TR
    GK --> GRK
    Worker --> GPT
    Worker --> GRK

External models run in read-only mode: they cannot edit files, execute shell commands, or run git operations. Suggestions return as text for Claude Code to review, apply, or undo using its built-in checkpoints.

Declarative provider framework

The framework provides a defineProvider() helper that automates configuration loading, environment variables, client instantiation, and error handling. Providers register MCP tools inside the onRegisterTools hook:

// A complete MCP Server for any OpenAI-compatible API
defineProvider({
    type: "openai",
    name: "claude-versatile-codex",
    version: "0.3.0",
    configFile: "codex.agent.json",
    onRegisterTools(server, ctx) {
        server.tool("codex_chat", schema, async (params) => {
            const result = await ctx.complete({
                model: params.model,
                messages: [{ role: "user", content: params.prompt }],
            });
            return { content: [{ type: "text", text: result.content }] };
        });
    },
});

The lifecycle supports "openai" for standard REST endpoints and "native" for custom client SDKs, flowing through four steps:

flowchart LR
    A["onLoadConfig"] --> B["onCreateClient"]
    B --> C["onRegisterTools"]
    C --> D["onServerReady"]

For OpenAI-compatible providers, ctx.complete() encapsulates message assembly, request dispatch, token logging, and error mapping in one call.

Request flow

When Claude delegates a task, the MCP server initializes its client lazily upon the first tool call (allowing servers to boot before keys are configured), formats the output into a CompletionResult, and returns text with token usage details.

sequenceDiagram
    participant U as User
    participant C as Claude Code
    participant M as MCP Server
    participant P as Provider API

    U->>C: "Use codex to review this function"
    C->>C: Select tool: codex_chat
    C->>M: MCP tool call (prompt, model, params)
    M->>M: Load config from .versatile/
    M->>M: Initialize client (lazy)
    M->>P: chat.completions.create()
    P-->>M: CompletionResult (content, usage)
    M-->>C: MCP response (text + usage footer)
    C->>C: Review result, decide next action
    C-->>U: Present findings

Data-driven model routing

The MODEL_ROUTES table maps model name prefixes to config files, environment variables, and protocol flags (such as supportsFunctionCalling). The agent’s collectEnv() uses this table to load provider credentials, while createProviderFromEnv() instantiates the corresponding CompletionProvider at runtime.

graph LR
    M[model name] --> R{MODEL_ROUTES}
    R -->|"grok-*"| G["grok.agent.json (FC: false)"]
    R -->|"default"| O["codex.agent.json (FC: true)"]
    G --> P[CompletionProvider]
    O --> P
    P --> Planner

Passing a different model parameter swaps providers dynamically without code changes or restarts. The planner targets the CompletionProvider interface rather than a concrete vendor SDK.

Autonomous agent and iteration control

The agent executes inside a child process with a ReAct loop (Think, Act, Observe, Decide) to read files, search patterns, and return findings. Process isolation ensures worker crashes do not terminate the parent MCP server.

ReAct reasoning loop

The MCP server spawns a worker process over IPC. The worker’s planner communicates with the model using either OpenAI function calling (tools parameter) or prompt-based XML tags (<thought> and <action>), selected by the route’s supportsFunctionCalling flag.

sequenceDiagram
    participant C as Claude Code
    participant M as MCP Server
    participant W as Worker Process
    participant LLM as External LLM

    C->>M: agent_execute(goal, model, autoMode)
    M->>W: fork() + IPC start

    opt autoMode enabled
        W->>LLM: Prompt with goal + plan tool
        LLM-->>W: tool_calls: plan(estimated_steps)
        W->>W: Set effectiveMax = ceil(estimated * 1.5)
    end

    loop ReAct Loop
        W->>LLM: Goal + context + tool definitions
        LLM-->>W: tool_calls: read_file / search_pattern / ...
        W->>W: Execute read-only tools
        W->>W: Update context (sliding window + summary)
        W->>W: Check: iterations, tokens, repetition
        W->>M: IPC status update

        alt LLM calls done tool
            W->>M: IPC complete(result)
        end
    end

    M-->>C: Formatted result (summary, files, tokens)

The agent provides read-only tools: read_file, list_dir, search_pattern, web_search (when Grok credentials exist), plan (in autoMode), and done. File paths are verified with resolveSafePath() to block directory traversal. The context manager applies a sliding window with automatic summarization at 80% context capacity to prevent token limits on large codebases.

L1 complexity estimation

In autoMode, the agent invokes plan on its first turn to estimate necessary steps. The planner sets effectiveMaxIterations = ceil(estimated * 1.5), scaling iteration limits to the problem size.

L2 runtime guards

L2 monitors track execution during each turn:

  • Repetition detection checks the last five tool calls; two identical calls trigger a redirect message, and a second redirect ends the task.
  • Token budgets cap total usage (default 100k tokens), triggering a clean stop when reached.
stateDiagram-v2
    [*] --> Planning: agent_execute(goal)
    Planning --> Executing: plan tool sets effectiveMax
    Executing --> Thinking: ReAct Loop
    Thinking --> Acting: Select tool
    Acting --> Observing: Execute tool
    Observing --> Thinking: Continue reasoning

    Thinking --> Done: done tool called
    Thinking --> Terminated: Max iterations reached
    Observing --> Terminated: Token budget exceeded
    Acting --> Redirected: Repetition detected
    Redirected --> Thinking: Inject redirect hint
    Redirected --> Terminated: 2nd redirect
    Terminated --> [*]
    Done --> [*]

When autoMode is turned off, the agent reverts to a fixed iteration limit and disables the plan tool.

Plugin-based tool architecture

The agent tool system uses a modular directory structure (core/, filesystem/, grok/, codex/), with each domain registering tools through ToolRegistry.

flowchart TD
    subgraph Registry["ToolRegistry"]
        R[register / getEnabled / has]
    end

    subgraph Core["core/"]
        P[plan]
        D[done]
    end

    subgraph FS["filesystem/"]
        RF[read_file]
        LD[list_dir]
        SP[search_pattern]
    end

    subgraph Grok["grok/"]
        WS[web_search]
    end

    subgraph Codex["codex/"]
        FT[future tools...]
    end

    Core --> Registry
    FS --> Registry
    Grok --> Registry
    Codex --> Registry
    Registry --> Worker

The factory createBuiltinRegistry(env) checks environment keys before registering external tools. If GROK_API_KEY is present, web_search is registered. Core and filesystem tools register unconditionally.

Metadata-driven planner

Each tool carries an AgentToolMetadata declaration:

interface AgentToolMetadata {
    category?: "core" | "filesystem" | "external" | "custom";
    tracksFileRead?: boolean;       // Planner tracks 'path' arg in filesRead
    skipRepetitionCheck?: boolean;  // Exempt from L2 repetition detection
    systemPromptHint?: string;      // Injected into system prompt when tool is available
}

The planner uses metadata to decide whether to record file paths, whether to bypass repetition checks, and what instructions to append to the system prompt, keeping tool-specific rules out of the planner loop.

Config-driven tool enablement

The enabledTools array in agent.json specifies which tools the agent exposes to the model:

{
  "enabledTools": ["read_file", "list_dir", "search_pattern", "done", "plan", "web_search"]
}

ToolRegistry.getEnabled(names) filters available tools against this list, allowing per-project customization.

Multi-model tool calling adaptation

When external models lack support for OpenAI’s tools parameter (such as Grok-4 behind certain proxy endpoints), the framework activates an XML prompt fallback:

flowchart TD
    M[Model Request] --> Check{supportsFunctionCalling?}
    Check -->|true| FC[Function Calling Mode]
    Check -->|false| XML[XML Prompt Mode]

    FC --> Tools["tools param + tool_calls response"]
    XML --> Prompt["System prompt describes tools"]
    XML --> Format["LLM outputs <thought> + <action> XML"]
    XML --> Parse["parseLegacyContent extracts tool calls"]

    Tools --> Norm[Normalized CompletionResult]
    Parse --> Norm

When supportsFunctionCalling is false in MODEL_ROUTES:

  1. The tools field is excluded from the API payload
  2. Tool descriptions and <thought>/<action> format guidelines are injected into the prompt
  3. parseLegacyContent extracts tool requests from the model’s text response
  4. ContextManager maps tool history to standard assistant/user messages

Both paths resolve into a uniform CompletionResult object with content, toolCalls, and usage data.

Skill encapsulation and configuration

Skill layer

Skills provide prompt templates and context-gathering routines that sit above MCP tools. The codex-task skill bundles project files and dependency structures before calling codex_chat. The grok-search skill classifies search queries and selects appropriate search prompts before invoking grok_search.

Skills are saved as Markdown and YAML files under .claude/skills/, keeping capability (MCP server) and usage workflow (Skill) distinct.

Configuration and timeout strategy

Configuration files are placed in .versatile/ (one JSON per provider) and resolve through a three-step fallback:

flowchart LR
    A[".versatile/*.json"] -->|not found| B["process.env"]
    B -->|not found| C["Hardcoded default"]

Missing configs are populated with templates on first launch. Placeholder API keys are flagged and fall back to environment variables.

Timeouts run at two levels: singleCallTimeout (default 2 minutes) governs individual API requests with exponential backoff retries, while maxTimeMs (default 5 minutes) limits total agent run time.

Design decisions

Supervised execution

External models operate strictly as read-only tools. Claude Code retains context, inspects generated snippets, and applies file modifications, keeping changes auditable and reversible.

Configuration-driven extensions

Providers, routing tables, and tool metadata are declared in data structures. Adding a model or tool requires configuration entries rather than restructuring the execution loop.

Process isolation

The agent runs in a separate worker process over IPC. Crashes in external tool execution or memory exhaustion remain contained without terminating the MCP server.

Read-only boundaries

External models cannot run shell commands or modify files directly. All write operations remain under local user control through Claude Code.

Progressive complexity

Layer 1 handles straightforward API queries, Layer 2 engages multi-step planning when required, and skills organize context only when needed.