Skip to main content

Meerkat Python SDK

Python SDK for the Meerkat runtime.

  • Contract version: the same as the package version (meerkat.CONTRACT_VERSION)
  • Python: >=3.10
  • Package: meerkat-sdk

The SDK is session-first and runtime-backed: it spawns rkat-rpc and exposes typed Python handles (Session, DeferredSession, Mob) over the same canonical JSON-RPC surface used by CLI/REST/MCP.

Install

pip install meerkat-sdk

For local development:

pip install -e "sdks/python[dev]"

The rkat-rpc binary

MeerkatClient starts the first rkat-rpc it finds:

  1. MEERKAT_BIN_PATH, when set.
  2. An explicit MeerkatClient(rkat_path=...) other than the default "rkat-rpc".
  3. rkat-rpc on PATH.
  4. The release binary of this SDK's own version, downloaded once from the project's GitHub release and cached under ~/.cache/meerkat/bin/rkat-rpc/v<version>/<target>/.

The first three are binaries you chose, and the SDK runs them as they are: trusting them is your decision. Only the download is verified. A published meerkat-sdk package pins the sha256 of every release archive it may download and of the rkat-rpc inside it: pinned when the package is built, from the release's manifest, with each archive's build attestation verified, and never fetched at download time. The download uses HTTPS only, redirects included, and refuses an archive over 256 MiB (BINARY_DOWNLOAD_TOO_LARGE). The archive is checked before anything is extracted, the binary before it is installed, and the cached binary again each time it is reused (a few tenths of a second). On a mismatch the SDK raises BinaryIntegrityError (code BINARY_INTEGRITY_MISMATCH, with asset, path, expected_sha256 and actual_sha256), deletes what failed, runs nothing and does not fall back to another binary.

A source or git install of the SDK (for example pip install -e) pins no digests, so instead of downloading it raises MeerkatError with code BINARY_DIGEST_UNAVAILABLE. Install rkat-rpc yourself (brew install lukacf/meerkat/rkat, or the release archive for your platform) and put it on PATH, or set MEERKAT_BIN_PATH or rkat_path to a binary you trust. In that case, and when the download fails, an rkat binary on PATH is still used through its rpc subcommand.

Quick start

The selected model needs a compatible credential route: an already provisioned auth binding or the provider's environment key. This example selects Claude. For a clean API-key setup, export your Anthropic key in the shell that starts Python; the rkat-rpc child inherits it:

export ANTHROPIC_API_KEY="<your-anthropic-api-key>"

Skip this export if you already use a compatible auth binding; pass it with auth_binding when it is not the configured default. A different provider's key is not a substitute for the selected model's credentials.

import asyncio
from meerkat import MeerkatClient

async def main() -> None:
    async with MeerkatClient() as client:
        session = await client.create_session(
            "What is the capital of France?",
            model="claude-sonnet-4-6",
        )
        print(session.text)

        result = await session.turn("And Germany?")
        print(result.text)

        await session.archive()

asyncio.run(main())

Core API

Session creation

create_session, create_session_streaming, and create_deferred_session support:

  • host context and identity: injected_context, auth_binding, plus transient_turn_context for immediate create/stream only
  • model/provider controls: model, provider, max_tokens, system_prompt, provider_params
  • structured output: output_schema, structured_output_retries
  • runtime/tool toggles: enable_builtins, enable_shell, enable_memory, enable_schedule, enable_workgraph, enable_mob, enable_web_search
  • comms/runtime metadata: keep_alive, comms_name, peer_meta, budget_limits
  • skills: preload_skills, skill_refs
  • session metadata: labels
  • additional session config: additional_instructions, app_context, shell_env, external_tools

Deferred creation stores injected context for its eventual first turn but does not accept transient turn context. Pass transient facts to DeferredSession.start_turn(...).

Session queries

  • await client.list_sessions(...) -> list[SessionSummary]
  • await client.read_session(session_id) -> SessionDetails
  • await client.read_session_history(session_id, offset=0, limit=None) -> SessionHistory

SessionSummary is returned by session/list and includes total_tokens. SessionDetails is returned by session/read and includes model, provider, and last_assistant_text. Both use integer unix timestamps for created_at and updated_at.

