Python client SDK for the NATS Agent Protocol — discover and prompt spec-compliant agents over NATS
Project description
synadia-ai-agents
Python client SDK for the NATS Agent Protocol. Discover protocol-compliant agents over NATS and prompt them with streamed typed responses.
Hosting an agent (Hermes / claude-code / openclaw / pi)? That side
of the protocol now ships separately as
synadia-ai-agent-service (import
synadia_ai.agent_service). It depends on this package for the
shared wire primitives — install both when authoring an agent
harness.
Cross-SDK parity with the TypeScript SDK
is tracked in tests/test_interop_e2e.py.
Both SDKs declare protocol_version = "0.3" in service metadata, so the
test spawns the TS reference agent via bun and rounds-trips a prompt
through it. The test pytest.skips cleanly when bun or the sibling
../typescript/ checkout is missing — running the suite without TS
interop is fine for day-to-day work.
Calling agents? → Quickstart - call an agent.
Hosting an agent? → see synadia-ai-agent-service.
Installation
From this checkout (no published wheel yet):
uv pip install -e .
Once released on PyPI:
pip install synadia-ai-agents
You also need a reachable nats-server. Pick whichever fits:
brew install nats-server # macOS
# Linux / anywhere with Docker:
docker run --rm -p 4222:4222 nats:2.12-alpine
# Then:
nats-server -a 127.0.0.1 -p 4222
See the nats.io install docs for more options. Synadia Cloud or any hosted NATS works too - see Connecting to NATS in production below.
Quickstart - call an agent
The SDK doesn't open NATS connections — you build a
nats.aio.client.Client and hand it to Agents. That mirrors what
Svcm(nc), jetstream(nc), Kvm(nc) do, and lets one connection serve
JetStream, KV, services, and agents at once.
import asyncio
import nats
from synadia_ai.agents import Agents, ResponseChunk, StatusChunk
async def main() -> None:
nc = await nats.connect(servers="nats://127.0.0.1:4222")
agents = Agents(nc=nc)
try:
found = await agents.discover() # list[Agent], stall by default
for a in found:
print(f"{a.agent}/{a.owner}/{a.name} @ {a.prompt_subject}")
# Each Agent is directly callable — no bind step.
async for msg in found[0].prompt("hello"):
if isinstance(msg, ResponseChunk):
print(msg.text, end="")
elif isinstance(msg, StatusChunk) and msg.status == "done":
print()
finally:
await agents.close() # SDK state only
await nc.close() # caller owns this
asyncio.run(main())
API matrix
| Symbol | Lives in | Purpose |
|---|---|---|
Agents |
agents.py |
Caller-side entry point. Construct with nc=; owns the heartbeat wildcard sub. |
Agent |
agent.py |
Live handle from Agents.discover(). .prompt() is the one method that does I/O. |
AgentInfo |
discovery.py |
Pure-data record (parsed $SRV.INFO per §4.3). What build_agent_info() returns. |
Liveness |
heartbeat.py |
Frozen snapshot from Agents.liveness(instance_id). |
load_context_options |
context.py |
Resolve a nats CLI context into kwargs for nats.connect(...). |
AgentService |
synadia-ai-agent-service |
Server-side; ships in a separate distribution. Import from synadia_ai.agent_service. |
Mid-stream queries
Agent handlers can pause their response stream to ask the caller a question (permission prompt, clarification, menu selection):
async for msg in agent.prompt("do the thing"):
if isinstance(msg, Query):
await msg.reply("yes")
else:
print(msg) # ResponseChunk / StatusChunk
Server-side, the handler asks via stream.ask(...) — see
synadia-ai-agent-service for the host-side
API.
Try the examples
Six runnable client-side demos live under
examples/. They talk to the reference agent
which now ships with synadia-ai-agent-service at
agent-sdk/python/examples/_reference_agent.py.
The ritual to see the SDKs work end-to-end:
# terminal 1 — start the reference agent (from the agent-sdk dist)
uv run --directory ../../agent-sdk/python python examples/_reference_agent.py \
--url nats://127.0.0.1:4222
# terminal 2 — discover and prompt (from this dist)
uv run python examples/01-discover.py --url nats://127.0.0.1:4222
uv run python examples/02-prompt-text.py --url nats://127.0.0.1:4222 "hello"
See examples/README.md for the full tour.
Connecting to NATS in production
For Synadia Cloud or any self-hosted
NATS that needs credentials, JWTs, or a non-default URL, use a nats
CLI context and load its kwargs into nats.connect:
import nats
from synadia_ai.agents import Agents, load_context_options
nc = await nats.connect(**load_context_options("prod"))
agents = Agents(nc=nc)
load_context_options(...) reads
~/.config/nats/context/<name>.json — URL, creds file, token,
user/password, inbox prefix are all honored. See
CLAUDE.md for the full field-by-field
table (including which NATS-context fields are not yet supported and
fail fast rather than silently).
Hosting an agent
The agent-host surface (AgentService, PromptStream,
PromptHandler, the heartbeat publisher) ships separately as
synadia-ai-agent-service — install that
package alongside this one when authoring an agent harness, and
import the host classes from synadia_ai.agent_service. The shared
wire types (Envelope, Attachment, error classes,
HeartbeatPayload, AgentSubject, the discovery constants) stay in
this package and continue to import from synadia_ai.agents.
Probe a running agent with the nats CLI (subjects are verb-first
per protocol v0.3):
nats micro list # see "agents"
nats req agents.prompt.demo.alice.worker-1 "hello" # prompt it
nats req agents.status.demo.alice.worker-1 "" # heartbeat-shaped status reply
nats sub "agents.hb.demo.alice.worker-1" # watch heartbeats
Documentation
- NATS Agent Protocol spec
- the wire contract (source of truth, lives in
synadia-ai/nats-agent-sdk-docs).
- the wire contract (source of truth, lives in
docs/protocol-mapping.md- every SDK call mapped to its spec section; for auditors and other-SDK implementers.examples/README.md- tour of the runnable demos underexamples/.CHANGELOG.md- release notes and migration guidance.CLAUDE.md- project context and engineering conventions.
Development
uv sync # install
uv run ruff check . && uv run ruff format --check . && uv run mypy src tests examples && uv run pytest
Integration tests spawn a real nats-server per session and record wire
evidence under tests/_evidence/<test-nodeid>/. Cross-SDK interop tests
(tests/test_interop_e2e.py) additionally spawn the TypeScript
reference agent via bun; they skip cleanly if bun or the sibling
../typescript/ checkout isn't present.
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file synadia_ai_agents-0.6.0.tar.gz.
File metadata
- Download URL: synadia_ai_agents-0.6.0.tar.gz
- Upload date:
- Size: 138.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa83e40162c1300c755f48edec3aac153dedb777fb23f8ded4313e7e6b1eb0fb
|
|
| MD5 |
327749a77eeb3501aecb223c03b1acae
|
|
| BLAKE2b-256 |
8f4bb6c3faf5f11c1251de927cbecfd7f53a26cfe42f046dd76d4f5bcb6c3141
|
Provenance
The following attestation bundles were made for synadia_ai_agents-0.6.0.tar.gz:
Publisher:
release-python.yml on synadia-ai/synadia-agents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synadia_ai_agents-0.6.0.tar.gz -
Subject digest:
fa83e40162c1300c755f48edec3aac153dedb777fb23f8ded4313e7e6b1eb0fb - Sigstore transparency entry: 1436190167
- Sigstore integration time:
-
Permalink:
synadia-ai/synadia-agents@1ff46531779e6b070640cd64440ad5814d7aad80 -
Branch / Tag:
refs/tags/python-v0.6.0 - Owner: https://github.com/synadia-ai
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@1ff46531779e6b070640cd64440ad5814d7aad80 -
Trigger Event:
push
-
Statement type:
File details
Details for the file synadia_ai_agents-0.6.0-py3-none-any.whl.
File metadata
- Download URL: synadia_ai_agents-0.6.0-py3-none-any.whl
- Upload date:
- Size: 52.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ab8a7f180af97926e4934240f95fbed67b7ae63b8eefccf2b6b860111d971a2
|
|
| MD5 |
7b2b5eac0ef8f7c63aabe5f3a86adfac
|
|
| BLAKE2b-256 |
b2ea81c29e3564f01e942b094c8fb63d8347cf1278e1b11641b203973ae97f0c
|
Provenance
The following attestation bundles were made for synadia_ai_agents-0.6.0-py3-none-any.whl:
Publisher:
release-python.yml on synadia-ai/synadia-agents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synadia_ai_agents-0.6.0-py3-none-any.whl -
Subject digest:
3ab8a7f180af97926e4934240f95fbed67b7ae63b8eefccf2b6b860111d971a2 - Sigstore transparency entry: 1436190173
- Sigstore integration time:
-
Permalink:
synadia-ai/synadia-agents@1ff46531779e6b070640cd64440ad5814d7aad80 -
Branch / Tag:
refs/tags/python-v0.6.0 - Owner: https://github.com/synadia-ai
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@1ff46531779e6b070640cd64440ad5814d7aad80 -
Trigger Event:
push
-
Statement type: