SDKs & toolsAgent SDK

Agent SDK

⚠️
Preview — this feature is being built; the design below is the spec.

The Syntic Agent SDK is a library for building your own agents in Python or TypeScript. It gives you the parts every agent needs (the model loop, tools, permission checks, context management, sub-agents and session storage) so you write only what is specific to your agent: its instructions and its tools.

It works in two modes with the same code:

ModeWhere the loop runsWhere your tools runUse it when
LocalIn your processIn your processTools must touch your files, network or databases directly; you want full control.
ManagedOn Syntic, as a sessionSyntic-run tools on Syntic; your function tools in your process, called back over the session streamYou want Syntic to keep history, run built-in tools and environments, and show runs in the Console.

Switching modes is one constructor argument.

Where it fits

You wantUse
A single model call, or a simple tool loopThe Python or TypeScript SDK’s tool runner
An agent defined and run entirely on Syntic, with no code of your own in the loopAgent definitions and sessions through the API
A custom agent with your own tools, permission rules, hooks and sub-agentsThe Agent SDK
A ready-made coding agentSyntic Code

Install

pip install syntic-agents          # Python 3.10+
npm install @syntic/agents         # Node.js 18+

Both depend on the matching Syntic SDK and read SYNTIC_API_KEY.

A first agent

import asyncio
from syntic_agents import Agent, tool, Permissions
 
@tool
async def read_invoice(invoice_id: str) -> dict:
    """Return an invoice's lines and totals."""
    return await billing_db.fetch_invoice(invoice_id)
 
@tool(requires_approval=True)
async def issue_credit(invoice_id: str, amount: float, reason: str) -> str:
    """Issue a credit note against an invoice."""
    return await billing_db.credit(invoice_id, amount, reason)
 
agent = Agent(
    name="billing-helper",
    model="Amara S1.2",
    instructions="You answer billing questions. Check the invoice before replying.",
    tools=[read_invoice, issue_credit],
    permissions=Permissions(ask=["issue_credit"]),   # pause for a person before crediting
    max_turns=20,
)
 
async def main():
    async for event in agent.run("Customer says invoice INV-2201 double-charged shipping."):
        if event.type == "approval.required":
            await event.approve() if input(f"Allow {event.name}{event.arguments}? [y/N] ") == "y" else await event.reject("Declined")
        elif event.type == "message.completed":
            print(event.content)
 
asyncio.run(main())

To run the same agent in managed mode, add mode="managed" (Python) or mode: 'managed' (TypeScript). The SDK creates or updates the agent definition by name, starts a session, and runs your function tools locally when the session asks for them.

Building blocks

PieceWhat it does
AgentModel, instructions, tools, limits. run() yields typed events; run_sync() / runToCompletion() returns the final result.
toolTurns a function into a tool; schema from type hints (Python) or Zod / JSON Schema (TypeScript). Arguments are validated before your function runs.
Built-in toolsweb_search, web_fetch, code_execution (Syntic-run), plus local read_file, write_file, edit_file, shell and glob limited to a working directory you choose.
PermissionsRules per tool: allow, ask, deny, with argument patterns (for example allow shell only for git status and npm test). Rules are checked before every tool call.
HooksFunctions called before and after each tool call and model call, to log, change arguments, or block.
Sub-agentsAn agent can be given other agents as tools; each runs with its own instructions, tools and context, and returns a summary.
Context managementLong runs are kept inside the context window by summarising old turns and trimming large tool results, at a threshold you set.
Sessionsagent.run(..., session=store) saves history to a store (in memory, a local file, or Syntic in managed mode) so a later run resumes the conversation.
MCPAttach remote MCP servers by URL; their tools appear alongside yours.
TracingEach run produces a trace with every model call, tool call, approval and token count. In managed mode it is also an execution in the Console.

Events

run() yields the same event types as the session stream, in both modes: status.changed, message.delta, message.completed, tool_call.started, tool_call.completed, approval.required, usage.updated and error. Code written against local mode keeps working in managed mode.

Errors and limits

  • API errors surface as the SDK’s typed exceptions (see Python SDK).
  • A run stops with a final status.changed whose outcome is max_turns, budget_exhausted or error when a limit is hit; it never stops silently.
  • max_turns, max_output_tokens_per_turn and a token or cost budget are set on the agent and can be lowered per run.
  • Billing is the same as any API traffic: the model tokens each run uses, plus built-in tool fees.

Build notes for the platform team

  • Depends on managed agents, sessions and the native SDKs; build after those are stable.
  • The event types are the session stream’s types, so one set of SDK types covers both modes.
  • A later phase can expose the Syntic Code engine itself as a library behind the same interface; design the Agent surface so that swap is invisible to users.
Was this page helpful?