Tool Discovery.
Discover available API operations as machine-readable tool definitions.
The /tools endpoint lets your agent discover what it can do. It returns machine-readable tool definitions scoped to the authenticated key's role and permissions. No manual tool configuration needed.
How It Works
sequenceDiagram
participant Agent as Your Agent
participant API as Agent API
Note over Agent: 1. Agent starts up
Agent->>API: GET /api/agent/v1/tools?format=openai
Note over API: Reads agent-api.json<br/>Filters by key's role<br/>Filters by key's scopes<br/>Filters by permissions<br/>Formats for framework
API-->>Agent: Tool definitions (JSON)
Note over Agent: 2. Passes tools to LLM provider
Agent->>API: POST, GET, PUT... (tool calls)
Note over Agent: 3. LLM calls tools →<br/>Agent routes to Agent API
API-->>Agent: API responses
Your agent calls /tools once at startup, then uses the returned definitions for all subsequent LLM interactions.
Request
curl -X GET https://your-domain.com/api/agent/v1/tools?format=openai \
-H "X-Agent-Key: rl_agent_..."
| Parameter | Type | Default | Description |
|---|---|---|---|
format |
string | generic |
Output format: openai, anthropic, mcp, or generic |
Response
{
"error": false,
"tools": [ ... ],
"meta": {
"role": "partner",
"format": "openai",
"tool_count": 46,
"cached": false
}
}
The meta section includes how many tools are available and whether this response was served from cache.
Supported Formats
OpenAI Function Calling (?format=openai)
Returns tools in the OpenAI function calling format. Each tool is a type: "function" object with name, description, and parameters schema.
[
{
"type": "function",
"function": {
"name": "list_loyalty_cards",
"description": "List all loyalty cards for this partner\n...\n\nHTTP: GET /api/agent/v1/partner/cards",
"parameters": {
"type": "object",
"properties": {
"page": { "type": "integer", "default": 1 },
"per_page": { "type": "integer", "default": 25 }
},
"required": []
}
}
}
]
Anthropic/Claude Tool Use (?format=anthropic)
Returns tools in the Anthropic tool use format. Each tool has name, description, and input_schema.
[
{
"name": "record_purchase",
"description": "Record a purchase and award loyalty points\n...\n\nHTTP: POST /api/agent/v1/partner/transactions/purchase",
"input_schema": {
"type": "object",
"properties": { ... },
"required": ["card_id", "member_identifier", "purchase_amount"]
}
}
]
MCP (?format=mcp)
Returns tools in the Model Context Protocol format. Wrapped in a { "tools": [...] } envelope with inputSchema for each tool.
Generic JSON Schema (?format=generic)
Returns a self-documenting format with method, path, scopes, and full parameter schemas as first-class fields. Suitable for custom integrations, Zapier/Make/n8n, and any platform that can consume JSON.
{
"api_name": "Reward Loyalty Agent API",
"api_version": "1.1.0",
"base_url": "/api/agent/v1",
"auth": {
"type": "api_key",
"header": "X-Agent-Key"
},
"tools": [
{
"name": "list_loyalty_cards",
"description": "List all loyalty cards for this partner",
"method": "GET",
"path": "/api/agent/v1/partner/cards",
"required_scopes": ["read", "write:cards"],
"parameters": { ... }
}
]
}
Schema Preservation
Tool parameters preserve the full JSON Schema structure from the OpenAPI spec. This includes:
- Translatable fields.
oneOf: [string, object]for fields that accept either a plain string or a locale-keyed object ({"en": "...", "nl": "..."}) - Nested objects. Object-typed properties with their own sub-properties
- Arrays. Array-typed properties with
itemsschema - Enums, patterns, min/max, format. All JSON Schema keywords pass through
This means your LLM receives accurate type information for every parameter, including complex types.
Role, Scope & Permission Filtering
The /tools endpoint returns tools the authenticated key can use, nothing more. Three filtering layers apply:
Role Filtering
- Partner keys see partner endpoints and the health check
- Admin keys see admin endpoints and the health check
- Member keys see member endpoints and the health check
Scope Filtering
- Keys with limited scopes (e.g.,
read) see the endpoints their scopes grant access to, nothing more - Keys with the
adminsuper-scope see all endpoints for their role
Permission Filtering (Partner)
Partner keys are subject to two layers of permission checks:
Top-level API access. If agent_api_permission is false for the partner, /tools returns 403 FEATURE_DISABLED. The response matches what the partner would receive from any partner endpoint.
{
"error": true,
"code": "FEATURE_DISABLED",
"message": "Agent API access has been revoked for this partner.",
"retry_strategy": "contact_support",
"details": { "permission": "agent_api_permission" }
}
Sub-feature filtering. If the partner has API access but individual features are disabled, the platform excludes the gated endpoints from the tool list. Calling them would return 403 FEATURE_DISABLED:
| Feature Permission | Endpoints Hidden When Disabled |
|---|---|
loyalty_cards_permission |
All loyalty card and reward CRUD |
stamp_cards_permission |
All stamp card CRUD and stamp/redeem operations |
vouchers_permission |
All voucher CRUD and validate/redeem operations |
This means two partner keys with the same scopes but different plans may see different tool sets.
The /tools endpoint does not appear in its own tool list. A tool that lists tools adds noise for agents.
CLI Export
You can also export tool definitions via the command line:
# Default: generic format, partner role
php artisan agent:export-tools
# OpenAI format for admin endpoints
php artisan agent:export-tools openai --role=admin
# Claude format for member endpoints
php artisan agent:export-tools anthropic --role=member
# Read partner tools (no writes)
php artisan agent:export-tools generic --role=partner --scopes=read
Output is saved to storage/api-docs/agent-tools-{format}.json.
Caching
The platform caches tool definitions for 24 hours per unique combination of spec version, key role, scopes, feature permissions, and format. The cache invalidates when:
- The OpenAPI spec is regenerated (spec version/filemtime changes)
- A different combination of role + scopes + permissions is requested
No manual cache:clear is needed.
Error Responses
| Status | Code | Meaning |
|---|---|---|
| 403 | FEATURE_DISABLED |
Partner's Agent API access has been revoked (agent_api_permission = false). |
| 422 | INVALID_FORMAT |
Unsupported format parameter. Use: openai, anthropic, mcp, or generic. |
| 503 | TOOLS_UNAVAILABLE |
The OpenAPI spec has not been generated yet. Run php artisan l5-swagger:generate agent. |
Integration Example
A complete integration with OpenAI:
import openai, requests, json
AGENT_KEY = "rl_agent_..."
BASE_URL = "https://your-domain.com"
# 1. Discover tools (once at startup)
resp = requests.get(
f"{BASE_URL}/api/agent/v1/tools?format=openai",
headers={"X-Agent-Key": AGENT_KEY}
)
tools = resp.json()["tools"]
# 2. Pass to OpenAI
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Award 50 points to [email protected]"}],
tools=tools,
)
# 3. Execute the tool call
tool_call = response.choices[0].message.tool_calls[0]
fn = tool_call.function
desc = next(t["function"]["description"] for t in tools if t["function"]["name"] == fn.name)
http_line = [l for l in desc.split("\n") if l.startswith("HTTP:")][0]
method, path = http_line.replace("HTTP: ", "").split(" ", 1)
result = requests.request(
method=method,
url=f"{BASE_URL}{path}",
headers={"X-Agent-Key": AGENT_KEY},
json=json.loads(fn.arguments),
)
print(result.json())
Related Topics
- OpenAPI 3.0 Spec: Interactive Swagger UI for the Agent API (raw JSON)
- Endpoint Reference: Every endpoint across all roles in one page
- Scopes & Permissions: Permission levels and what each scope unlocks
- Authentication: How keys work, security model, and error handling