Tools

Tools are functions the LLM can call during execution.

Agentspan ships built-in tools for the most common cases — HTTP endpoints, full OpenAPI specs, and MCP servers — so you don’t have to write code for them. When you need custom logic, decorate any Python function with @tool and it’s available to the LLM automatically.

TypeWho writes the codeHow it runsUse when
@toolYouPython worker processCustom logic, any Python library
http_tool()Nobody — configure onlyServer-side HTTP taskSingle API endpoint
api_tool()Nobody — configure onlyServer-side, auto-discoveredFull OpenAPI/Swagger/Postman spec
mcp_tool()Nobody — configure onlyServer-side MCP taskMCP server

For http_tool, api_tool, and mcp_tool, you provide a URL and optionally credentials. Agentspan handles execution entirely on the server — no worker process, no Python code, nothing to maintain.

@tool — Custom Python Functions

Decorate any Python function to make it a tool:

from agentspan.agents import Agent, AgentRuntime, tool

@tool
def get_weather(city: str) -> dict:
    """Get current weather for a city."""
    return {"city": city, "temp": 72, "condition": "Sunny"}

agent = Agent(name="assistant", model="openai/gpt-4o", tools=[get_weather])

with AgentRuntime() as runtime:
    result = runtime.run(agent, "What's the weather in NYC?")
    result.print_result()

The JSON schema for the LLM is auto-generated from type hints and the docstring. The function also works as a normal Python function: get_weather("NYC") returns the dict directly.

Options

@tool(name="custom_name", approval_required=True, timeout_seconds=60)
def dangerous_action(target: str) -> dict:
    """Do something that requires human approval."""
    return {"done": True}
ParameterDefaultDescription
namefunction nameOverride the tool name
approval_requiredFalseInsert a wait task for human approval before execution
timeout_secondsNoneMaximum execution time

ToolContext — shared state and dependencies

Add a context: ToolContext parameter to access execution context and shared state:

from agentspan.agents import tool, ToolContext

@tool
def query_database(query: str, context: ToolContext) -> dict:
    """Run a database query."""
    db = context.dependencies["db"]
    user_id = context.dependencies["user_id"]
    return db.execute(query, user=user_id)

@tool
def add_item(item: str, context: ToolContext) -> str:
    """Add an item to the shared list."""
    items = context.state.get("items", [])
    items.append(item)
    context.state["items"] = items
    return f"Added '{item}'. List has {len(items)} items."

ToolContext fields:

FieldTypeDescription
execution_idstrExecution ID
session_idstrSession ID
agent_namestrName of the executing agent
dependenciesDictDependencies injected via Agent(dependencies=...)
stateDictMutable shared state across tool calls in the same execution
metadataDictMetadata from the agent

The context parameter is excluded from the tool’s JSON schema — the LLM never sees it.

Pass dependencies at agent creation:

agent = Agent(
    name="bot",
    model="openai/gpt-4o",
    tools=[query_database],
    dependencies={"db": my_database, "user_id": "u-123"},
)

http_tool() — HTTP Endpoints

Define a single HTTP endpoint as a tool. Executes entirely server-side — no worker process needed:

from agentspan.agents import http_tool

