Skip to content

Python SDK overview

The Python SDK ships as the platools package (packages/platools-py in the monorepo). It targets Python 3.10+ and depends on Pydantic v2, docstring-parser, and websockets.

Public surface

Everything you need lives at the top level:

from platools import (
Platools, # the per-app instance (decorator + registry + client)
PlatoolsClient, # advanced: manual WebSocket client
ToolDef, # runtime tool descriptor (frozen dataclass)
ToolSchema, # MCP-compliant schema dataclass
AuthLevel, # Literal["none", "user", "admin"]
SchemaError, # raised for missing type hints / bad signatures
)

All of these are declared in platools/__init__.py::__all__. Keep your imports at the top-level platools module so future refactors inside core/, transport/, etc. don’t break your code.

The Platools instance

from platools import Platools
platools = Platools(
url="https://platform.example.com", # or $PLATOS_URL
secret="platos_agent_...", # or $PLATOS_SECRET
)

Each Platools() owns its own ToolRegistry. Create one per backend process and import it from every module that defines tools:

my_app/core.py
from platools import Platools
platools = Platools()
# my_app/billing.py
from my_app.core import platools
@platools.tool()
def list_invoices(customer_id: str) -> list[Invoice]: ...

Properties you can read:

AttributeTypePurpose
platools.urlstr | NonePlatform base URL (constructor or $PLATOS_URL).
platools.secretstr | NoneJWT-bearing secret (constructor or $PLATOS_SECRET).
platools.tooldecorator factory@platools.tool(...) - see Decorator.
platools.registryToolRegistryRead-mostly. The doctor CLI walks this.
platools.toolsdict[str, ToolDef]Read-only snapshot keyed by tool name.
platools.get_tool(name)ToolDef | NoneConvenience lookup.
platools.get_mcp_schemas()list[ToolSchema]MCP-ready schemas for every tool.
await platools.connect()coroutineOpens the outbound WebSocket. See Client.

What happens when you decorate

The @platools.tool() decorator:

  1. Validates the options (auth in {none, user, admin}, timeout > 0).
  2. Builds the input schema via platools.core.schema.build_input_schema(). A Pydantic model is created from the function’s type hints, docstring param descriptions feed Field(description=...), and model_json_schema() produces the final dict.
  3. Builds an output schema from the return annotation (if any).
  4. Creates a ToolDef and registers it on the instance’s ToolRegistry. Duplicate names raise ValueError.
  5. Returns the original function unchanged. Decorated tools remain directly callable in user code. Unit tests don’t have to go through the registry.

See Decorator and Schemas for details.

SDK architecture

platools/
├── __init__.py # Platools class + public exports
├── types.py # AuthLevel, ToolDef, ToolSchema (frozen dataclasses)
├── core/
│ ├── decorator.py # @platools.tool() factory
│ ├── registry.py # ToolRegistry (dict[str, ToolDef] wrapper)
│ └── schema.py # type hints -> Pydantic model -> JSON Schema
├── transport/
│ ├── client.py # PlatoolsClient (WebSocket + heartbeat + backoff)
│ └── protocol.py # wire messages (Pydantic models)
├── serve/
│ ├── dispatcher.py # JSON-RPC -> tool call dispatcher
│ ├── http.py # stdlib-only HTTP/1.1 transport for platools serve
│ └── stdio.py # stdio transport
├── doctor/
│ ├── analyzer.py # static analysis of a ToolRegistry
│ ├── checks.py # individual lint rules
│ └── reporter.py # human-readable / JSON output
├── testing/
│ ├── runner.py # ToolTestRunner + BatchResult + latency stats
│ └── mock_client.py # in-process mock for unit tests
└── cli/
├── __init__.py # platools entrypoint
├── doctor.py # platools doctor
├── test.py # platools test
└── serve.py # platools serve

Next steps

  • Decorator - full @platools.tool() keyword API.
  • Schemas - how type hints become JSON Schema.
  • Client - WebSocket connection lifecycle, heartbeat, backoff.
  • CLI - doctor, test, and serve subcommands.