Skip to content

stationeros/opentoolplane

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenToolplane v0.3.0

The control plane for safe, scalable agent tool and API execution.

OpenToolplane sits between an agent and the HTTP tools or APIs it calls. The agent decides what to do. OpenToolplane decides how that work runs in production: with retries, rate limits, idempotency, caching, async execution, quotas, and auditability.

It is built for the moment when agent workflows stop being demos and start behaving like real distributed systems. Instead of re-implementing execution policy inside every agent, you centralize it in one runtime.

When to use OpenToolplane

Use OpenToolplane when your agents need to:

  • fan out across many APIs without overwhelming downstream systems
  • make mutating calls safely with idempotency and audit logs
  • queue long-running work and deliver results via callbacks
  • enforce tenant-specific tool access and quotas
  • survive restarts without losing jobs, tool definitions, or runtime state

OpenToolplane is less useful if your system is a single-process demo with a small number of trusted internal tools and no multi-tenant or operational requirements.

Two-minute demo

The fastest way to understand OpenToolplane is to watch three behaviors:

  1. the same read is served from cache instead of hitting the provider twice
  2. the same write is replayed safely instead of creating a duplicate side effect
  3. a slow tool runs in the background and completes through a signed webhook callback

Start the stack:

docker compose up --build

In another terminal, run the guided demo:

python examples/clarity_demo.py

That script walks through the system and prints the evidence from the control plane and the mock downstream services.

For the demo narrative, see examples/CLARITY_DEMO.md.

What ships in v0.3.0

Reliability

  • durable async jobs stored in the service database
  • lease-based workers and webhook delivery recovery after process restart
  • retries with exponential backoff and jitter
  • circuit breaking
  • idempotency-key replay for mutating calls
  • short-lived caching for readonly calls

Control and isolation

  • shared rate limiting and concurrency control backed by the service database
  • persisted tool registry, so registered tools survive restart
  • API-key auth with admin and invoke scopes
  • tenant-aware tool visibility and quotas

Operations

  • signed webhook callbacks for async jobs
  • audit log for registration, invocation, jobs, and webhook delivery
  • /readyz and Prometheus /metrics
  • OpenTelemetry-compatible spans through opentelemetry-api

Integration

  • OpenAPI import with x-agent-policy

How it works

Agent
  -> OpenToolplane
      -> tool registry
      -> policy enforcement
      -> rate limits / concurrency / retries
      -> cache / idempotency / background jobs
      -> webhook delivery / audit / metrics / traces
  -> downstream HTTP tools and APIs

Production model

OpenToolplane uses a single durable database as its coordination layer.

That means:

  • jobs, callbacks, idempotency state, cache entries, and tool definitions survive restart
  • multiple app instances pointed at the same database can share execution budgets and safely claim work
  • no external queue is required for an initial production deployment

This release currently supports sqlite:/// database URLs. That makes it suitable for local use, pilots, and simple first deployments. The next scale-up step is the same coordination model backed by Postgres and Redis.

Quickstart

Local Python

python -m venv .venv
source .venv/bin/activate
pip install -e .

export OPENTOOLPLANE_POLICY_FILE=examples/policies.yaml
export OPENTOOLPLANE_AUTH_REQUIRED=true
export OPENTOOLPLANE_API_KEYS='[
  {"key_id":"admin","value":"otp-admin-local","scopes":["admin"]},
  {"key_id":"tenant-a","value":"otp-tenant-a-local","scopes":["invoke"],"tenant_id":"tenant-a"}
]'
export OPENTOOLPLANE_TENANT_QUOTAS='{
  "tenant-a": {"requests_per_minute": 300, "max_concurrent_requests": 10, "max_background_jobs": 25}
}'

uvicorn app.main:app --reload --port 8080

Run the example downstream services in separate terminals:

uvicorn examples.mock_provider:app --port 8090
uvicorn examples.mock_webhook_receiver:app --port 8091

Docker Compose

docker compose up --build

This starts:

  • OpenToolplane on http://localhost:8080
  • mock downstream provider on http://localhost:8090
  • mock webhook receiver on http://localhost:8091

Demo API keys in docker-compose.yml:

  • admin: otp-admin-local
  • tenant-a invoke: otp-tenant-a-local

Core API

Health and readiness

curl http://localhost:8080/healthz
curl http://localhost:8080/readyz

List visible tools

curl http://localhost:8080/v1/tools \
  -H 'x-api-key: otp-tenant-a-local'

Register a tool

curl -X POST http://localhost:8080/v1/tools \
  -H 'content-type: application/json' \
  -H 'x-api-key: otp-admin-local' \
  -d '{
    "name": "orders.create",
    "description": "Create an order",
    "request": {
      "method": "POST",
      "url_template": "http://localhost:8090/orders"
    },
    "policy": {
      "idempotent": true,
      "retry": {"max_attempts": 3}
    },
    "allowed_tenants": ["tenant-a"]
  }'

Invoke synchronously

curl -X POST http://localhost:8080/v1/tools/orders.create:invoke \
  -H 'content-type: application/json' \
  -H 'x-api-key: otp-tenant-a-local' \
  -d '{
    "body": {"sku": "sku-123", "quantity": 1},
    "idempotency_key": "order-123"
  }'

Invoke asynchronously with a signed callback

curl -X POST http://localhost:8080/v1/tools/reports.daily:invoke \
  -H 'content-type: application/json' \
  -H 'x-api-key: otp-tenant-a-local' \
  -d '{
    "async_mode": true,
    "callback": {
      "url": "http://localhost:8091/callback",
      "secret": "demo-secret",
      "headers": {"x-receiver": "demo"}
    }
  }'

Then inspect the job and callback delivery:

curl http://localhost:8080/v1/jobs/<job_id> \
  -H 'x-api-key: otp-tenant-a-local'

curl http://localhost:8080/v1/jobs/<job_id>/deliveries \
  -H 'x-api-key: otp-tenant-a-local'

Import tools from OpenAPI

Operations can include x-agent-policy and x-allowed-tenants.

curl -X POST http://localhost:8080/v1/tools/import/openapi \
  -H 'content-type: application/json' \
  -H 'x-api-key: otp-admin-local' \
  -d @examples/openapi-import-payload.json

View audit events

curl 'http://localhost:8080/v1/audit?limit=20' \
  -H 'x-api-key: otp-admin-local'

Scrape metrics

curl http://localhost:8080/metrics

Example policy file

tools:
  - name: catalog.get_item
    description: Fetch a catalog item
    request:
      method: GET
      url_template: http://localhost:8090/catalog/items/{item_id}
    policy:
      readonly: true
      timeout_ms: 3000
      concurrency:
        max_in_flight: 8
      rate_limit:
        enabled: true
        capacity: 16
        refill_per_second: 8
      caching:
        enabled: true
        ttl_seconds: 60
    allowed_tenants:
      - tenant-a

Repository layout

app/        FastAPI service, runtime, storage, auth, execution engine
examples/   guided demo, mock provider, webhook receiver, sample policy and OpenAPI payload
issues/     backlog as issue-style markdown files
tests/      API and runtime tests

Run tests

pytest -q

Packages

 
 
 

Contributors