Skip to main content

Overview

Shannon can automatically generate tools from OpenAPI 3.x specifications, allowing you to integrate any REST API without writing code. The OpenAPI loader:
  • ✅ Parses OpenAPI 3.0/3.1 specs
  • ✅ Generates one tool per operation
  • ✅ Handles authentication (Bearer, API Key, Basic)
  • ✅ Supports path/query/header parameters
  • ✅ Includes circuit breaker and rate limiting
  • ✅ Validates requests against schema
  • ✅ Resolves $ref references locally
Quick Start: See Adding Custom Tools Guide for step-by-step instructions.

Configuration Reference

Basic Configuration

# config/shannon.yaml or config/overlays/shannon.myvendor.yaml
openapi_tools:
  tool_collection_name:
    enabled: true | false                   # Enable/disable this tool collection
    spec_path: string                       # Path to OpenAPI spec file
    spec_url: string                        # OR: URL to fetch spec from
    spec_inline: string                     # OR: Inline YAML/JSON spec

    auth_type: none | api_key | bearer | basic
    auth_config: object                     # Auth configuration (see below)

    category: string                        # Tool category (e.g., "analytics", "data")
    base_cost_per_use: float               # Estimated cost per operation (USD)
    rate_limit: integer                     # Requests per minute (default: 30)
    timeout_seconds: float                  # Request timeout (default: 30)
    max_response_bytes: integer             # Max response size (default: 10MB)

    operations: [string]                    # Optional: Filter by operationId
    tags: [string]                          # Optional: Filter by tags
    base_url: string                        # Optional: Override spec's base URL

Field Descriptions

FieldTypeRequiredDefaultDescription
enabledbooleanYes-Enable this tool collection
spec_pathstringOne of spec_*-Local file path to OpenAPI spec
spec_urlstringOne of spec_*-URL to fetch spec from (supports relative server URLs)
spec_inlinestringOne of spec_*-Inline YAML/JSON spec content
auth_typeenumYesnoneAuthentication method
auth_configobjectIf auth ≠ none{}Auth configuration (varies by type)
categorystringNo"api"Tool category for organization
base_cost_per_usefloatNo0.001Estimated cost per invocation (USD)
rate_limitintegerNo30Max requests per minute per tool
timeout_secondsfloatNo30.0HTTP request timeout
max_response_bytesintegerNo10485760Max response size (10MB)
operationsarray[string]NoAllFilter to specific operationIds
tagsarray[string]NoAllFilter operations by tags
base_urlstringNoFrom specOverride API base URL

Authentication Types

No Authentication

openapi_tools:
  public_api:
    enabled: true
    spec_url: https://api.example.com/openapi.json
    auth_type: none

Bearer Token

Used by: GitHub, GitLab, most modern APIs
openapi_tools:
  github_api:
    enabled: true
    spec_url: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json
    auth_type: bearer
    auth_config:
      token: "${GITHUB_TOKEN}"              # From environment variable
      # OR:
      # token: "ghp_xxxxxxxxxxxxx"          # Hardcoded (not recommended)
Environment Variable:
GITHUB_TOKEN=ghp_your_token_here
Headers sent:
Authorization: Bearer ghp_your_token_here

API Key in Header

Used by: OpenAI, Anthropic, many SaaS APIs
openapi_tools:
  openai_api:
    enabled: true
    spec_url: https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml
    auth_type: api_key
    auth_config:
      api_key_name: Authorization                # Header name
      api_key_location: header                   # "header" or "query"
      api_key_value: "Bearer ${OPENAI_API_KEY}"  # Value with prefix
Environment Variable:
OPENAI_API_KEY=sk-your-key-here
Headers sent:
Authorization: Bearer sk-your-key-here

API Key in Query Parameter

Used by: OpenWeather, some legacy APIs
openapi_tools:
  weather_api:
    enabled: true
    spec_url: https://api.openweathermap.org/data/3.0/openapi.json
    auth_type: api_key
    auth_config:
      api_key_name: appid                   # Query param name
      api_key_location: query               # "query" not "header"
      api_key_value: "${OPENWEATHER_KEY}"
Request URL:
GET https://api.openweathermap.org/data/3.0/weather?appid=your_key_here&q=London

Basic Authentication

