Files
turnstone/docs/tools.md
T
Patrick Buckley f02972c11d Add provider-native web search with Tavily fallback (#13)
* Add provider-native web search with Tavily fallback

Replace client-side Tavily web search with provider-native implementations:

- Anthropic: inject web_search_20250305 server-side tool, handle
  server_tool_use / web_search_tool_result streaming blocks, emit
  info_delta for search status display
- OpenAI: inject web_search_options for gpt-5-search-api, format
  url_citation annotations as footnote sources
- Local/vLLM: preserve existing Tavily-based web_search tool as fallback

Add supports_web_search to ModelCapabilities and info_delta to StreamChunk.
Remove end-of-life GPT-4o model entries from capability tables.
Update docs, diagrams, and README. 88 provider tests (32 new).

* Fix Copilot PR #13 review: capture streaming url_citation annotations

Accumulate url_citation annotations during OpenAI streaming and emit
formatted citations as a final info_delta chunk after the stream ends.
Previously annotations were only captured in non-streaming mode, so
search model users in the interactive path never saw citation sources.
2026-03-03 17:12:49 -08:00

20 KiB

Tools Reference

turnstone exposes 14 built-in tools plus any number of external MCP tools to the LLM via the OpenAI function-calling interface. Built-in tools are defined as JSON files under turnstone/tools/ and loaded at startup by turnstone/core/tools.py. MCP tools are discovered from configured MCP servers at startup by turnstone/core/mcp_client.py.


Tool Schema Format

Each JSON file in turnstone/tools/ contains a standard OpenAI function-calling schema plus turnstone-specific metadata keys:

{
  "name": "tool_name",
  "description": "What the tool does.",
  "parameters": {
    "type": "object",
    "properties": { ... },
    "required": ["param1"]
  },
  "agent": true,
  "task_agent": true,
  "auto_approve": true,
  "primary_key": "param1"
}

Metadata keys (stripped before sending the schema to the model):

Key Type Meaning
agent bool Tool is available to plan/task sub-agents (read-only subset).
task_agent bool Tool is available to task sub-agents (broader subset).
auto_approve bool Tool runs without user confirmation (read-only, safe operations).
primary_key str When the model sends a bare string instead of JSON args, map it to this parameter name.

Derived Tool Sets

turnstone/core/tools.py loads all JSON files and derives these collections:

Name Description
TOOLS All 14 tool definitions (sent to the model).
AGENT_TOOLS Tools with agent: true -- available to plan sub-agents. Read-only tools.
TASK_AGENT_TOOLS Tools with task_agent: true -- available to task sub-agents. Includes write operations.
AGENT_AUTO_TOOLS Set of tool names with auto_approve: true -- no user confirmation needed.
TASK_AUTO_TOOLS Same as AGENT_AUTO_TOOLS (identical filter).
PRIMARY_KEY_MAP Dict mapping tool name to its primary_key parameter name.

Execution Pipeline

See also: Tool Pipeline diagram

Tool execution follows a three-phase pipeline inside ChatSession._execute_tools():

Phase 1: Prepare

_prepare_tool(tc) is called for each tool call returned by the model.

  • Parses the JSON arguments (with fallback for malformed JSON).
  • If JSON parsing fails entirely, uses PRIMARY_KEY_MAP to map a bare string to the correct parameter.
  • Validates arguments and builds a preview dict containing:
    • call_id, func_name, header, preview (for display)
    • needs_approval (bool)
    • execute (callable to run the tool)
    • error (set if validation fails; tool will not execute)

Phase 2: Approve

All prepared items are sent to the UI via ui.approve_tools(items).

  • The UI displays each tool's header and preview to the user.
  • Items where needs_approval is False (auto-approved tools) are shown but do not block execution.
  • Items where needs_approval is True require the user to accept or deny.
  • The user can provide feedback alongside their approval (e.g. "y, use full path").
  • If auto_approve is True on the session (headless mode), all tools are approved automatically.

Phase 3: Execute

Each item's execute callable is invoked:

  • Single tool calls run directly on the current thread.
  • Multiple tool calls run in parallel via ThreadPoolExecutor(max_workers=4).
  • Errored or denied items return their error/denial message without executing.
  • The bash tool streams stdout incrementally: each line calls ui.on_tool_output_chunk(call_id, line) as it is produced, then the final combined output (stdout + stderr) is delivered via ui.on_tool_result(call_id, name, output). The call_id links tool_info/approve_request items to their streaming chunks and final result, enabling correct routing when multiple bash tools run in parallel. Other tools deliver results atomically via ui.on_tool_result(call_id, name, output) only.
  • Special post-execution gate for plan: the plan output is shown to the user for review, and the user can reject or annotate it.

Tool Approval Flow

Auto-approved (no user confirmation needed at runtime):

  • read_file -- reads files, no side effects
  • search -- grep-style search, no side effects
  • man -- reads man pages, no side effects
  • remember -- writes to persistent memory database (lightweight, always auto-approved)
  • recall -- reads from persistent memory database
  • forget -- deletes from persistent memory database (lightweight, always auto-approved)

Requires user confirmation (write operations, network access, side effects):

  • bash -- arbitrary command execution
  • write_file -- creates or overwrites files
  • edit_file -- modifies file content
  • math -- sandboxed computation (confirmation required despite being sandboxed)
  • web_fetch -- fetches a URL (SSRF-protected, but makes network requests)
  • web_search -- web search via Tavily API (makes network requests)
  • task -- spawns an autonomous sub-agent
  • plan -- spawns a planning sub-agent, plus post-execution review gate

Note: The JSON schema metadata key auto_approve controls membership in AGENT_AUTO_TOOLS/TASK_AUTO_TOOLS (used for agent sub-sessions). The actual runtime approval behavior is determined by the needs_approval field set in each _prepare_* method on ChatSession. These two mechanisms can differ.


Primary Key Fallback

When the model sends a bare string instead of a JSON object as tool arguments (common with smaller models), the primary_key mapping rescues the call:

Model sends:  bash("ls -la")
              raw_args = "ls -la"   (not valid JSON)

PRIMARY_KEY_MAP["bash"] = "command"
Result:       args = {"command": "ls -la"}

Every tool defines a primary_key. The mapping is:

Tool primary_key
bash command
read_file path
write_file content
edit_file old_string
search query
math code
man page
web_fetch url
web_search query
task prompt
plan prompt
remember key
recall query
forget key

File Operations

bash

Execute a bash command and return stdout + stderr.

Parameter Type Required Description
command string yes The bash command to execute.
  • What it does: Runs the command in a subprocess with a configurable timeout. Commands are sanitized and checked against a blocklist (e.g. rm -rf /).
  • Auto-approve: No -- requires user confirmation.
  • Agent availability: task_agent only (not available to plan sub-agents).

read_file

Read the contents of a file, returning numbered lines.

Parameter Type Required Description
path string yes Absolute or relative file path.
offset integer no Line number to start from (1-based, default: 1).
limit integer no Maximum number of lines to read. Omit for full file.
  • What it does: Reads the file and returns content with line numbers. Must be called before edit_file on the same path (the session tracks which files have been read).
  • Auto-approve: Yes.
  • Agent availability: agent and task_agent.

write_file

Write content to a file, creating it if needed.

Parameter Type Required Description
path string yes Absolute or relative file path.
content string yes The full file content to write.
  • What it does: Creates or overwrites the file at the given path. Parent directories are created as needed.
  • Auto-approve: No -- requires user confirmation.
  • Agent availability: task_agent only.

edit_file

Replace an exact string in a file with new content.

Parameter Type Required Description
path string yes Absolute or relative file path.
old_string string yes The exact text to find and replace.
new_string string yes The replacement text.
near_line integer no Disambiguate when old_string matches multiple locations.
  • What it does: Finds old_string in the file and replaces it with new_string. Fails if the string is not found or matches multiple locations (unless near_line is provided to pick the nearest match). Requires a prior read_file call on the same path.
  • Auto-approve: No -- requires user confirmation.
  • Agent availability: task_agent only.

Search file contents for a regex pattern.

Parameter Type Required Description
query string yes Regex pattern (extended regex).
path string no File or directory to search in (default: current directory).
  • What it does: Recursively searches for the pattern using grep -rn. Returns matching lines with file paths and line numbers.
  • Auto-approve: Yes.
  • Agent availability: agent and task_agent.

Computation

math

Execute Python code for math and computation in a sandbox.

Parameter Type Required Description
code string yes Python code to execute. Must use print() for output.
  • What it does: Runs Python code in a sandboxed environment with pre-imported libraries: sympy, numpy, scipy, math, fractions, itertools, functools, collections, decimal, operator, random, re, string. Common sympy names (symbols, solve, simplify, sqrt, Matrix, etc.) are pre-imported.
  • Auto-approve: No -- requires user confirmation.
  • Agent availability: agent and task_agent.

Information

man

Read a man page.

Parameter Type Required Description
page string yes The man page name (e.g. grep, socket, printf).
section string no Manual section (e.g. 1 commands, 2 syscalls, 3 library).
  • What it does: Returns the full formatted manual entry. Preferred over bash('man ...') or web_search for command/API documentation.
  • Auto-approve: Yes.
  • Agent availability: agent and task_agent.

web_fetch

Fetch a URL and extract specific information from it.

Parameter Type Required Description
url string yes The URL to fetch (must start with http:// or https://).
question string yes What to extract or answer from the page content.
  • What it does: Fetches the URL, strips HTML to plain text, and uses the LLM to extract the answer to the question from the page content. Protected against SSRF (blocks private/internal IPs).
  • Auto-approve: No -- requires user confirmation (makes network requests).
  • Agent availability: agent and task_agent.

Search the web using a text query.

Parameter Type Required Description
query string yes The search query.
max_results integer no Max results to return (default 5, max 20).
topic string no Search topic: general, news, or finance (default general).
  • What it does: Searches the web and returns ranked results with titles, URLs, and content snippets. Uses provider-native search when available:
    • Anthropic: Replaced at the API boundary with Anthropic's web_search_20250305 server-side tool. Claude decides when to search; the API executes it and returns results with citations inline. No Tavily key needed.
    • OpenAI search models (gpt-5-search-api): Replaced with web_search_options parameter. The model always searches and returns url_citation annotations.
    • Local/vLLM models: Falls back to the Tavily API. Requires tavily_key in config.toml or $TAVILY_API_KEY.
  • Auto-approve: Yes (auto-approved for all tool dispatch paths).
  • Agent availability: agent and task_agent.

Agent

task

Delegate a general-purpose task to an autonomous sub-agent.

Parameter Type Required Description
prompt string yes Complete task description for the sub-agent.
  • What it does: Spawns a sub-agent that inherits the TASK_AGENT_TOOLS set (read, write, edit, search, bash, math, man, web tools, memory tools). The sub-agent runs autonomously to completion. Use for work that requires file modifications or command execution.
  • Auto-approve: No -- requires user confirmation.
  • Agent availability: Not available to sub-agents (top-level only).

plan

Plan before implementing -- an autonomous agent explores the codebase and writes a structured plan.

Parameter Type Required Description
prompt string yes What to plan -- the goal, constraints, and scope.
  • What it does: Spawns a planning sub-agent with AGENT_TOOLS (read-only tools: read_file, search, math, man, web_fetch, web_search). The agent explores the codebase and writes a structured plan to .plan-<session_id>.md (unique per session, so concurrent workstreams never collide). If the plan tool has been called before in the same session, the prior plan is passed to the agent as context so it refines rather than restarts. After completion, the user is prompted to review and can accept, reject, or annotate the plan.
  • Auto-approve: No -- requires user confirmation, plus post-execution review gate.
  • Agent availability: Not available to sub-agents (top-level only).

Memory

remember

Save a persistent memory that persists across sessions.

Parameter Type Required Description
key string yes Short identifier (e.g. user_name).
value string yes Content to remember.
  • What it does: Stores a key-value pair in the SQLite memory database. Memories persist across sessions and are included in the system prompt on startup.
  • Auto-approve: Yes.
  • Agent availability: Not available to sub-agents (top-level only).

recall

Search memories and past conversations.

Parameter Type Required Description
query string no Search term or phrase. Omit to list all memories.
limit integer no Max conversation results to return (default 20).
  • What it does: With no query, lists all saved memories. With a query, searches both the memory store and conversation history using FTS5 full-text search.
  • Auto-approve: Yes.
  • Agent availability: Not available to sub-agents (top-level only).

forget

Remove a persistent memory by key.

Parameter Type Required Description
key string yes The memory key to remove (e.g. user_name).
  • What it does: Deletes the memory entry with the given key from the SQLite database.
  • Auto-approve: Yes.
  • Agent availability: Not available to sub-agents (top-level only).

Summary Table

Tool Category Auto-approve agent task_agent primary_key
bash File Ops No No Yes command
read_file File Ops Yes Yes Yes path
write_file File Ops No No Yes content
edit_file File Ops No No Yes old_string
search File Ops Yes Yes Yes query
math Compute No Yes Yes code
man Info Yes Yes Yes page
web_fetch Info No Yes Yes url
web_search Info No Yes Yes query
task Agent No No No prompt
plan Agent No No No prompt
remember Memory Yes No No key
recall Memory Yes No No query
forget Memory Yes No No key

MCP Tools (External)

Turnstone supports the Model Context Protocol (MCP) for connecting external tool servers — GitHub, databases, filesystems, or any MCP-compatible service.

How it works

  1. Configuration: MCP servers are defined in config.toml under [mcp.servers.*] sections, or via a standard MCP JSON config file (--mcp-config).

  2. Discovery: At startup, MCPClientManager connects to each configured server (via stdio subprocess or HTTP), performs the MCP initialize handshake, and calls tools/list to discover available tools.

  3. Schema conversion: Each MCP tool's inputSchema is converted to OpenAI function-calling format. The tool name is prefixed: mcp__{server}__{tool}.

  4. Merging: MCP tools are appended after the 14 built-in tools via merge_mcp_tools(). Built-in tools appear first, giving them natural LLM priority.

  5. Dispatch: When the LLM calls an MCP tool, _prepare_mcp_tool() builds a generic approval preview and _exec_mcp_tool() calls MCPClientManager.call_tool_sync(), which dispatches the call to the background asyncio event loop.

Approval behavior

MCP tools require user approval by default (needs_approval: True). turnstone does not auto-approve MCP tools based on their schema, since it cannot guarantee that external tools are read-only. However, global overrides such as --skip-permissions or the UI's "always allow" setting will auto-approve all tools, including MCP tools.

Sub-agent availability

MCP tools are available to:

  • Main session — full access
  • Task sub-agents — via self._task_tools (merged list)
  • Plan sub-agents — via self._agent_tools (merged list)

Naming convention

MCP tool names follow the pattern mcp__{server}__{original}:

  • mcp__github__search_repossearch_repos tool from github server
  • mcp__postgres__queryquery tool from postgres server

Server names must not contain __ (double underscore), which is reserved as the delimiter. Servers with __ in their name are rejected at connection time.

Configuration

TOML (~/.config/turnstone/config.toml):

[mcp.servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]

[mcp.servers.github.env]
GITHUB_TOKEN = "ghp_..."

[mcp.servers.remote]
type = "http"
url = "https://mcp.example.com/mcp"

JSON (standard mcpServers format, via --mcp-config):

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {"GITHUB_TOKEN": "ghp_..."}
    }
  }
}

Introspection

Use the /mcp slash command to list all connected MCP tools:

/mcp
MCP tools (3):
  mcp__github__search_repos  [MCP: github] Search GitHub repositories
  mcp__github__create_issue  [MCP: github] Create a GitHub issue
  mcp__postgres__query       [MCP: postgres] Run a SQL query