Universal Tool Calling Protocol (UTCP) Code-Mode

repository·main·Indexed 23 days ago

https://github.com/universal-tool-calling-protocol/code-mode

A library and CLI that transforms AI agents from JSON-based tool callers into code executors. It enables agents to write and execute TypeScript code to orchestrate complex workflows across MCP, HTTP, and CLI tool ecosystems in a single request. Includes @utcp/code-mode-cli for shell agents and @utcp/code-mode-mcp for MCP-only clients like Claude Desktop, supporting sandboxed execution, tool discovery, and interactive OAuth logins via .utcp_config.json.

Tokens
12.1K
Snippets
31
Records
57
Agent score
80%

What's inside UTCP Code-Mode

  1. Handle Interactive OAuth with oauth2_user

    main

    To support tools requiring interactive sign-in, declare an oauth2_user auth block in your configuration. UTCP does not run the OAuth flow itself; instead, it provides the interface for the user to complete it.

    Remote MCP Servers (e.g., Notion)

    Use a minimal block where endpoints are auto-discovered via the MCP SDK. The token is injected using a variable placeholder:

    "auth": { "auth_type": "oauth2_user", "access_token": "${NOTION_TOKEN}" }

    To complete the login, run utcp login notion. This prints a sign-in URL. After authorizing, provide the redirect URL back via the --code flag.

    HTTP APIs with Device-Code Flow

    Declare the following keys in your auth block:

    • device_authorization_endpoint
    • token_endpoint
    • client_id
    • scope

    Running utcp login <manual> will print a URL and a code, then poll until the user completes the flow.

    Note: Tokens are written to the dotenv file (default .env), keyed by <manual>_<VAR>. You must include a dotenv loader block in your config to use these secrets.

  2. Security and Performance Characteristics

    main

    Code Mode is designed with the following constraints:

    Security:

    • Node.js VM sandboxing: Execution occurs in an isolated context.
    • No filesystem access: Tools are only accessible through explicitly registered servers.
    • Timeout protection: Configurable limits prevent resource exhaustion.
    • Zero network access: No external dependencies or API keys are exposed directly from the sandbox.

    Performance:

    • Minimal memory footprint: Uses lightweight VM contexts.
    • Efficient tool caching: TypeScript interfaces are cached automatically.
    • Streaming console output: Real-time log capture without buffering.
  3. The runtime context inside callToolChain

    main

    When executing code via callToolChain, the following variables and globals are available in the sandbox:

    VariableDescription
    __interfacesA string containing all TypeScript interface definitions
    __getToolInterface(name)A function to retrieve the interface for a specific tool
    __availableToolsAn array of available tool access patterns
    console.log/error/warnStandard console methods (output is captured and returned in consoleOutput)
    Standard JS globalsJSON, Math, Date, Array, etc.

    Note: The sandbox is isolated via isolated-vm and has no access to Node.js APIs, the file system, or the network.

  4. Quickstart: Use Code Mode in 3 lines

    main

    You can initialize a client, register tools, and execute a TypeScript tool chain in just three steps. This allows AI agents to execute complex workflows in a single request instead of multiple tool calls.

    import { CodeModeUtcpClient } from '@utcp/code-mode';
    
    const client = await CodeModeUtcpClient.create();                    // 1. Initialize
    await client.registerManual({ name: 'github', /* MCP config */ });  // 2. Add tools  
    const { result } = await client.callToolChain(`/* TypeScript */`);   // 3. Execute code
  5. Quickstart: Using @utcp/code-mode-cli with an Agent

    main

    The @utcp/code-mode-cli is designed to be used by an LLM agent rather than a human. To get started, provide an agent with an API description (a UTCP call template, an OpenAPI/Swagger spec URL/file, or a plain-English description) and instruct it to run the following command:

    npx -y @utcp/code-mode-cli prompt

    This command outputs a full self-configuration and usage guide. The agent should read this output, use it to write a .utcp_config.json file, and then proceed to use the CLI commands to interact with the API.

  6. Quick Start: Execute TypeScript with Tool Access

    main

    You can execute TypeScript code that has direct access to UTCP tools by registering tool manuals and implementations using addFunctionToUtcpDirectCall, creating a CodeModeUtcpClient, and then calling callToolChain.

    import { CodeModeUtcpClient } from '@utcp/code-mode';
    import { addFunctionToUtcpDirectCall } from '@utcp/direct-call';
    
    // 1. Register a function that returns a UTCP manual (describes the tool schema)
    addFunctionToUtcpDirectCall('getWeatherManual', async () => ({
      utcp_version: '0.2.0',
      tools: [{
        name: 'get_current',
        description: 'Get current weather for a city',
        inputs: {
          type: 'object',
          properties: { city: { type: 'string' } },
          required: ['city']
        },
        tool_call_template: {
          call_template_type: 'direct-call',
          callable_name: 'getWeather'
        }
      }]
    }));
    
    // 2. Register the actual tool implementation
    addFunctionToUtcpDirectCall('getWeather', async (city: string) => ({
      city,
      temperature: 22,
      condition: 'sunny'
    }));
    
    // 3. Create client and register manual
    const client = await CodeModeUtcpClient.create();
    await client.registerManual({
      name: 'weather',
      call_template_type: 'direct-call',
      callable_name: 'getWeatherManual'
    });
    
    // 4. Execute code with tool access
    const { result, logs } = await client.callToolChain(`
      const data = weather.get_current({ city: 'London' });
      console.log('Weather:', data);
      return data;
    `);
    
    console.log(result);
    // { city: 'London', temperature: 22, condition: 'sunny' }
    import { CodeModeUtcpClient } from '@utcp/code-mode';
    import { addFunctionToUtcpDirectCall } from '@utcp/direct-call';
    
    // Register a function that returns a UTCP manual
    addFunctionToUtcpDirectCall('getWeatherManual', async () => ({
      utcp_version: '0.2.0',
      tools: [{
        name: 'get_current',
        description: 'Get current weather for a city',
        inputs: {
          type: 'object',
          properties: { city: { type: 'string' } },
          required: ['city']
        },
        tool_call_template: {
          call_template_type: 'direct-call',
          callable_name: 'getWeather'
        }
      }]
    }));
    
    // Register the actual tool implementation
    addFunctionToUtcpDirectCall('getWeather', async (city: string) => ({
      city,
      temperature: 22,
      condition: 'sunny'
    }));
    
    // Create client and register manual
    const client = await CodeModeUtcpClient.create();
    await client.registerManual({
      name: 'weather',
      call_template_type: 'direct-call',
      callable_name: 'getWeatherManual'
    });
    
    // Execute code with tool access
    const { result, logs } = await client.callToolChain(`
      const data = weather.get_current({ city: 'London' });
      console.log('Weather:', data);
      return data;
    `);
    
    console.log(result);
    // { city: 'London', temperature: 22, condition: 'sunny' }
  7. Local development against the bridge

    main

    If you are developing the @utcp/code-mode library and want to test it via Claude Code, use the following workflow in the code-mode-mcp directory:

    1. Setup:

      npm install
      npm run dev:register

      This builds the library, overlays it into the bridge's node_modules, and registers it as utcp-codemode-dev in Claude Code.

    2. Iterate: After every edit, run:

      npm run dev:register

      (Note: You may need to restart Claude Code to see changes).

    3. Cleanup:

      npm run dev:unregister

    Dev Flags:

    • --name <mcp-name>: Set a custom name (defaults to utcp-codemode-dev).
    • --config <path>: Point to a specific UTCP config (defaults to ./.utcp_config.json).
    cd code-mode-mcp
    npm install
    npm run dev:register
  8. Configure Code Mode as an MCP Server

    main

    For MCP-only clients like Claude Desktop, use the @utcp/code-mode-mcp package. This allows the client to use call_tool_chain as an MCP tool. You can configure it in your MCP settings file.

    {
      "mcpServers": {
        "code-mode": {
          "command": "npx",
          "args": ["@utcp/code-mode-mcp"],
          "env": {
            "UTCP_CONFIG_FILE": "/path/to/your/.utcp_config.json"
          }
        }
      }
    }
  9. Configure utcp via .utcp_config.json

    main

    The utcp CLI does not use environment variables for configuration. Instead, create a .utcp_config.json file in your working directory to define how to load variables and how to call external APIs, MCP servers, or CLIs.

    Supported call_template_type transports include:

    • http (including OpenAPI URLs)
    • mcp (remote or local MCP servers)
    • cli
    • text
    • file
    {
      "load_variables_from": [
        { "variable_loader_type": "dotenv", "env_file_path": ".env" }
      ],
      "manual_call_templates": [
        {
          "name": "openlibrary",
          "call_template_type": "http",
          "http_method": "GET",
          "url": "https://openlibrary.org/static/openapi.json",
          "content_type": "application/json"
        }
      ]
    }
  10. Integrate Code Mode with AI Agents

    main

    To use Code Mode with an AI framework (like OpenAI or Anthropic), include the CodeModeUtcpClient.AGENT_PROMPT_TEMPLATE in your system prompt. This template instructs the agent on the correct workflow for tool discovery and execution:

    1. Discovery: Use searchTools(query) to find relevant tools.
    2. Introspection: Use __interfaces and __getToolInterface() to understand tool schemas.
    3. Execution: Use callToolChain(code) to run the logic.
    4. Syntax: Use the manual.tool() hierarchical access pattern.
    import { CodeModeUtcpClient } from '@utcp/code-mode';
    
    const systemPrompt = `
    You are an AI assistant with access to tools via UTCP CodeMode.
    ${CodeModeUtcpClient.AGENT_PROMPT_TEMPLATE}
    Additional instructions...
    `;
    
    // Works with any AI library
    const response = await openai.chat.completions.create({
      model: 'gpt-4',
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: 'Analyze the latest PR in microsoft/vscode' }
      ]
    });