weather_api = http_tool(
    name="get_weather",
    description="Get weather for a city",
    url="https://api.weather.com/v1/current",
    method="GET",
    headers={"Authorization": "Bearer token"},
    input_schema={
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
)

agent = Agent(name="assistant", model="openai/gpt-4o", tools=[weather_api])

api_tool() — OpenAPI Auto-Discovery

Point to any OpenAPI, Swagger, or Postman spec. All endpoints are auto-discovered and exposed as tools. The LLM filters to the most relevant ones at runtime:

from agentspan.agents import api_tool

stripe = api_tool(
    url="https://api.stripe.com/openapi.json",
    headers={"Authorization": "Bearer ${STRIPE_KEY}"},
    credentials=["STRIPE_KEY"],
    max_tools=20,   # LLM auto-filters 300+ ops to top 20 most relevant
)

agent = Agent(
    name="billing_assistant",
    model="openai/gpt-4o",
    tools=[stripe],
)

with AgentRuntime() as runtime:
    result = runtime.run(agent, "Create a Stripe customer for alice@example.com")
    result.print_result()
ParameterDescription
urlURL to an OpenAPI/Swagger JSON spec, or a Postman collection
headersHTTP headers sent with every request (use ${CRED_KEY} for credentials)
credentialsList of credential keys to inject (see below)
max_toolsMaximum tools to expose to the LLM (default: all)

mcp_tool() — MCP Servers

Connect to an MCP server. Tools are auto-discovered at runtime. Executes server-side:

from agentspan.agents import mcp_tool

github = mcp_tool(
    server_url="http://localhost:6767/mcp",
    name="github",
    description="GitHub operations",
)

agent = Agent(name="dev_assistant", model="openai/gpt-4o", tools=[github])

Credential Management

Store secrets on the server once. Tools resolve them automatically at runtime — no .env files, no hardcoded keys, no secrets in git.

Step 1: Store credentials

agentspan credentials set GITHUB_TOKEN ghp_xxxxxxxxxxxx
agentspan credentials set SEARCH_API_KEY xxx-your-key

Credentials are encrypted at rest (AES-256-GCM).

Step 2: Declare which credentials a tool needs

from agentspan.agents import tool, get_credential

# Option A: isolated subprocess (credentials available as env vars)
@tool(credentials=["GITHUB_TOKEN"])
def list_repos(username: str) -> dict:
    """List GitHub repos."""
    import os
    token = os.environ["GITHUB_TOKEN"]   # Auto-injected
    return {"repos": ["repo1", "repo2"]}

# Option B: in-process (use get_credential)
@tool(isolated=False, credentials=["SEARCH_API_KEY"])
def search(query: str) -> dict:
    """Search using API key."""
    key = get_credential("SEARCH_API_KEY")   # Resolved from server
    return {"results": ["result1"]}

With HTTP and MCP tools:

# HTTP tool: server substitutes ${KEY} in headers at runtime
api = http_tool(
    name="weather_api", description="Get weather",
    url="https://api.weather.com/v1/current",
    headers={"Authorization": "Bearer ${WEATHER_KEY}"},
    credentials=["WEATHER_KEY"],
)

# MCP tool: credentials passed to MCP server connection
github = mcp_tool(
    server_url="http://localhost:3001/mcp",
    credentials=["GITHUB_TOKEN"],
)

Agent-level credentials (shared with all tools):

agent = Agent(
    name="github_helper",
    model="openai/gpt-4o",
    tools=[list_repos, search],
    credentials=["GITHUB_TOKEN"],
)

Code Execution

Agents can execute code in a sandboxed environment:

from agentspan.agents import Agent, AgentRuntime
from agentspan.agents.code_executor import DockerCodeExecutor

executor = DockerCodeExecutor(image="python:3.12-slim", timeout=30)

agent = Agent(
    name="coder",
    model="openai/gpt-4o",
    tools=[executor.as_tool()],
    instructions="Write and execute Python code to solve problems.",
)

with AgentRuntime() as runtime:
    result = runtime.run(agent, "Calculate the first 20 Fibonacci numbers.")
    result.print_result()

Four built-in execution environments:

ExecutorUse case
DockerCodeExecutorDocker container (isolated, recommended for production)
LocalCodeExecutorLocal subprocess (fast, no isolation)
JupyterCodeExecutorJupyter kernel (stateful, good for data science)
ServerlessCodeExecutorCloud function (scales to zero)

Human Approval

Mark any tool to require human approval before it runs:

@tool(approval_required=True)
def delete_production_data(table: str) -> dict:
    """Delete data from a production table. Requires approval."""
    return {"deleted": True, "table": table}

The execution pauses when the LLM calls this tool. Use handle.approve() or handle.reject(reason) to resume. See Human-in-the-Loop examples.

Circuit Breaker

Tools that fail 3 consecutive times are automatically disabled. The LLM is told to try a different approach. On a successful call, the error counter resets.

Mixing Tool Types

Agents can use all tool types together:

agent = Agent(
    name="assistant",
    model="openai/gpt-4o",
    tools=[
        get_weather,       # @tool Python function
        weather_api,       # http_tool
        stripe,            # api_tool (OpenAPI)
        github,            # mcp_tool
    ],
)