Session runtime inputs

  • await client.inject_context(session_id, text, source=..., idempotency_key=...)
  • await session.inject_context(...)
  • await client.send_external_event(session_id, event_type, payload, blocks=None)
  • await session.send_external_event(event_type, payload, blocks=None)

Turns and streaming

Session.turn(...) and Session.stream(...) now expose full turn overrides:

  • injected_context, transient_turn_context
  • skill_refs, turn_tool_overlay
  • additional_instructions
  • keep_alive, model, provider, self_hosted_server_id, max_tokens, system_prompt
  • output_schema, structured_output_retries, provider_params

DeferredSession.start_turn(...) supports the same override set.

Config APIs return config write results

get_config returns a ConfigEnvelope:

{
  "config": {...},
  "generation": 12,
  "realm_id": "...",
  "instance_id": "...",
  "backend": "...",
  "resolved_paths": {...} | None,
}

set_config and patch_config return the same envelope fields plus optional live_propagation when a write fans out to live channels.

Example:

config_envelope = await client.get_config()
updated = await client.patch_config(
    {"max_tokens": 2048},
    expected_generation=config_envelope["generation"],
)

Additional surfaces

Models catalog

  • await client.get_models_catalog() -> ModelsCatalogResponse

Schedules

  • await client.create_schedule(request)
  • await client.get_schedule(schedule_id)
  • await client.list_schedules(labels=None, limit=None, offset=None)
  • await client.update_schedule(request)
  • await client.pause_schedule(schedule_id)
  • await client.resume_schedule(schedule_id)
  • await client.delete_schedule(schedule_id)
  • await client.list_schedule_occurrences(schedule_id, include_terminal=True|False)
  • await client.list_schedule_tools()
  • await client.call_schedule_tool({"name": "...", "arguments": {...}})

WorkGraph

WorkGraph is exposed as read-only observability from the SDK. Agents mutate work through their WorkGraph tools; SDK callers inspect the current graph.

  • await client.get_workgraph_item(item_id, realm_id=None, namespace=None)
  • await client.list_workgraph_items(filter=None)
  • await client.list_ready_workgraph_items(filter=None)
  • await client.get_workgraph_snapshot(filter=None)
  • await client.list_workgraph_events(filter=None)
  • await client.get_workgraph_goal_status(GoalStatusRequest(...))
  • await client.list_workgraph_attention(AttentionListRequest(...))

Jobs, approvals, artifacts, and projected event replay

  • jobs: jobs_get/list/cancel/progress/result/artifacts/retry/health/subscribe/unsubscribe
  • monitors and Meta workers: monitors_start, meta_job_*
  • approvals: request_approval, list_approvals, get_approval, decide_approval
  • artifacts: list_artifacts, get_artifact, download_artifact
  • projected event recovery: latest_event_cursor, list_events_since, event_snapshot when host event projection is enabled

Approval calls maintain audit records only; they do not automatically gate, authorize, or execute an action. They persist when the RPC bundle exposes a store path and otherwise remain process-local.

Explicitly ephemeral memory realms do not expose event cursor or snapshot replay APIs.

Every public Python RPC wrapper binds its request and result transport boundary to generated RPC-schema contracts. Generated request/result classes for the families above currently live in meerkat.generated.types; they are not re-exported at the package root.

Auth profiles and credentials are realm-scoped. Provisioning yields an auth_binding for a session or mob member spec, so the spec does not carry the provider secret. Remote hosts resolve bindings only inside their authorized realm.

Transcript editing and reconciliation

  • input_state, export_session_atif
  • transcript revision read/list, fork-at/fork-replace, rewrite, system-prompt update, and revision restore

Mob runtime

  • await client.read_mob_events(mob_id, after_cursor=0, limit=100)
  • await mob.read_events(after_cursor=0, limit=100)
  • await mob.spawn_many(specs) -> MobSpawnManyResult
  • await mob.wait_for_kickoff_complete(...)
  • helper methods use canonical role_name
  • trusted multi-host controls cover host bind/revoke, route installs, scope grants, hard cancel, member history, and remote member live channels

Multi-host mutations are trusted host APIs. They are not agent-callable, and the agent spawn wire cannot declare Rust's one-shot resume_from_role role migration authority.

