Quickstart

Get up and running with jaato in 5 minutes. This guide covers installation, basic setup, and sending your first message with tool execution.

Requirements

  • Python 3.10 or higher
  • Credentials for at least one AI provider (see below)

Choose a Provider

jaato reaches a wide range of model providers through one uniform interface. They group by how the model runs:

  • Hosted API providers — cloud services you reach with an API key or OAuth (e.g. Anthropic, Google GenAI).
  • Local runtimes — models on your own hardware, no API cost (e.g. Ollama, vLLM).
  • Unified gateways — a single endpoint fronting many vendors' models (e.g. OpenRouter).
  • Subscription-based — drive an existing Claude Pro/Max or Google subscription (e.g. Claude CLI, Antigravity).

See the Providers Reference for the complete list of supported providers and per-provider setup — the sidebar links each one directly.

Check Python version
python3 --version
# Python 3.10+

Installation

Create a virtual environment and install from TestPyPI.

Terminal
python3 -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

pip install \
  --extra-index-url https://test.pypi.org/simple/ \
  jaato-sdk jaato-server jaato-tui

Environment Setup

Create a .env file with your credentials. The provider auto-detects the endpoint based on your configuration.

.env (Anthropic)
JAATO_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-api03-...your-key
MODEL_NAME=claude-sonnet-4-20250514
.env (Google GenAI)
JAATO_PROVIDER=google_genai
GOOGLE_GENAI_API_KEY=AIza...your-key
MODEL_NAME=gemini-2.5-flash
.env (Ollama - local)
JAATO_PROVIDER=ollama
MODEL_NAME=qwen3:32b
# Requires: ollama serve && ollama pull qwen3:32b

Developer Tooling

Two console scripts help you build and debug clients and agent profiles against the installed framework, so they can't drift from the code.

jaato-doctor (ships with jaato-sdk) is a client preflight — run it before your client calls connect(). It checks whether server is importable (autostart), the socket is listening or stale, the daemon's HOME vs yours (why pass:// secrets resolve from the wrong store), the env_file, and where profiles/logs land. Non-zero exit on any failure, so it doubles as a CI gate.

jaato-scaffold (ships with jaato-server) interrogates, validates, and scaffolds: explain reads the live plugin/provider registry, validate lints a profile against it, and new generates a starting client or cascade — the source of truth for current patterns.

For runtime failures, jaato-scaffold explain runtime is the map — the session/runner entities, how the workspace flows from client to plugin, and where each log lands — and jaato-doctor --session <id|latest> applies it to a live session, reporting whether its runner-tier path plugins resolved the workspace or got workspace=none (path tools denied). That turns a manual log hunt into one command.

jaato-doctor — client preflight
jaato-doctor --workspace . --env-file .env
# non-zero exit on any FAIL → usable as a gate
jaato-doctor --session latest --workspace .  # debug a RUNNING session:
# did its runner-tier path plugins resolve the workspace, or get
# workspace=none (→ readFile/file_edit/cli Permission-denied)?
# (equivalent: python -m jaato_sdk.doctor)
jaato-scaffold — interrogate / validate / scaffold
jaato-scaffold explain                 # plugins · providers · gc · archetypes
jaato-scaffold explain provider anthropic   # capabilities · knobs · quirks
jaato-scaffold explain profile         # the agent-profile schema, field by field
jaato-scaffold explain runtime         # session/runner entities · workspace flow · log map
jaato-scaffold validate my-profile.yaml     # lint vs the live registry
jaato-scaffold new client --workspace . --provider anthropic --model claude-sonnet-4-20250514
# (equivalent: python -m shared.scaffold ...)

Basic Usage

Create a client, connect, and send a message. When called without arguments, connect() reads JAATO_PROVIDER and MODEL_NAME from your environment (or .env file).

Steps

  1. Import JaatoClient
  2. Create client and call connect()
  3. Use generate() for text completion
Environment Variable Resolution
connect() resolves configuration from environment variables:
  • JAATO_PROVIDER — selects the provider
  • MODEL_NAME — selects the model
  • Provider-specific vars (e.g., ANTHROPIC_API_KEY, OLLAMA_MODEL) — authentication and overrides
basic_example.py
from jaato import JaatoClient

client = JaatoClient()
client.connect()  # Reads JAATO_PROVIDER and MODEL_NAME from env

response = client.generate("What is 2 + 2?")
print(response)  # "4"

Talking to a Daemon (recommended)

The example above runs the agent runtime in-process with JaatoClient. The recommended Python entry path instead talks to a long-lived jaato daemon over IPC, using the async IPCClient facade (full reference).

IPCClient.session(...) is an async context manager: on entry it connects to (or auto-starts) the daemon and creates a session; on exit it disconnects. The yielded Session owns the send-and-wait recipe, so await s.ask(...) returns the answer without any event plumbing. For a throwaway call, the module-level ask() opens a session, asks, and tears down in one line.