Used by: Legacy APIs, internal services
openapi_tools:
  legacy_api:
    enabled: true
    spec_path: config/openapi_specs/legacy_api.yaml
    auth_type: basic
    auth_config:
      username: "${API_USERNAME}"
      password: "${API_PASSWORD}"
Environment Variables:
API_USERNAME=admin
API_PASSWORD=secret123
Headers sent:
Authorization: Basic YWRtaW46c2VjcmV0MTIz

Custom Headers

For vendor-specific authentication:
openapi_tools:
  custom_api:
    enabled: true
    spec_url: https://api.custom.com/v1/openapi.json
    auth_type: api_key
    auth_config:
      vendor: myvendor                      # Triggers vendor adapter
      api_key_name: X-API-Key
      api_key_location: header
      api_key_value: "${CUSTOM_API_KEY}"
      extra_headers:
        X-Account-ID: "{{body.account_id}}"  # Dynamic from request body
        X-User-ID: "${CUSTOM_USER_ID}"       # Static from environment
        X-Timestamp: "{{timestamp}}"          # Dynamic template
Dynamic Header Templates:
  • "${ENV_VAR}" - Resolved from environment
  • "{{body.field}}" - Resolved from request body at runtime
  • Static strings - Used as-is

Advanced Features

Operation Filtering

By operationId (recommended):
openapi_tools:
  petstore:
    spec_url: https://petstore3.swagger.io/api/v3/openapi.json
    operations:
      - getPetById              # Only generate tool for this operation
      - findPetsByStatus
      - addPet
By tags:
openapi_tools:
  petstore:
    spec_url: https://petstore3.swagger.io/api/v3/openapi.json
    tags:
      - pet                     # Only operations tagged "pet"
      - user

Base URL Override

Override the base URL from the spec:
openapi_tools:
  api_staging:
    spec_url: https://api.example.com/openapi.json
    base_url: https://staging-api.example.com  # Use staging instead
Use cases:
  • Testing against staging/dev environments
  • Internal proxies or gateways
  • Local development

Rate Limiting

Protect external APIs from overload:
openapi_tools:
  expensive_api:
    spec_url: https://expensive.api.com/openapi.json
    rate_limit: 10              # Only 10 requests per minute
    timeout_seconds: 60         # 60 second timeout for slow operations
Per-tool limits: Each operation generated from the spec inherits this limit. Behavior:
  • Enforced via token bucket algorithm
  • Shared across all tool instances (single Shannon instance)
  • Returns error if limit exceeded

Circuit Breaker

Automatic failure protection: Configuration (via environment):
# Circuit breaker opens after 5 failures
# Stays open for 60 seconds before allowing retry
# (These are defaults, not directly configurable per tool yet)
States:
  1. Closed (normal): All requests pass through
  2. Open (failing): All requests immediately rejected
  3. Half-open (testing): One trial request allowed
Behavior:
  • After 5 consecutive failures → opens circuit
  • Circuit stays open for 60 seconds
  • Then allows one trial request (half-open)
  • Success → closes circuit
  • Failure → reopens for another 60 seconds

Response Size Limits

Prevent memory exhaustion:
openapi_tools:
  api_with_large_responses:
    spec_url: https://api.example.com/openapi.json
    max_response_bytes: 52428800  # 50MB limit
Behavior:
  • Responses larger than limit are truncated
  • Error returned with truncated marker

Troubleshooting

Tool Not Registered

Symptom: Tool doesn’t appear in /tools/list Debug:
# 1. Check if spec is valid
curl -X POST http://localhost:8000/tools/openapi/validate \
  -H "Content-Type: application/json" \
  -d '{"spec_url": "https://api.example.com/openapi.json"}'

# 2. Check logs
docker logs shannon-llm-service-1 | grep -i "openapi"

# 3. Verify enabled flag
grep -A 5 "my_tool" config/shannon.yaml | grep enabled
Common causes:
  • enabled: false in config
  • Invalid OpenAPI spec
  • Domain not in OPENAPI_ALLOWED_DOMAINS
  • Spec fetch timeout
  • Circular $ref references

Domain Validation Error

Symptom: URL host 'example.com' not in allowed domains Fix:
# Development: Allow all
OPENAPI_ALLOWED_DOMAINS=*

# Production: Specific domains
OPENAPI_ALLOWED_DOMAINS=api.github.com,api.example.com,api.partner.com
In docker-compose.yml:
services:
  llm-service:
    environment:
      - OPENAPI_ALLOWED_DOMAINS=api.example.com

