Universal Tool Calling Protocol (UTCP) Python Client

repository·main·Indexed 20 days ago

https://github.com/universal-tool-calling-protocol/python-utcp

A Python client library (v1.1.3) for the Universal Tool Calling Protocol (UTCP), a standard for defining and interacting with tools across various communication protocols. It includes support for multiple plugins: CLI for executing command sequences with state preservation, File for returning local file contents, GraphQL for schema-based tool calls, and HTTP for REST API integration, including SSE and streaming capabilities.

Tokens
50.4K
Snippets
151
Records
205
Agent score
69%

What's inside utcp

  1. How the UTCP CLI Plugin works

    main

    The UTCP CLI plugin allows you to define a sequence of commands that execute within a single subprocess. This ensures that state, such as directory changes (cd) and environment variables, persists across the entire command chain.

    Key features include:

    • State Preservation: Changes to the working directory or environment persist between commands in the sequence.
    • Argument Substitution: Use the UTCP_ARG_argname_UTCP_END placeholder to inject tool arguments into commands.
    • Output Referencing: Access the output of any previous command in the sequence using $CMD_N_OUTPUT (where N is the zero-based index of the command).
    • Output Control: Use append_to_final_output: false on specific commands to prevent their output from being included in the final tool result returned to the client.
    • Cross-Platform Support: Automatically generates appropriate scripts (PowerShell for Windows, Bash for Unix/Linux/macOS).
    from utcp.utcp_client import UtcpClient
    
    # Example of a multi-step tool definition
    client = await UtcpClient.create(config={
        "manual_call_templates": [{
            "name": "file_analysis",
            "call_template_type": "cli",
            "commands": [
                {
                    "command": "cd UTCP_ARG_target_dir_UTCP_END",
                    "append_to_final_output": false
                },
                {
                    "command": "find . -type f -name '*.py' | wc -l"
                }
            ]
        }]
    })
    
    result = await client.call_tool("file_analysis.count_python_files", {"target_dir": "/project"})
  2. Use Server-Sent Events (SSE) and Streaming HTTP

    main

    The plugin supports real-time event streaming and large data handling:

    • SSE: Set call_template_type to "sse". You can specify an event_type and set reconnect: true for resilience.
    • Streaming HTTP: Set call_template_type to "streamable_http". Use chunk_size to manage large response downloads.
    // SSE Example
    {
      "name": "event_stream",
      "call_template_type": "sse",
      "url": "https://api.example.com/events",
      "event_type": "message",
      "reconnect": true
    }
    
    // Streaming HTTP Example
    {
      "name": "large_data",
      "call_template_type": "streamable_http",
      "url": "https://api.example.com/download",
      "chunk_size": 8192
    }
  3. How the UTCP In-Memory Embeddings Search Plugin works

    main

    The plugin registers itself with the UTCP 1.0 core via the utcp.plugins entry point, allowing for automatic discovery and registration of the in_mem_embeddings strategy.

    Semantic Search vs. Fallback

    • With sentence-transformers and torch: The plugin uses vector embeddings to capture semantic meaning, allowing the search to find conceptually similar tools even without exact keyword matches. It uses the all-MiniLM-L6-v2 model by default.
    • Without dependencies: The plugin automatically falls back to a simpler character frequency-based text similarity method, which has reduced accuracy.

    Performance

    Embeddings are cached in memory to improve performance during repeated searches.

  4. How the UTCP Text Plugin works

    main

    The Text plugin facilitates tool discovery and execution using string-based content:

    1. Tool Discovery (register_manual): The plugin parses the content field of a template as either a UTCP manual or an OpenAPI specification. This allows the UtcpClient to identify available tools.
    2. Tool Execution (call_tool): When a tool is invoked, the plugin returns the content field directly.

    Note: If you need to load tool definitions from the file system, use the utcp-file plugin instead.

  5. Handle WebSocket responses

    main

    You can control how the plugin processes incoming messages from the WebSocket server using the response_format field in your configuration:

    • Default (No specification): Returns the raw response (JSON string, text, or binary) as-is.
    • json: Automatically parses the response as a JSON object.
    • text: Returns the response as a text string.
    • raw: Returns the response without any processing.
    {
        "call_template_type": "websocket",
        "url": "wss://api.example.com/ws",
        "response_format": "json"
    }
  6. How the UTCP File Plugin works

    main

    The File plugin operates via two primary mechanisms:

    1. Tool Discovery (register_manual): The plugin reads a UTCP manual file (JSON or YAML) to discover available tools. This allows the UtcpClient to know which tools exist and how to call them.
    2. Tool Execution (call_tool): When a tool is invoked, the plugin checks its tool_call_template. If the call_template_type is set to file, the plugin reads and returns the entire content of the file_path specified in that template.

    Note: The call_tool function ignores any arguments passed to it; it simply returns the full content of the file defined in the tool's template.

  7. Reference previous command outputs

    main

    You can reference the output of any previous command in your sequence using the syntax $CMD_N_OUTPUT, where N is the index of the command (starting at 0). This is useful for passing data between steps, such as a file path or a status string.

    {
      "name": "conditional_processor",
      "call_template_type": "cli",
      "commands": [
        {
          "command": "git status --porcelain",
          "append_to_final_output": false
        },
        {
          "command": "echo \"Changes detected: $CMD_0_OUTPUT\"",
          "append_to_final_output": true
        }
      ]
    }
  8. How WebSocket message templating works

    main

    The WebSocket plugin uses a flexible templating system to allow communication with any existing WebSocket endpoint. You can define how arguments are mapped to the outgoing message using UTCP_ARG_arg_name_UTCP_ARG placeholders.

    There are three primary modes:

    1. No Template (Default): If the message field is omitted, the plugin sends the tool arguments directly as a JSON object. This is ideal for endpoints that already accept standard JSON.
    2. Dict Template: Use a dictionary to create structured messages (e.g., JSON-RPC or custom nested JSON). Templates work recursively in dicts and lists.
    3. String Template: Use a string for text-based or delimited protocols (e.g., IoT commands or custom text formats).

    Example of a Dict Template: If you define a template with "action": "UTCP_ARG_action_UTCP_ARG" and call the tool with {"action": "search"}, the plugin will inject search into the placeholder.

    {
        "message": {
            "type": "request",
            "action": "UTCP_ARG_action_UTCP_ARG",
            "params": {
                "user_id": "UTCP_ARG_user_id_UTCP_ARG"
            }
        }
    }
  9. Configure protocol restrictions for manuals

    main

    UTCP uses the allowed_communication_protocols field to implement fine-grained security control. This prevents a manual from accidentally or maliciously escalating to unauthorized protocols (e.g., an HTTP manual attempting to execute CLI tools).

    Default Behavior

    If allowed_communication_protocols is not set or is empty, a manual is restricted to tools that use the same protocol type as the manual itself.

    Explicitly Allowing Multiple Protocols

    To allow a manual to interact with tools from different protocols, you must explicitly list them in the allowed_communication_protocols array.

    Security Enforcement

    1. Registration Filtering: During register_manual(), tools with unauthorized protocols are filtered out and a WARNING is issued.
    2. Call-Time Validation: If an unauthorized tool is called, the client will raise a ValueError.

    Configuration Summary

    allowed_communication_protocolsManual TypeAllowed Tool Protocols
    Not set / null"http"Only "http"
    [] (empty)"http"Only "http"
    ["http", "cli"]"http""http" and "cli"
    ["http", "cli", "mcp"]"cli""http", "cli", and "mcp"
    from utcp_http.http_call_template import HttpCallTemplate
    
    # This manual can ONLY register/call HTTP tools (default restriction)
    http_manual = HttpCallTemplate(
        name="my_api",
        call_template_type="http",
        url="https://api.example.com/utcp"
        # allowed_communication_protocols not set → defaults to ["http"]
    )
    
    # This manual can register/call both HTTP and CLI tools
    multi_protocol_manual = HttpCallTemplate(
        name="flexible_manual",
        call_template_type="http",
        url="https://api.example.com/utcp",
        allowed_communication_protocols=["http", "cli"]  # Explicitly allow both
    )
  10. Configure a CLI tool template

    main

    To define a CLI tool, add a template to the manual_call_templates list in your UtcpClient configuration.

    Set call_template_type to "cli". The template object supports:

    • name: The unique identifier for the tool.
    • commands: An array of command objects. Each object contains:
      • command: The actual shell command string.
      • append_to_final_output: (Optional) Boolean indicating if this command's output should be part of the final result.
    • working_dir: (Optional) The directory where the commands should start.
    • env_vars: (Optional) A dictionary of environment variables to pass to the subprocess.
    {
      "name": "python_pipeline",
      "call_template_type": "cli",
      "commands": [
        {
          "command": "python setup.py install",
          "append_to_final_output": false
        },
        {
          "command": "python script.py --input UTCP_ARG_input_file_UTCP_END --result \"$CMD_0_OUTPUT\"",
          "append_to_final_output": true
        }
      ],
      "env_vars": {
        "PYTHONPATH": "/custom/path",
        "API_KEY": "${API_KEY}"
      }
    }