Documentation

Tools

How tools are declared, surfaced, and executed in Nimblesite.

Tools

Tools are how your agent interacts with the real world — files, databases, APIs, user accounts, anything. Nimblesite gives you two ways to run them:

  • Client-side tools (default). The agent decides what to call. Your app runs it. We never touch your data, your APIs, or your secrets. This is the same pattern as OpenAI function calling and Anthropic tool use, but persisted and multi-tenant out of the box.
  • Sandboxed agents. The agent runs against a managed sandbox we provision per config — filesystem, shell, build pipeline included. Same idea as Code Interpreter or E2B, with conversation memory wired in.

This page documents the client-side protocol. For sandboxed agents see Agent execution modes.

The tool contract

A tool is a name and a signature. That's it. You declare the names in your agent config:

{
  "tools_config": ["read_file", "write_file", "build_site"]
}

When the agent decides to call one, you get a tool_calls array in the chat response:

{
  "response": null,
  "tool_calls": [
    {
      "name": "write_file",
      "arguments": {
        "path": "data/site.json",
        "content": "..."
      }
    }
  ],
  "conversation_id": "11111111-1111-1111-1111-111111111111"
}

Your app executes the tool, then posts the result back:

curl -X POST https://api.nimblesite.dev/api/v1/chat/$CONFIG_ID \
  -H "X-API-Key: $NIMBLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "11111111-1111-1111-1111-111111111111",
    "tool_results": [
      {"name": "write_file", "result": "ok"}
    ]
  }'

The agent picks up where it left off, with the new information.

Why you execute, not us — in client-side mode

Three reasons, in order of how loud we'll say them:

  1. Security. Tools touch your data, your APIs, and your secrets. We don't want any of those, and you don't want us to have them. Your auditors will thank you.
  2. Correctness. Your tools run in your language, your runtime, your trust boundary, with your error handling. You already know how to run code in your own app. Adding a second runtime is a tax, not a feature.
  3. Portability. You never write a "Nimblesite tool." You write a normal function in your app. If you ever switch platforms, the function still works.

If those tradeoffs don't fit — say, you want a coding agent with a real filesystem and shell — switch the config to sandboxed mode and we run a managed sandbox for you. See Agent execution modes.

A real example

Here's how AI CMS uses tools to power a chat-to-edit website builder:

{
  "tools_config": [
    "read_file",
    "write_file",
    "replace_in_file",
    "list_files",
    "build_site",
    "escalate_to_claude_code"
  ]
}

None of those tools exist inside Nimblesite. They exist inside AI CMS's own backend, which proxies tool calls to ephemeral Fly.io containers that own the website source files. When the user says "change my phone number", the flow is:

  1. AI CMS sends the message to Nimblesite
  2. Nimblesite's agent decides to call replace_in_file
  3. Nimblesite returns a tool_calls array to AI CMS
  4. AI CMS proxies the call to the right Fly.io container
  5. The container runs the replace and returns the result
  6. AI CMS posts the result back to Nimblesite
  7. Nimblesite generates the final "Done — phone updated" response

Nimblesite never saw the file. Never had credentials to the container. Never executed code. That's the correct architecture.

Custom tools vs. built-in tools

Nimblesite ships with a small set of built-in tool names (get_current_time, echo) for testing. In production you declare whatever names you want in tools_config, and your app is responsible for recognising and running them.

There is no "tool marketplace." There is no shared tool runtime. There is no Nimblesite SDK you have to import to write a tool. A tool is a name you made up, and a function in your app that handles it.