Spec Fetch Timeout

Symptom: Failed to fetch OpenAPI spec: timeout Fix:
# Increase timeout (default: 30s)
OPENAPI_FETCH_TIMEOUT=60

# Or use local spec file instead
openapi_tools:
  my_api:
    spec_path: config/openapi_specs/my_api.yaml  # Local file

Circuit Breaker Triggered

Symptom: Circuit breaker open for https://api.example.com Debug:
# Check recent errors
docker logs shannon-llm-service-1 --tail 100 | grep -i "circuit\|failure"
Fix:
  • Wait 60 seconds for automatic recovery
  • Fix underlying API issues
  • Increase timeout if API is slow:
    timeout_seconds: 60
    

Rate Limit Exceeded

Symptom: Rate limit exceeded for tool my_tool Fix:
# Increase limit
openapi_tools:
  my_api:
    rate_limit: 120  # 120 requests per minute

Authentication Failures

Symptom: 401 Unauthorized or 403 Forbidden Debug:
# Check environment variables
env | grep API_KEY

# Test token manually
curl -H "Authorization: Bearer $YOUR_TOKEN" https://api.example.com/endpoint
Common causes:
  • Environment variable not set
  • Token expired
  • Wrong auth type (should be bearer not api_key)
  • Missing Bearer prefix for API key auth

Examples

Example 1: GitHub API

openapi_tools:
  github:
    enabled: true
    spec_url: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json
    auth_type: bearer
    auth_config:
      token: "${GITHUB_TOKEN}"
    category: development
    rate_limit: 60
    operations:
      - repos/get
      - repos/list-for-user
      - issues/list-for-repo
Usage:
./scripts/submit_task.sh "List all repositories for user octocat"

Example 2: OpenWeather API

openapi_tools:
  openweather:
    enabled: true
    spec_url: https://api.openweathermap.org/data/3.0/openapi.json
    auth_type: api_key
    auth_config:
      api_key_name: appid
      api_key_location: query
      api_key_value: "${OPENWEATHER_API_KEY}"
    category: data
    rate_limit: 60
Usage:
./scripts/submit_task.sh "What's the weather forecast for Tokyo?"

Example 3: Internal API with Vendor Adapter

openapi_tools:
  internal_analytics:
    enabled: true
    spec_path: config/openapi_specs/internal_analytics.yaml
    auth_type: bearer
    auth_config:
      vendor: mycompany                     # Triggers vendor adapter
      token: "${ANALYTICS_API_TOKEN}"
      extra_headers:
        X-Tenant-ID: "{{body.tenant_id}}"
    category: analytics
    base_cost_per_use: 0.005
Vendor Adapter (python/llm-service/llm_service/tools/vendor_adapters/mycompany.py):
class MyCompanyAdapter:
    def transform_body(self, body, operation_id, prompt_params):
        # Add tenant context
        if prompt_params and "tenant_id" in prompt_params:
            body["tenant_id"] = prompt_params["tenant_id"]

        # Transform metric names
        if "metrics" in body:
            body["metrics"] = [f"myco:{m}" for m in body["metrics"]]

        return body

Security Best Practices

# ❌ Bad
auth_config:
  token: "sk-1234567890"

# ✅ Good
auth_config:
  token: "${API_TOKEN}"
# Production
OPENAPI_ALLOWED_DOMAINS=api.trusted.com,api.partner.com

# Not: OPENAPI_ALLOWED_DOMAINS=*
rate_limit: 60  # Match your API provider's limits
  • Shannon automatically upgrades HTTP to HTTPS for external APIs
  • localhost/127.0.0.1 allowed on HTTP for development
max_response_bytes: 10485760  # 10MB default

See Also

Quick Reference

# Validate spec
curl -X POST http://localhost:8000/tools/openapi/validate \
  -d '{"spec_url": "https://api.example.com/openapi.json"}' | jq .

# List registered tools
curl http://localhost:8000/tools/list | jq .

# Get tool schema
curl http://localhost:8000/tools/myTool/schema | jq .

# Execute tool
curl -X POST http://localhost:8000/tools/execute \
  -d '{"tool_name":"myTool","parameters":{...}}' | jq .

# Test via workflow
./scripts/submit_task.sh "Your query using the tool"
Need Help?