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.
python3 --version
# Python 3.10+
Installation
Create a virtual environment and install from TestPyPI.
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.
JAATO_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-api03-...your-key
MODEL_NAME=claude-sonnet-4-20250514
JAATO_PROVIDER=google_genai
GOOGLE_GENAI_API_KEY=AIza...your-key
MODEL_NAME=gemini-2.5-flash
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 --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 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
- Import
JaatoClient - Create client and call
connect() - Use
generate()for text completion
connect() resolves configuration from environment variables:
JAATO_PROVIDER— selects the providerMODEL_NAME— selects the model- Provider-specific vars (e.g.,
ANTHROPIC_API_KEY,OLLAMA_MODEL) — authentication and overrides
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.
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.
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
- Create a
PluginRegistrywith the model name - Call
discover()to find available plugins - Use
expose_tool()to enable specific plugins — it takes a plugin name (e.g."cli","web_search"), not an individual tool name - Call
configure_tools()on the client - 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
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}")
[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 historyreset_session()- clear history and start freshreset_session(history)- reset with modified history
# 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:
- JaatoClient API - Full method reference
- PluginRegistry - Managing plugins
- Building Plugins - Create custom tools
- MCP Integration - Connect MCP servers
- Permissions - Control tool execution
- Connection Recovery - Handle disconnects and resume sessions
# 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