Live WebSocket and WebRTC

LiveChannel wraps the session-bound live lifecycle. live_open returns a discriminated websocket or webrtc bootstrap. Only the WebSocket variant has url; WebRTC callers create an SDP offer and call live_webrtc_answer(channel_id, token, offer_sdp).

WebRTC requires connect(live_webrtc=True) and an rkat-rpc binary compiled with its non-default live-webrtc Cargo feature. The 0.8.24 auto-downloaded release binary omits that feature. Build a custom binary with ./scripts/repo-cargo build -p meerkat-rpc --features live-webrtc, and add an audio track or the meerkat.live data channel before the browser creates its offer. Set the local description, wait for ICE gathering to complete, and send peer.localDescription.sdp; there is no candidate-trickle RPC. Install the returned answer_sdp as the browser peer's remote description before live input. WebSocket uses connect(live_ws=True) and does not need that Cargo feature.

Realm profile CRUD

  • await client.create_mob_profile(name, profile)
  • await client.get_mob_profile(name) returns StoredMobProfile | None
  • await client.list_mob_profiles()
  • await client.update_mob_profile(name, profile, expected_revision=...)
  • await client.delete_mob_profile(name, expected_revision=...)

Streaming

session.stream(...) and client.create_session_streaming(...) return EventStream, an async context manager yielding typed event dataclasses (TextDelta, TurnCompleted, ToolExecutionCompleted, etc.).

from meerkat import TextDelta

async with session.stream("Explain this in detail.") as events:
    async for event in events:
        match event:
            case TextDelta(delta=chunk):
                print(chunk, end="", flush=True)
    result = events.result

TurnCompleted.usage is optional; absent means unmeasured, not zero. RunStarted.input preserves the typed RunInput: content (text or blocks) or a pending_tool_results continuation without a fabricated prompt. RunFailed accepts the current error_report and derives its display fields from that report.

Inventory-known events without a handwritten parser class arrive as UnknownEvent, preserving event.type and the complete wire payload in event.data. This includes server_tool_content, all four model_fallback_* events, transcript_rewrite_audit_receipt_committed, boundary_append_applied, and boundary_appends_discarded. A present type outside the generated inventory raises MeerkatError with code UNKNOWN_EVENT_TYPE.

A missing type or malformed payload for a handwritten parser class arrives as UnknownEvent(type="malformed_event"), with the original payload in event.data. The Retrying and HookFailed parsers still expect legacy flat fields; current payloads carry retry and reason, respectively, so they take this malformed path. See the event compatibility reference for details.

Run tests

pip install -e "sdks/python[dev]"

pytest sdks/python/tests/test_types.py -v
pytest sdks/python/tests/test_audit_parity.py -v
pytest sdks/python/tests/test_e2e.py -v
pytest sdks/python/tests/test_e2e_smoke.py -v

Metadata

Release files for meerkat-sdk 0.9.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for meerkat-sdk 0.9.4
File Size Uploaded Provenance
meerkat_sdk-0.9.4.tar.gz 350.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for meerkat-sdk 0.9.4
File Interpreter ABI Platform
meerkat_sdk-0.9.4-py3-none-any.whl Python 3 none any Details

Total release size: 596.3 kB

Release files / meerkat_sdk-0.9.4.tar.gz

Download URL meerkat_sdk-0.9.4.tar.gz
Size 350.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7a50768dfba64e5b59f088fde03533b89b51fc13e179e9bc0f7d932eb701b4dd
BLAKE2b-256 checksum
How to use checksums
ebd4af66dd4f4f461617c2d87aef553b8b5c3f3d7ee11002763f370391c4dd24
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / meerkat_sdk-0.9.4-py3-none-any.whl

Download URL meerkat_sdk-0.9.4-py3-none-any.whl
Size 246.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da4ad8afb15ae45fdb562ea48cdd5e06601da164a2af014cb764ab1c433dfdbe
BLAKE2b-256 checksum
How to use checksums
12a885d6035c12ae924d2bfd1eef595a2b180bbbf5d1405c1bc43c44b5bb721c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Security for meerkat-sdk 0.9.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.9.4 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page