One facade, three transports
The same Session facade (s.ask / s.complete / s.stream) runs three ways via jaato.session(mode=...)mode is the only thing that changes: mode="in_process" (embedded; the agent runs in your process with no daemon — InProcessClient), mode="ipc" (a local daemon over a Unix socket — IPCClient, shown here), and mode="ws" (a remote daemon over ws:// / wss://WSClient, needs the jaato-sdk[ws] extra). An inline profile spec requires a plugins key (use [] for a tool-less session); credentials resolve from a pass: knob, never a raw key or env var.

On the daemon transports, add recovery=True for the auto-reconnect client (IPCRecoveryClient / WSRecoveryClient; mode="in_process" rejects it). For a self-signed wss:// cert, pass ca= (a CA-bundle path) on mode="ws". A non-terminal client (chat / web) can pass presentation= (a PresentationContext or dict) to replace the default terminal display context so the model adapts its output.
daemon_facade.py
import asyncio
from jaato_sdk import IPCClient, ask

async def main():
    # Open a session, ask, tear down on exit.
    async with IPCClient.session(profile={
        "model": "claude-sonnet-4-20250514",
        "provider": "anthropic",
        "plugins": [],
        "plugin_configs": {
            "anthropic": {"api_key": "pass://jaato/anthropic/api-key"},
        },
    }) as s:
        print(await s.ask("What is 2 + 2?"))   # "4"

    # ...or the one-shot helper, for a throwaway call.
    print(await ask("What is 2 + 2?", profile={
        "model": "claude-sonnet-4-20250514",
        "provider": "anthropic",
        "plugins": [],
        "plugin_configs": {
            "anthropic": {"api_key": "pass://jaato/anthropic/api-key"},
        },
    }))

# Transport-agnostic entry — same spec + same s.ask/.complete/.stream,
# flip the transport with mode:
import jaato
#   jaato.session(mode="in_process", profile={...})   # embedded, no daemon
#   jaato.session(mode="ipc",        profile={...})   # local daemon (above)
#   jaato.session(mode="ws", url="wss://host:8080", token="...", profile={...})
# Add recovery=True (mode ipc/ws) for the auto-reconnect client; ca="ca.pem"
# (mode ws) to trust a self-signed wss:// cert.

asyncio.run(main())

Adding Tools

jaato becomes powerful when you give the model access to tools. The PluginRegistry discovers and manages tool plugins.

Steps

  1. Create a PluginRegistry with the model name
  2. Call discover() to find available plugins
  3. Use expose_tool() to enable specific plugins — it takes a plugin name (e.g. "cli", "web_search"), not an individual tool name
  4. Call configure_tools() on the client
  5. Use send_message() with an output callback

Output Callback

The callback receives three arguments for each output event:

  • source str
    "model" for model output, plugin name for tool output
  • text str
    The output text
  • mode str
    "write" for new block, "append" to continue
with_tools.py
from jaato import JaatoClient, PluginRegistry

client = JaatoClient()
client.connect()  # Reads JAATO_PROVIDER and MODEL_NAME from env

# Discover and expose plugins
registry = PluginRegistry(model_name=client.model_name)
registry.discover()

# Expose the CLI plugin for shell commands
registry.expose_tool("cli")

# Configure tools on the client
client.configure_tools(registry)

# Define output callback
def on_output(source, text, mode):
    prefix = f"[{source}] " if mode == "write" else ""
    print(f"{prefix}{text}", end="")

# Send message - model can now use CLI tools
response = client.send_message(
    "List Python files in the current directory",
    on_output=on_output
)

print(f"\n\nFinal: {response}")
Output
[model] I'll list the Python files for you.
[cli] cli_mcp_harness.py
test_vertex.py
...
[model] I found several Python files in the directory.

Final: I found several Python files...

Multi-turn Conversations

The client maintains conversation history internally. Each call to send_message() adds to the history, enabling multi-turn conversations with context.

History Management

  • get_history() - retrieve conversation history
  • reset_session() - clear history and start fresh
  • reset_session(history) - reset with modified history
multi_turn.py
# First message
response = client.send_message(
    "What files are in this directory?",
    on_output=on_output
)

# Follow-up (client remembers context)
response = client.send_message(
    "Which one is the largest?",
    on_output=on_output
)

# Check history
history = client.get_history()
print(f"Conversation has {len(history)} messages")

# Start fresh
client.reset_session()

Next Steps

Now that you have the basics working, explore these topics to get the most out of jaato:

Available built-in plugins
# List available plugins
registry = PluginRegistry()
registry.discover()

for name in registry.list_available():
    print(name)

# Common plugins:
# - cli: Execute shell commands
# - mcp: Model Context Protocol tools
# - file_edit: File editing
# - todo: Task management
# - web_search: Web search
# - permission: Permission control