TypeScript SDK overview
The TypeScript SDK ships as @platools/sdk (packages/platools-js in the monorepo). It targets Node.js 20+, depends on zod + zod-to-json-schema for schema generation, and mirrors the Python SDK’s semantics 1:1 - same AuthLevel, same ToolRegistry, same WebSocket wire protocol.
Public surface
Everything at @platools/sdk:
import { Platools, type PlatoolsConfig, ToolRegistry, makeLocalContext, makeToolFactory, SchemaError, buildInputSchema, buildOutputSchema, buildSchemas, type AuthLevel, type JsonSchema, type ToolContext, type ToolDef, type ToolHandler, type ToolOptions, type ToolSchema,} from "@platools/sdk";Transport-layer helpers (PlatoolsClient, protocol messages) and doctor checks are also re-exported from the top-level entrypoint. See packages/platools-js/src/index.ts for the exhaustive list.
Why no decorator?
TC39 stage-3 decorators are still shifting under our feet (and the various transpilers disagree on runtime semantics). The TypeScript SDK uses a plain factory function instead - the same pattern tRPC, Hono, and Zod itself use:
import { z } from "zod";import { Platools } from "@platools/sdk";
export const platools = new Platools({ url: process.env.PLATOS_URL, secret: process.env.PLATOS_SECRET,});
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: z.object({ refundId: z.string(), amountCents: z.number().int(), }), auth: "user", roles: ["support", "admin"], }, async ({ orderId, reason }, ctx) => { // ctx.callId is populated by the transport layer at dispatch time return refundService.process(orderId, reason); },);The tool() method is generic over the Zod input / output types, so the handler’s params argument is fully inferred - no z.infer incantations on your side.
The Platools instance
import { Platools } from "@platools/sdk";
const platools = new Platools({ url: process.env.PLATOS_URL, secret: process.env.PLATOS_SECRET,});Each instance owns its own ToolRegistry. You typically create one per backend process and import it from every module that defines tools:
import { Platools } from "@platools/sdk";export const platools = new Platools();
// src/billing.tsimport { platools } from "./platools.js";export const listInvoices = platools.tool(/* ... */);Properties:
| Attribute | Type | Purpose |
|---|---|---|
platools.url | string | null | Platform base URL (constructor or PLATOS_URL). |
platools.secret | string | null | JWT-bearing secret (constructor or PLATOS_SECRET). |
platools.tool(...) | method | Register a tool and return the handler. |
platools.registry | ToolRegistry | Read-mostly - the doctor analyzer walks this. |
platools.tools | Record<string, ToolDef> | Read-only snapshot keyed by tool name. |
platools.getTool(name) | ToolDef | undefined | Convenience lookup. |
platools.getMcpSchemas() | ToolSchema[] | MCP-ready schemas for every tool. |
await platools.connect() | Promise<void> | Opens the outbound WebSocket - see Client. |
Browser / edge compatibility
process.env access is guarded - the SDK can be imported in browser and edge environments that don’t expose process, but connect() still requires explicit credentials in those environments. You usually won’t run the client half in a browser, but you can import types/schemas for shared contracts.
Next steps
- Tool factory - full
platools.tool()options and handler typing. - Schemas - Zod to JSON Schema conversion.
- Client - WebSocket transport, heartbeat, backoff.