The Complete Guide to OpenClaw TOOLS.md: Organizing Credentials and API Keys

If you’re deploying OpenClaw agents on a Hetzner VPS and finding yourself constantly SSH’ing in to update API keys or wondering how to securely manage credentials for different tools, you’ve likely hit the wall of plain text files and environment variables. The TOOLS.md file in OpenClaw isn’t just for defining tools; it’s a critical, often underutilized, mechanism for organizing and securing your agent’s access to external services. The official documentation hints at its capabilities, but the real power lies in leveraging its structured format with environment variable substitution and a robust directory structure for different deployment scenarios.

Understanding OpenClaw’s Credential Resolution

OpenClaw’s TOOLS.md works by defining tools, their capabilities, and crucially, their authentication mechanisms. While you can hardcode API keys directly into TOOLS.md, this is a terrible practice for security and maintainability. A better approach is to use environment variables. OpenClaw processes TOOLS.md and replaces placeholders like {{ENV_VAR_NAME}} with the actual values from the environment where the OpenClaw agent is running. This allows you to keep sensitive information out of your version-controlled files.

For example, a tool definition for an OpenAI API call might look like this in your TOOLS.md:

# OpenAI Text Generation
  • name: openai_text_generator
  • description: Generates text using OpenAI's models. schema: type: object properties: model: type: string enum: ["gpt-4o", "gpt-3.5-turbo"] description: The model to use. prompt: type: string description: The prompt for text generation. required: ["model", "prompt"] call: | import openai import os client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) response = client.chat.completions.create( model=args["model"], messages=[{"role": "user", "content": args["prompt"]}] ) print(response.choices[0].message.content)

    Notice the os.getenv("OPENAI_API_KEY"). This is the standard Python way to fetch an environment variable, and OpenClaw’s tool execution environment respects it. The key insight here is that OpenClaw executes the call block as a standard Python script. Anything you can do in Python, including fetching environment variables or even reading from secure files, you can do here.

    Organizing Credentials with .env Files and Systemd

    For a single agent, managing environment variables via a .env file is straightforward. You can place a file named .env in your OpenClaw project root:

    # .env
    OPENAI_API_KEY="sk-your-openai-key"
    ANTHROPIC_API_KEY="sk-ant-your-anthropic-key"
    HETZNER_API_TOKEN="hz-your-hetzner-token"
    

    Then, when you launch your OpenClaw agent, ensure these variables are loaded. If you’re using a simple Python script to run your agent, you can manually load them:

    # run_agent.py
    from dotenv import load_dotenv
    import os
    # ... other OpenClaw imports
    
    load_dotenv() # This will load variables from .env into os.environ
    
    # Now you can instantiate and run your agent
    # agent = OpenClawAgent(...)
    

    However, for production deployments on a Hetzner VPS, especially if you’re using Systemd to manage your OpenClaw agent as a service, directly using .env files might not be the most robust approach. Systemd offers a more integrated way to handle environment variables securely.

    Create a Systemd unit file, for example, /etc/systemd/system/openclaw-agent.service:

    [Unit]
    Description=OpenClaw Agent Service
    After=network.target
    
    [Service]
    User=openclaw_user
    Group=openclaw_group
    WorkingDirectory=/path/to/your/openclaw/project
    ExecStart=/usr/bin/python3 /path/to/your/openclaw/project/run_agent.py
    Environment="OPENAI_API_KEY=sk-your-openai-key-from-systemd"
    Environment="ANTHROPIC_API_KEY=sk-ant-your-anthropic-key-from-systemd"
    # ... more environment variables
    
    # Or, if you prefer to source a file (less secure as file might be readable):
    # EnvironmentFile=/path/to/your/credentials.env
    
    Restart=always
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    

    The Environment= directive is powerful. It allows you to specify environment variables directly within the Systemd unit file. For highly sensitive keys, you might store these in a more restricted file, owned by root and readable only by the openclaw_user, and then use EnvironmentFile= to source them. However, for most VPS scenarios, embedding them directly in the Systemd unit, which typically has tight permissions, is a reasonable balance between security and convenience. Remember to run sudo systemctl daemon-reload and sudo systemctl start openclaw-agent after making changes.

    The Non-Obvious Insight: Dynamic Tool Loading and Environment-Specific Configurations

    Here’s where it gets interesting: what if you have multiple agents, or different environments (development, staging, production), each needing different API keys or even different sets of tools? Hardcoding everything or having one monolithic TOOLS.md quickly becomes unmanageable.

    OpenClaw allows you to load tools from multiple TOOLS.md files. You can specify a directory, and it will load all .md files within it. This enables a modular approach:

    .
    ├── .openclaw/
    │   └── config.json
    ├── agents/
    │   └── financial_analyst_agent.py
    │   └── customer_support_agent.py
    ├── tools/
    │   ├── core_utils.md
    │   ├── openai_tools.md
    │   ├── anthropic_tools.md
    │   └── custom_crm_tools.md
    └── .env # For local development
    

    Your .openclaw/config.json could then point to the tools/ directory:

    # .openclaw/config.json
    {
      "tools_path": "tools/",
      "model": "gpt-4o",
      "temperature": 0.7
    }
    

    Now, each .md file in the tools/ directory can define a specific set of tools. For example, openai_tools.md would contain only OpenAI-related tools, while anthropic_tools.md would contain Anthropic ones. Both would use os.getenv() to fetch their respective API keys.

    This structure shines when combined with environment-specific credential management. In development, you might use a .env file. In production, your Systemd unit file or a secrets management system (like Vault or AWS Secrets Manager) would inject the environment variables. The TOOLS.md files themselves remain unchanged, making them portable across environments. The only thing that changes is *how* the environment variables are provided.

    Furthermore, for specific agents requiring a subset of tools, you don’t even need to load all of them. You can pass a list of specific tool paths to your agent initialization:

    # agents/financial_analyst_agent.py
    from openclaw.agent import OpenClawAgent
    
    # Assuming your config.json is set up, this will load all tools from 'tools/'
    # agent = OpenClawAgent(config_path=".openclaw/config.json")
    
    # Or, for more granular control:
    agent = OpenClawAgent(
        tools_paths=["tools/core_utils.md", "tools/openai_tools.md"],
        model="gpt-4o",
        temperature=0.7
    )
    # ... run the agent
    

    This allows for fine-grained control over which tools (and thus which corresponding API keys) are exposed to a particular agent instance. Imagine a ‘public’ agent only having access to basic utilities, while an ‘admin’ agent has access to sensitive CRM tools. Each can have its dedicated TOOLS.md file or be

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *