Getting Started
Install the SDK, decorate one function, run platools doctor, and connect to the platform. Five minutes, zero boilerplate.
Install
Python
# From PyPI (when published)pip install platools
# Or from source inside the monorepopip install -e packages/platools-pyPython 3.10+ is required. See packages/platools-py/pyproject.toml for the full dependency list.
TypeScript
# From npm (when published)pnpm add @platools/sdk zod
# Or workspace-link inside the monorepopnpm add @platools/sdk@workspace:* zodNode.js 20+ is required. zod is a peer dependency - you pass Zod schemas directly to platools.tool().
Decorate your first tool (Python)
from typing import Annotated
from platools import Platoolsfrom pydantic import BaseModel, Field
platools = Platools() # reads PLATOS_URL / PLATOS_SECRET from env
class RefundResult(BaseModel): refund_id: str amount_cents: int
_USER = {"x-user-providable": True}
@platools.tool(auth="user", roles=["support", "admin"])def process_refund( order_id: Annotated[ str, Field(description="The order ID to refund.", json_schema_extra=_USER), ], reason: Annotated[ str, Field( description="Reason for the refund, surfaced in the audit log.", json_schema_extra=_USER, ), ],) -> RefundResult: """Process a refund for an order.""" # ... your real refund logic here ... return RefundResult(refund_id="rf_123", amount_cents=4999)The decorator introspects process_refund, builds an MCP-compliant input schema from the type hints, and registers the tool with the Platools instance. The function itself is returned unchanged, so unit tests can still call it directly.
Decorate your first tool (TypeScript)
import { z } from "zod";import { Platools } from "@platools/sdk";
export const platools = new Platools();
const RefundResult = z.object({ refundId: z.string(), amountCents: z.number().int(),});
export const processRefund = platools.tool( { name: "process_refund", description: "Process a refund for an order", input: z.object({ orderId: z.string(), reason: z.string(), }), output: RefundResult, auth: "user", roles: ["support", "admin"], }, async ({ orderId, reason }) => { // ... your real refund logic here ... return { refundId: "rf_123", amountCents: 4999 }; },);Same mental model as the Python SDK: a single registration call, the handler is returned untouched, and the SDK stores a ToolDef on the Platools instance’s registry.
Run the ship gate
# Pythonplatools doctor my_app.tools
# TypeScript - point at the *built* barrel module that imports your toolspnpm exec platools doctor ./dist/registry.jsplatools doctor imports your tool module, walks every Platools instance it finds, and statically analyzes the registry:
- Every parameter has a type hint + description
- Auth levels match the sensitivity of the operation
- Output schemas exist (so agents know what they’re getting)
- No unreachable / shadowed parameters
- No destructive tools missing annotations
Doctor exits non-zero on any error-severity finding. Wire it into CI.
Connect to the platform
When you’re ready to expose tools to a real agent:
import asynciofrom my_app.tools import platools
asyncio.run(platools.connect()) # opens outbound WebSocket, runs foreverimport { platools } from "./tools.js";
await platools.connect();The client opens a WebSocket to ${PLATOS_URL}/ws/sdk, sends the JWT from PLATOS_SECRET, registers the tool schemas, and starts dispatching calls. Heartbeat every 30s, exponential-backoff reconnect on drop. No open ports required on your side.
Local MCP mode (no platform)
Test with Claude Desktop or Cursor without deploying Platos:
platools serve --module my_app.toolsThis starts a local MCP server on stdio that exposes your registry to any MCP client. See Local mode for the full walkthrough.
Next steps
- Python SDK - full decorator API, schema generation, client lifecycle, CLI commands.
- TypeScript SDK - Zod-native tool factory, client, doctor.
- Examples - runnable reference integrations you can clone and modify.
- Local mode - develop tools locally with Claude Desktop or Cursor.