Skip to main content

SQLite-backed multi-project messaging relay for cooperating agent/orchestrator sessions

Project description

Sandesh

संदेश (Sanskrit/Hindi: message · dispatch) — a tiny, standalone, multi-project messaging system for cooperating agent/orchestrator sessions. A SQLite-backed maildir + a mailbox watcher, pure Python stdlib (no third-party deps).

Latest release (see all releases) — the global store (one DB for all projects, cross-project messaging behind an admin grant, archive→tombstone lifecycle), inbox filters + FTS5 search (CLI/MCP/Pi), 12 MCP tools, and the Pi extension at full parity.

Built for the "Model-B" parallel-orchestration pattern (a Mainline coordinator + worker Track sessions that can't talk to each other directly), but project-agnostic.

Why

Sessions can't message each other directly, and re-invoking a sleeping agent is only possible via its host's background-task mechanism. Sandesh provides the relay: a durable, queryable mailbox each session reads from, plus a blocking notify watcher a session runs in the background so it wakes when mail addressed to it arrives.

Model

table holds
address the addressbook — durable identities, '<Orchestrator> - <Project>'
message the envelope — subject (required), kind, in_reply_to, body_path
message_recipient per-message addressees — role (to/cc) + per-recipient read_at
notifier per-session watcher liveness — pid, token, heartbeat, tombstone

Semantics: To wakes / Cc silent · all-tracks broadcast (minus sender) · per-recipient read (read = being acted on; reply = done) · subject-onlyfile-body (full absolute paths) · keep history · reply threading · crash-safe liveness (dead-pid / stale-heartbeat reap) · cooperative tombstone eviction · validated address format.

Layout

Sandesh is a standard Python package (sandesh/) installed via pyproject.toml; the two console scripts (sandesh, sandesh-mcp) land on $PATH. Runtime data lives under the XDG data dir — one global DB for all projects, plus a body folder per project:

$XDG_DATA_HOME/sandesh/                           (default ~/.local/share/sandesh/…)
├── sandesh.db                                    (the ONE global DB, WAL — all projects)
└── projects/<project_id>/
    └── messages/msg-<id>.md

Install

Distribution name: sandesh-relay (the import package + the sandesh / sandesh-mcp commands keep the name sandesh; sandesh was taken on PyPI). The [mcp] extra adds the MCP server; the bare install is the stdlib-only CLI + notify.

Two things need sandesh on PATH: the MCP client spawns sandesh-mcp, and the agent backgrounds sandesh notify (the wake) every cycle. A persistent install puts both on PATH and is the steady-use recommendation; ephemeral uvx is ideal for trial / the registration command.

uv (recommended)

# persistent — both scripts on PATH (run `uv tool update-shell` once for PATH)
uv tool install 'sandesh-relay[mcp]'           # from PyPI (once published — see RELEASING.md)
uv tool install 'git+https://github.com/anthill-tec/sandesh'        # from git, today
uv tool install '.[mcp]'                        # from a local checkout, today

# ephemeral — run the server with no install (deps cached on first run)
uvx --from 'sandesh-relay[mcp]' sandesh-mcp

uv manages its own Python, so it sidesteps PEP 668 (see below). PyPI publishing is automated via OIDC trusted publishing — see RELEASING.md.

pipx / pipxu (alternative)

pipx install 'sandesh-relay[mcp]' && pipx ensurepath   # user-space; restart shell once
sudo pipx install --global 'sandesh-relay[mcp]'        # all users (pipx ≥ 1.5)

(pipxu gives the same UX on a uv backend — common on Arch.)

Arch Linux (AUR)

yay -S sandesh-relay      # or: paru -S sandesh-relay

pacman/AUR resolves the prerequisites, so this sidesteps PEP 668 entirely (no uv/pipx bootstrap). The MCP server's dependency is offered as an optional python-mcp (from the AUR) — install it for sandesh-mcp; if it's unavailable, use the uv/pipx path for the server. (The AUR package is published per release — see RELEASING.md.)

install.sh (offline / from-source fallback)

No uv or pipx? install.sh builds its own venv and pip-installs the package into it — it needs only python3 (with venv) + pip, and is PEP-668-safe (the venv is not the system environment):

./install.sh           # → venv at ~/.local/share/sandesh/.venv + symlinks on ~/.local/bin

No installer present?

Neither uv nor pipx is guaranteed. Bootstrap one, or use install.sh:

# uv:   sudo pacman -S uv   |   curl -LsSf https://astral.sh/uv/install.sh | sh   |   pip install --user uv
# pipx: pip install --user pipx && pipx ensurepath   |   sudo pacman -S python-pipx

PEP 668: a plain pip install sandesh-relay into the system Python is blocked on externally-managed distros (Arch, Debian, Fedora, recent macOS) by design. Use uv, pipx, or install.sh (each isolates into a venv). On Arch, the AUR PKGBUILD (CR-SAN-009) sidesteps this entirely — pacman resolves the prerequisites.

Use

sandesh setup --project Atlas
sandesh --project Atlas register --address "Mainline - Atlas" --kind mainline
sandesh --project Atlas register --address "Track 2 - Atlas"  --kind track

# send (subject-only ⇢ no file; --body/--body-file ⇢ md body)
sandesh --project Atlas send --from "Track 2 - Atlas" --to "Mainline - Atlas" \
        --subject "CR-308 started — chain unaffected"

# the watcher (run in the background; exits 0 with ids when 'to' mail lands)
sandesh --project Atlas notify --to "Mainline - Atlas"

# read (consolidates unread to+cc, marks read)
sandesh --project Atlas fetch --to "Mainline - Atlas"

# reply (threads under the parent; the reply IS the completion signal)
sandesh --project Atlas reply --to-msg 1 --from "Mainline - Atlas" --body "ack"

sandesh --project Atlas addressbook

$SANDESH_PROJECT and $SANDESH_ADDRESS default --project and the caller's own address. $SANDESH_POLL_SECONDS sets the watcher cadence (default 10, floor 3).

Cross-project messaging

All projects share the one global DB, but sending across projects is admin-gated: the sender's project needs a one-time grant — sandesh grant --cross-project --project <id> --by <admin> (revoke with sandesh revoke --cross-project …; project-wide, inherited by every participant of the granted project). Without it, the send fails with cross-project sending not approved for project '<id>' — ask the Sandesh admin. The admin is assigned only at install via $SANDESH_ADMIN (install.sh) — there is no CLI or MCP surface to set it. all-tracks broadcasts never cross projects, grant or not. sandesh projects shows each project's state and grant (PROJECT STATE CROSS-PROJECT).

Project lifecycle

A project moves active → archived → tombstoned — strictly two-step, with two-tier authorization:

# read-only freeze — reversible, deletes NOTHING (project's own Mainline only)
sandesh archive   --project Atlas --by "Mainline - Atlas"
sandesh unarchive --project Atlas --by "Mainline - Atlas"

# permanent retirement — ARCHIVED projects only (the install-assigned super-admin only)
sandesh tombstone --project Atlas --by <admin>          # prompts y/N; --yes to skip
  • archive evicts the project's live notify watchers (cooperatively; --force reaps stragglers) and freezes it: sends from/to it and new registrations are refused, but every message, body, and thread stays fully readable. unarchive reverses it.
  • tombstone is destructive and irreversible: it purges the project's internal messages (sender and all recipients inside the project) and deletes its projects/<id>/ body folder. What survives: cross-project envelopes (rows stay for audit + thread anchoring — their bodies are lost), and the tracker row itself as a permanent tombstoned marker (the project id is retired; setup refuses to reuse it). Afterwards inbox/fetch hide the tombstoned project's traffic, and thread marks holes with incomplete chain — message(s) removed (project tombstoned).
  • Who may do what: archive/unarchive take the project's own Mainline as --by; tombstone takes only the super-admin assigned at install via $SANDESH_ADMIN. Without --yes, tombstone asks for interactive confirmation (and refuses when stdin is not a terminal).
  • --dry-run (all three verbs) reports what would happen — watchers to evict, the would-be state, and for tombstone the purge counts (internal messages, body files, cross-project messages whose bodies would be lost) — and writes nothing. Guards still apply: a dry-run on the wrong state or with the wrong --by errors exactly like the real command.

MCP server

The verbs are also exposed as an MCP server (stdio) so an agent can call them as tools instead of shelling out. mcp is the only third-party dependency — it ships behind the optional [mcp] extra and is imported only by sandesh.mcp_server, so the CLI above stays pure-stdlib either way. Installing sandesh-relay[mcp] (above) provides the sandesh-mcp command; a base install without the extra still ships sandesh-mcp but it prints a one-line "install the [mcp] extra" hint and exits non-zero rather than crashing.

Register with Claude Code (stdio; bake a default project via env so tools can omit project_id):

claude mcp add sandesh --scope user --env SANDESH_PROJECT=<id> -- sandesh-mcp

That writes an mcpServers entry (~/.claude.json for user scope, or a committed .mcp.json for --scope project). Manage with claude mcp list / claude mcp get sandesh / claude mcp remove sandesh; the in-session /mcp panel shows status + tools.

Nine tools, each taking project_id (falls back to $SANDESH_PROJECT): sandesh_setup, sandesh_register, sandesh_unregister, sandesh_addressbook, sandesh_send, sandesh_reply, sandesh_inbox, sandesh_fetch, sandesh_thread. The server also returns usage instructions on initialize and serves the full scenarios doc as the sandesh://usage resource. (Lifecycle: read = being acted on, reply = done — there is no separate status tool.)

Manual smoke — the MCP Inspector (browser UI):

mcp dev -m sandesh.mcp_server     # needs the [mcp] extra; explore tools by hand

The wake path stays the notify watcher — MCP exposes the verbs, not the wake (an MCP server can't re-invoke a sleeping agent). No notify/watch tool is exposed over MCP.

Discover via the MCP Registry

Sandesh is listed on the official MCP Registry as io.github.anthill-tec/sandesh (see server.json), so MCP-aware clients/aggregators (Claude Code, Cursor, Glama, PulseMCP, mcp.so, …) can discover it. The listing points at the PyPI distribution sandesh-relay and the stdio launch uvx --from 'sandesh-relay[mcp]' sandesh-mcp. The listing is the server (the verbs) only — the notify wake is a separate background process, not a tool. Publishing the listing is a maintainer step (after the PyPI release) — see RELEASING.md.

Test

python3 -m unittest -v          # from the repo root (stdlib-only: CLI + library)
.venv/bin/python -m unittest -v # includes the MCP server + E2E tests (needs the venv)

Roadmap

  • MCP server — DONE (Phase 2): 9 tools over stdio + instructions + sandesh://usage resource; [mcp]-extra isolation; in-memory + real-subprocess E2E tests. The notify watcher remains the wake path.
  • Packaging — DONE (Phase 3): pyproject.toml (hatchling + tag-driven hatch-vcs version), sandesh/sandesh-mcp console scripts, [mcp] extra; uv/pipx/install.sh install.
  • PyPI publish — DONE (Phase 3, CR-SAN-010): .github/workflows/publish-pypi.yml publishes sandesh-relay to PyPI on a GitHub Release via OIDC trusted publishing (TestPyPI dry-run on manual dispatch); version is git-tag-driven. See RELEASING.md.
  • Discovery/distribution — DONE (Phase 3): AUR PKGBUILD (CR-SAN-009, packaging/aur/) + official MCP Registry listing (CR-SAN-011, server.json io.github.anthill-tec/sandesh).
  • Pi extension — DONE (Phase 4): a native Pi extension at integrations/pi/ — registers the Sandesh verbs as Pi tools (CR-SAN-013) and a native wake (CR-SAN-014: the extension wakes the idle agent itself via sendUserMessage, no host background task) — published to npm as @anthill-tec/sandesh-pi (CR-SAN-015). See integrations/pi/README.md.
  • The registry publishes (PyPI / AUR / MCP-registry / npm) are maintainer actions — see RELEASING.md.

License

Copyright © 2026 anthill-tec. Licensed under the GNU General Public License v3.0 — see LICENSE. You may use, study, modify, and redistribute Sandesh under the GPLv3; distributed derivatives must remain GPL-licensed with source available.

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

sandesh_relay-0.2.2.tar.gz (590.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sandesh_relay-0.2.2-py3-none-any.whl (76.8 kB view details)

Uploaded Python 3

File details

Details for the file sandesh_relay-0.2.2.tar.gz.

File metadata

  • Download URL: sandesh_relay-0.2.2.tar.gz
  • Upload date:
  • Size: 590.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sandesh_relay-0.2.2.tar.gz
Algorithm Hash digest
SHA256 6f40255ba994aaad2a9afeadfdd73c3e677fcdcf29b260f895758daca1ea0915
MD5 17dc0806d0eac6485529371e87b61c26
BLAKE2b-256 5d435f7cd55f83de94607faf9af537cc72687f2b541440dad959c68be0f10176

See more details on using hashes here.

Provenance

The following attestation bundles were made for sandesh_relay-0.2.2.tar.gz:

Publisher: publish-pypi.yml on anthill-tec/sandesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sandesh_relay-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: sandesh_relay-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 76.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sandesh_relay-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3f47c7c0ab1ac2b0dbe11950c4118cfe92c7a715f39db576f6a4663f88e04429
MD5 c36b42b173c1284900220ca2e3b717c1
BLAKE2b-256 eac7e2098c460d844a4ca05437f869d4cfb57f5ea36f660280b49cc35d9373cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for sandesh_relay-0.2.2-py3-none-any.whl:

Publisher: publish-pypi.yml on anthill-tec/sandesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page