> For the complete documentation index, see [llms.txt](https://docs.promptshuttle.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.promptshuttle.com/tools/tools.md).

# Tools & Function Calling

Tools extend what an LLM can do during execution. When a template has tools assigned, PromptShuttle automatically manages the tool-calling loop: the LLM decides which tools to call, PromptShuttle executes them, feeds results back, and re-invokes the LLM until it produces a final answer.

## How the tool loop works

```
1. Send prompt + tool definitions to LLM
2. LLM responds with tool_calls (or final text)
3. If tool_calls:
   a. Execute all tool calls in parallel
   b. Append results to conversation
   c. Go to step 1 (re-invoke LLM with results)
4. If final text: return response
```

The loop continues until the LLM stops calling tools or the `maxToolCalls` limit is reached.

## Tool types

PromptShuttle supports five tool types, each suited to different use cases:

| Type             | What it does                                                            |
| ---------------- | ----------------------------------------------------------------------- |
| **External**     | Calls an HTTP endpoint with parameters from the LLM's tool call         |
| **Virtual**      | Enables provider-native capabilities (web search, code interpreter)     |
| **Agent**        | Invokes another flow as a sub-agent, enabling multi-agent orchestration |
| **CritiqueLoop** | Runs a producer/critic refinement loop to iteratively improve output    |
| **MCP**          | Calls a tool on an external Model Context Protocol server               |

## External tools

External tools call HTTP endpoints. The LLM decides the arguments; PromptShuttle builds the request and returns the response.

### Configuration

| Field                 | Description                                                                                                                                                          |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                | Tool name (shown to the LLM)                                                                                                                                         |
| `description`         | What the tool does (helps the LLM decide when to use it)                                                                                                             |
| `parameters`          | Parameter schema (name, type, description, required, enum values)                                                                                                    |
| `webUrl`              | Base HTTP endpoint URL. An absolute `https://…` names a fixed host; a relative path (`/api/v1/search`) makes the tool [caller-hosted](/tools/caller-hosted-tools.md) |
| `webUrls`             | Environment-specific URL overrides (`{ "production": "https://...", "staging": "https://..." }`)                                                                     |
| `headers`             | Custom HTTP headers (e.g. API keys)                                                                                                                                  |
| `callbackTokenHeader` | Header carrying the caller's per-run token on caller-hosted calls (default `X-Tool-Token`)                                                                           |

### How it works

1. The LLM returns a tool call with arguments (e.g. `{ "query": "weather in Berlin" }`)
2. PromptShuttle sends an HTTP GET to the configured URL with arguments as query parameters
3. The response body (up to 1MB) is returned to the LLM as the tool result

### Parameter placeholders in URLs

External tool URLs support `[[param]]` placeholders that are substituted from the flow's request parameters:

```
https://api.example.com/v1/[[region]]/search
```

If the flow was called with `"parameters": { "region": "eu" }`, the URL becomes `https://api.example.com/v1/eu/search`.

### Environment-specific URLs

Use `webUrls` to point to different endpoints per environment:

```json
{
  "webUrl": "https://staging-api.example.com/search",
  "webUrls": {
    "production": "https://api.example.com/search",
    "staging": "https://staging-api.example.com/search"
  }
}
```

### Tools your own app serves

If the endpoint lives in the same application that calls PromptShuttle, its address differs on every machine that application runs on — so it does not belong on the tool at all. Give the tool a relative path instead and let each request supply the host:

```json
{ "webUrl": "/api/v1/tools/search?schema_id=[[schema_id]]" }
```

The origin then comes from the `X-Shuttle-Callback-Url` header per request. If a request omits it, the tool call fails rather than falling back to a stored URL.

See [Caller-Hosted Tools](/tools/caller-hosted-tools.md).

## Virtual tools (provider-native)

Virtual tools enable capabilities built into the LLM provider, such as web search. They are handled natively by the provider — PromptShuttle passes them through.

### Portable tokens

Use `search` or `web_search` to request web search. Both tokens map to the serving provider's own search tool, whatever its wire name is. Matching is case-insensitive.

| Token you send  | OpenAI               | Gemini          | Anthropic    | xAI                               |
| --------------- | -------------------- | --------------- | ------------ | --------------------------------- |
| `search`        | `web_search_preview` | `google_search` | `web_search` | `web_search`                      |
| `web_search`    | `web_search_preview` | `google_search` | `web_search` | `web_search`                      |
| `social_search` | —                    | —               | —            | `x_search`                        |
| `x_search`      | —                    | —               | —            | `x_search`                        |
| `url_context`   | —                    | `url_context`   | —            | `web_search` (degrades to search) |

A token the serving provider does not offer is **skipped, not failed** — the model answers without the tool, so a fallback candidate degrades instead of dying. The run then records a `vendorToolUnavailable` warning (see Run Health) naming the skipped token. Before this warning existed the skip was silent.

### Using virtual tools

Virtual tools can be enabled in two ways:

1. **On the template** — Create a tool with `type: Virtual` and `virtualToolId: "search"`, then assign it to the template
2. **At request time** — Pass `vendorTools: ["search"]` in the flow run request

### Citations

Search-grounded responses carry their sources in `citations` on the response payload (`url`, `title`, `domain`). Sources are unified across providers: OpenAI `url_citation` annotations, Gemini grounding chunks, xAI inline citations plus its top-level source list.

Combining `vendorTools` with a `responseSchema` is safe: PromptShuttle runs the research schema-free, adds one structuring turn when needed, and carries the research turn's citations onto the final structured answer. Measured provider behavior for a *single* combined call (the passthrough `/chat/completions` path): OpenAI keeps annotations alongside a JSON-schema response (measured 6/6, two model families, 2026-08-20); Gemini returns grounding chunks only unreliably under a schema (4 of 12 runs came back with zero chunks even though searches ran — both AI Studio and Vertex, gemini-3-flash-preview).

## Agent tools

Agent tools invoke another flow as a sub-agent. This is the foundation of multi-agent orchestration in PromptShuttle.

### Configuration

| Field             | Description                                                              |
| ----------------- | ------------------------------------------------------------------------ |
| `agentTemplateId` | The ID of the flow to invoke as a sub-agent                              |
| `maxAgentDepth`   | Maximum nesting depth for this specific agent (overrides tenant default) |

### How it works

1. The LLM calls the agent tool with arguments
2. PromptShuttle creates a child request linked to the parent
3. The child flow executes with its own tool-calling loop
4. The child's text response is returned to the parent LLM as the tool result
5. Cost is tracked both per-agent (direct) and across the tree (cumulative)

### Safety mechanisms

* **Depth limits** — Default max depth is 10. Configurable per tool, per tenant, or per request.
* **Cycle detection** — PromptShuttle tracks the ancestor chain and prevents an agent from calling itself (directly or indirectly).
* **Cost limits** — Per-request cost ceilings apply across the entire agent tree.

### Context tools

When a template is running as a sub-agent, PromptShuttle automatically injects context tools:

| Tool                 | Description                                                                | Injected at             |
| -------------------- | -------------------------------------------------------------------------- | ----------------------- |
| `get_context`        | Returns execution metadata: depth, agent path, cost used, budget remaining | sub-agents only         |
| `get_original_input` | Returns the root request's original user messages                          | sub-agents only         |
| `get_state`          | Read from shared state (lexically scoped — child values shadow parent)     | all requests with tools |
| `set_state`          | Write to shared state (visible to child agents)                            | all requests with tools |

These let agents be context-aware without hardcoding parent/child relationships.

A **root** request (the one you start) is only given the two state tools. The other two answer questions a root request can already see: `get_original_input` replays the root's own messages back to the model that is already reading them, and `get_context` reports a depth of 0 on a path of one. The state tools stay because a root write lands on the root's state, and every descendant reads it — that is the supported way to seed state for a whole agent tree before spawning.

All four remain *callable* by name at any depth, so a prompt that names one directly keeps working.

## CritiqueLoop tools

CritiqueLoop tools implement an iterative refinement pattern: a "producer" flow generates output, then a "critic" flow evaluates it. If the critic rejects the output, the producer tries again with the critic's feedback.

### Configuration

| Field               | Description                               |
| ------------------- | ----------------------------------------- |
| `producerFlowId`    | Flow ID of the producer                   |
| `criticFlowId`      | Flow ID of the critic                     |
| `maxLoopIterations` | Maximum refinement iterations (default 3) |

### How it works

```
for each iteration (up to maxLoopIterations):
    1. Run producer with original input + last critique feedback
    2. Run critic with (original input + producer output)
    3. Critic returns: { "approved": true/false, "feedback": "..." }
    4. If approved → return producer output
    5. If rejected → feed critique back to producer, continue
```

The critic always runs with `temperature: 0` for consistent evaluation.

### Critic response format

The critic flow must return JSON matching this schema:

```json
{
  "approved": true,
  "feedback": "The response addresses all key points."
}
```

If `approved` is `false`, the `feedback` string is passed back to the producer for the next iteration.

## MCP tools

MCP tools call tools on external [Model Context Protocol](https://modelcontextprotocol.io) servers. This lets you integrate with any MCP-compatible tool server.

See [MCP Server Integration](/tools/mcp-servers.md) for setup details.

## Managing tools via API

| Method   | Endpoint                   | Description                         |
| -------- | -------------------------- | ----------------------------------- |
| `GET`    | `/api/v1/tools`            | List all tools                      |
| `GET`    | `/api/v1/tools/{id}`       | Get a tool by ID                    |
| `POST`   | `/api/v1/tools`            | Create a new tool                   |
| `PUT`    | `/api/v1/tools/{id}`       | Update a tool                       |
| `DELETE` | `/api/v1/tools/{id}`       | Delete a tool                       |
| `GET`    | `/api/v1/tools/{id}/usage` | List flows that reference this tool |

### Tool parameter schema

Each tool defines its parameters with a list of properties:

```json
{
  "name": "search_api",
  "description": "Search for products by keyword",
  "toolType": "External",
  "webUrl": "https://api.example.com/search",
  "parameters": {
    "type": "Object",
    "properties": [
      {
        "name": "query",
        "description": "Search query string",
        "type": "String",
        "required": true
      },
      {
        "name": "category",
        "description": "Product category to filter by",
        "type": "String",
        "required": false,
        "enum": ["electronics", "clothing", "books"]
      },
      {
        "name": "limit",
        "description": "Maximum number of results",
        "type": "Number",
        "required": false
      }
    ]
  }
}
```

Property types: `String`, `Number`. Use `isList: true` for array parameters. Use `enum` to constrain values.
