Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Carrier logo

Carrier

Send. Reply. Acknowledge.
Carrier gives agents and programs authenticated, durable inboxes. Exchange messages through an async Python SDK or MCP tools, with identities and message history persisted in SQLite.

Quick start · Demos · Python SDK · MCP setup · Documentation


Contents · Capabilities · Installation · Demos · Quick start · Build with Carrier · Architecture · Delivery semantics · Documentation · Development

What you can do

  • Address agents and programs directly. Send to a permanent address or a unique readable name.
  • Keep inboxes across restarts. Retain identities, messages, replies, and acknowledgments in SQLite, including messages sent while a recipient is offline.
  • Follow a request and its reply. Correlate responses with the original message instead of guessing from inbox order.
  • Make retries explicit. Reuse a send's retry key to avoid duplicate storage, and acknowledge messages after accepting them.
  • Integrate through Python or MCP. Run async consumers in your application or expose inbox tools to an agent conversation.

See Carrier in action

These recordings run against a real, isolated Carrier service. Expand a workflow to watch the GIF. The accompanying VHS tapes show the recorded commands.

Send, reply, and acknowledge — Python SDK

Send a request to another inbox, match the reply to that request, then explicitly acknowledge both messages. Reading and replying leave them pending; acknowledgment preserves their history.

Python SDK exchanges a request and correlated reply, acknowledges both messages, and reads retained inbox history.

Quick start · VHS tape

Retry a send without storing a duplicate

Publish the same build event with a stable retry key. Both sends return message ID 1. After archiving and acknowledging it, another retry returns that same message with status acknowledged. The inbox retains one message.

Repeated build-event sends return the same message ID, with one pending event before archival and zero afterward.

Retry guide · VHS tape

Recover an inbox after consumer downtime

Store three events while the consumer is offline and the service stays running. The archiver reads two-message pages, commits each event before acknowledgment, and accepts zero events when run again. All three messages remain in history.

Three stored events are archived with two-message pagination, acknowledged, and retained; a second archive run finds no pending events.

Recovery guide · VHS tape

Exchange messages through real MCP tools

Two scripted Python clients connect to separate Carrier stdio adapters and call send, wait, and ack. A reply is correlated with its original request. This records real MCP tool calls and service responses; no AI model is used.

Two Carrier MCP clients send a review request, receive its correlated reply, and acknowledge both messages.

MCP setup · VHS tape

Run the quick start and SDK examples to reproduce the messaging workflows locally. Real stdio MCP exchanges are covered by the integration tests. The tapes depend on a separate recording harness; this repository does not include that harness.

Installation

These instructions describe the development branch preparing aixand-carrier-mcp==0.1.0b0. Python 3.11+ is required. The required aixand-lib-filesystem dependency is published on PyPI and installs automatically with python -m pip install ..

After Carrier's first PyPI publication, install the beta in a virtual environment with python -m pip install aixand-carrier-mcp==0.1.0b0. See Python package releases for publication and verification.

The development helpers and native packaging still require Carrier and aixand-libraries source checkouts in sibling directories:

workspace/
  carrier/
  aixand-libraries/
    filesystem/

Obtain the filesystem source from the same source workspace for these helper commands. See source prerequisites if the helper reports a missing dependency.

To install the standalone CLI, run from the Carrier repository root:

./dev.sh install --prod
"$HOME/.local/bin/carrier" --help

On Windows use .\dev.ps1 install --prod, then carrier --help. On Linux/macOS, add ~/.local/bin to PATH before using bare carrier commands. The source installer needs network access to resolve third-party dependencies. For SDK applications, use the separate environment below.

Carrier also adopts the revision-2 product installer for locally built artifacts. Its native payload bundles CPython, Carrier, and dependency wheels. Read the installation guide for packaging, private scope, bootstraps, updates, and uninstall. The installer validation guide explains lifecycle checks and native qualification requirements. Hosted native installers are not provided by this checkout. Python package publication uses the tag-to-PyPI workflow.

Start the messaging service explicitly with carrier serve. Carrier databases and credentials survive software removal. See development setup for installer locations and lifecycle commands.

Python SDK setup

Applications on Unix sharing the local Carrier database can use carrier.ensure_session(database=..., credentials=..., address=..., name=...) to provision stable inbox identities. It preserves existing credentials and uses a private pending credential file and filesystem lock to recover an interrupted provisioning operation with the same token. Concurrent callers for the same credential path share one identity. An existing address with different credentials is rejected. This local administration helper must point at the database actually served by Carrier; verify access through Client.open(). It does not grant remote clients provisioning privileges.

For the Python SDK, install Carrier into your application's environment. The public CLI's private environment does not add Carrier to other Python environments. From a local checkout, the quick start below uses a .venv:

python3 -m venv .venv
.venv/bin/python -m pip install .
source .venv/bin/activate

The package is named aixand-carrier-mcp; the Python import and CLI are both carrier. The service, SDK, and MCP adapter ship together. On Windows, create the environment with py -3 -m venv .venv and use .venv\Scripts\python.exe and .venv\Scripts\carrier.exe for the examples.

Set up with mise

From the repository root:

mise trust
mise run setup
source .venv/bin/activate

Mise selects the Python version in mise.toml and installs Carrier and its test dependencies into .venv. You can also use mise run carrier to invoke the CLI without activating the environment.

Quick start

Exchange a request and reply between two inboxes. This walkthrough uses .carrier/demo.db and port 8876, separate from the service's default database and port. Run commands from the repository root.

  1. Create the two identities once.

    carrier session demo/requester --db .carrier/demo.db --credentials .carrier/requester.json
    carrier session demo/reviewer --db .carrier/demo.db --credentials .carrier/reviewer.json
    

    Each command prints Created ...; credentials saved to .... Keep those credential files to reuse the inboxes; provisioning refuses to overwrite them. The .carrier/ directory is ignored by Git.

  2. Start the shared service.

    carrier serve --db .carrier/demo.db --port 8876
    

    Leave this terminal running. In a second terminal, verify the service:

    carrier status --url http://127.0.0.1:8876
    

    Expect Carrier running at http://127.0.0.1:8876.

  3. Send, reply, and acknowledge.

    Save the following as .carrier/exchange.py and run .venv/bin/python .carrier/exchange.py from the repository root, using the SDK environment created during installation:

    import asyncio
    from uuid import uuid4
    
    from carrier import Client
    
    async def main():
        url = "http://127.0.0.1:8876"
        async with (
            Client(url=url, credentials=".carrier/requester.json") as requester,
            Client(url=url, credentials=".carrier/reviewer.json") as reviewer,
        ):
            retry_key = uuid4().hex
            question = await requester.send(
                "Can you review this change?",
                to="demo/reviewer",
                idempotency_key=retry_key,
            )
            page = await reviewer.receive_pending()
            incoming = next(m for m in page.messages if m.id == question.id)
            print("Reviewer received:", incoming.content)
            await incoming.reply("I'll review it.")
            await incoming.ack()
    
            answer = await requester.wait_for_reply(question, timeout=5)
            print("Requester received:", answer.content)
            await answer.ack()
            status = await requester.delivery_status(retry_key)
            print("Request delivery:", status["status"])
    
    asyncio.run(main())
    

    Expected output:

    Reviewer received: Can you review this change?
    Requester received: I'll review it.
    Request delivery: acknowledged
    

The example opens both inboxes in one program; each can instead belong to a separate application or agent. Use these demo inboxes without other active consumers. Each successful run acknowledges both messages, and a new run uses a fresh retry key. Acknowledgment records acceptance of the request; the review itself is application work.

Stop the service with Ctrl+C. Restart it with the same database and reuse the credential files to retain identities and history. For normal operation, the defaults are http://127.0.0.1:8765 and ~/.aixand/carrier/carrier.db. Carrier creates its per-user directory, ~/.aixand/carrier, when needed. Override the database with --db; provisioning and serving must use the same database. Credential files remain at the explicitly supplied --credentials path.

Existing databases at ~/.local/share/carrier/carrier.db are not moved automatically. To migrate, stop the service and other processes accessing the database, then move carrier.db and any SQLite sidecar files into ~/.aixand/carrier/, without overwriting an existing database. Keep your credential files and update any explicit database paths in service units or integrations before restarting. To continue using the previous location, pass --db "$HOME/.local/share/carrier/carrier.db" to both provisioning and serving.

Build with Carrier

For managed Codex terminal launches, the optional agent launch adapter provides CLI and async SDK entry points, durable conversation binding, and detached cleanup. Carrier's ordinary messaging SDK and MCP setup remain independent.

Python

The quick start uses the async SDK. Applications can also send JSON, wait for correlated replies, and register an async message handler with @inbox.consumer(). Calling await inbox.run() runs that consumer in your application; the handler explicitly replies and acknowledges messages.

Try the runnable SDK examples: request a CI report from a worker, publish build events safely across retries, and archive pending events after downtime. The Python SDK guide also covers the API, error handling, and compatibility requirements.

MCP

Configure a stdio MCP adapter for each agent conversation, using absolute paths. This example connects to the quick-start requester's inbox:

{
  "mcpServers": {
    "carrier": {
      "command": "/home/you/.local/bin/carrier",
      "args": [
        "mcp", "--url", "http://127.0.0.1:8876",
        "--credentials", "/absolute/path/to/carrier/.carrier/requester.json"
      ]
    }
  }
}

Replace the command with the absolute path to your installed carrier and the credentials path with your own. The configuration format depends on your app. Each independent conversation needs its own identity. The shared backend is an HTTP service; the local adapter provides the MCP tools. An agent can call send(to="demo/reviewer", content="Can you review this change?"), then use wait() to receive messages and ack() to accept them.

For the receiving conversation, use reviewer.json. Let the agents own their inbox reads once connected. To correlate a reply, send it back to the original sender with reply_to set to the received message's numeric id. The requester can then call wait(reply_to=...) with its original request's ID.

wait() defaults to 45 seconds and accepts timeout_seconds up to 300. Keep your app's MCP tool timeout above that duration. Waiting does not acknowledge messages; call ack(message_id=...) after accepting each one. Reconnect the adapter after upgrading Carrier to load updated tools.

The Python SDK calls the send retry key idempotency_key; MCP calls it message_id. Acknowledgments use the stored message's numeric id.

Architecture

Python applications and per-conversation MCP adapters share the same HTTP service. The service authenticates inbox access and persists messaging state in SQLite.

flowchart LR
    accTitle: Carrier runtime communication
    accDescr: Python applications and per-conversation MCP adapters use the async Python SDK to access the shared HTTP service. The service authenticates inbox access and stores identities and messages in SQLite.
    App[Python application] --> SDK[Async Python SDK]
    Agent[Agent conversation] -->|stdio MCP| Adapter[MCP adapter]
    Adapter --> SDK
    SDK -->|HTTP| Service[Carrier service]
    Service --> DB[(SQLite)]

Carrier owns message exchange. Applications own process launch, session lifecycle, and actions triggered by messages. An optional adapter coordinates agent launch and conversation binding.

Delivery semantics

  • Stored: Carrier has persisted the message. An offline recipient can read it later.
  • Acknowledged: The recipient has explicitly accepted the message. Reading or replying alone does not acknowledge it.
  • Completed: Your application defines and communicates task completion, for example through a correlated reply.

Reads preserve history. Unacknowledged messages remain pending and can be returned again; retry keys prevent duplicate message storage when reused with the same request. They do not guarantee exactly-once task execution.

Use one active receiving owner per inbox. Receiving requires an SDK consumer or an explicit MCP tool call; Carrier does not wake an agent that has ended its turn. The service binds to localhost by default. Remote deployment requires HTTPS and operational hardening. The Python SDK guide explains retry rules, pagination, error handling, and consumer ownership.

Documentation

I want to… Guide
Exchange my first request and reply Quick start
Add messaging to a Python application Python SDK
Connect an agent conversation MCP setup
Run a worker or publish events Runnable examples
Handle retries and pending messages Delivery semantics · Retry contract
Install the CLI or build a native artifact Installation · Product installation
Install or update the CLI from a local checkout Development · Linux/macOS installer · Windows installer
Diagnose setup or service errors Troubleshooting
Find CLI options and defaults CLI reference
Report an issue or read the participation policy Issues and pull requests · Support
Change a local checkout and run checks Development guide

The documentation index records the adopted standard and reader paths.

Development

Keep the required ../aixand-libraries/filesystem checkout described under installation. To build and install the standalone CLI, run from the repository root on Linux/macOS with Python 3.11+:

./dev.sh install --prod

On Windows, use PowerShell 5.1+ with Python 3.11+:

.\dev.ps1 install --prod
carrier --help

The PowerShell script detects py, python, python3, or an existing uv-managed Python. Set CARRIER_DEV_PYTHON to select a specific executable. It works from any directory and does not require administrator access. Install and reinstall add the command directory to your user PATH and the current PowerShell process, without duplicating existing entries. Run .\dev.ps1 directly to use carrier immediately. When using powershell -File or pwsh -File, the parent terminal keeps its old PATH; run the script directly there or reopen the terminal application.

The development installer publishes carrier (carrier.exe on Windows) in ~/.local/bin and installs its private environments in ~/.aixand/carrier/dev-install by default. On Linux/macOS, add the command directory to your shell PATH. Virtual environment activation is not needed.

Local reinstall, uninstall, and custom paths
./dev.sh reinstall
./dev.sh uninstall

On Windows:

.\dev.ps1 reinstall
.\dev.ps1 uninstall

Reinstall builds and checks a fresh environment before switching the command. Restart running services and MCP adapters afterward to load the new copy. On Windows, stop them before reinstalling so carrier.exe is not in use. Previous environments remain available to existing processes until uninstall. Stop those processes before uninstalling; uninstall removes the command and installed environments while preserving databases, credentials, and service configuration. The script does not register or manage a background service. Windows uninstall leaves the shared command directory on PATH for other tools.

Set CARRIER_DEV_PYTHON to select a Python executable, CARRIER_DEV_INSTALL_DIR to change the command directory, or CARRIER_DEV_ENV_DIR to change the private environment directory. Directory overrides must be absolute paths; use the same values for reinstall and uninstall. Choose separate local paths if you also use an artifact-based product installation. Run ./dev.sh --help or .\dev.ps1 --help for the full command reference. These commands manage development installations; they do not manage public-installer releases.

For a development installation using the previous default directory, stop the service and MCP adapters, then reinstall at the new location:

CARRIER_DEV_ENV_DIR="$HOME/.local/share/carrier/dev-install" ./dev.sh uninstall
./dev.sh install --prod

This preserves database and credential files; migrate an existing database as described above before restarting. Rebuild virtual environments at the new location because their scripts contain absolute paths.

Use an editable install for work on the service, SDK, or MCP adapter:

mise run setup
mise run test

For locked development, keep the ../aixand-libraries/filesystem checkout and use:

./dev.sh develop
./dev.sh check
./dev.sh test

The tests cover authentication, inbox isolation, delivery semantics, persistence, the SDK, and real stdio MCP exchanges. Use ./dev.sh reinstall (or .\dev.ps1 reinstall on Windows) when you want to update the standalone CLI from the current checkout.


Documentation · Issues and pull requests · Code of conduct · Security · Support · Changelog · Apache-2.0 license

AI-assisted development

We use AI tools to generate and revise code and documentation. Maintainers review and validate the resulting work and preserve required third-party attribution. We do not knowingly include proprietary code without permission. Report ownership or licensing concerns through the private reporting route.

Issues and pull requests

Bug reports, documentation issues, feature requests, and usage questions are welcome. We do not accept external pull requests because the team lacks capacity to review them. See the participation policy and support guide.

Metadata

Release files for aixand-carrier-mcp 0.1.0b0

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

Source distribution (sdist)

Source distribution for aixand-carrier-mcp 0.1.0b0
File Size Uploaded Provenance Actions
aixand_carrier_mcp-0.1.0b0.tar.gz 2.5 MB With provenance Details for aixand_carrier_mcp-0.1.0b0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aixand-carrier-mcp 0.1.0b0
File Interpreter ABI Platform Actions
aixand_carrier_mcp-0.1.0b0-py3-none-any.whl Python 3 none any Details for aixand_carrier_mcp-0.1.0b0-py3-none-any.whl

Total release size: 2.6 MB

Release files / aixand_carrier_mcp-0.1.0b0.tar.gz

Download URL aixand_carrier_mcp-0.1.0b0.tar.gz
Size 2.5 MB
Tags Source
SHA-256 checksum
How to use checksums
bb68c5fb81d6da21c25a2778e9ec616b379714f31a63f656349138d9be1d8375
BLAKE2b-256 checksum
How to use checksums
b70061242c162a97be5ac34d9f57b9154bc0dcc8f54440724ccaf6cea037b4f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Release files / aixand_carrier_mcp-0.1.0b0-py3-none-any.whl

Download URL aixand_carrier_mcp-0.1.0b0-py3-none-any.whl
Size 42.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
653c2b7aa20a31f5897877104781f2b7e6a684fe893e6c886b1bb8b72efdd2f8
BLAKE2b-256 checksum
How to use checksums
a4d9382ca2c19219e6aaa208ab307893dec7c7982a524ef9df066d888434d198
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Security for aixand-carrier-mcp 0.1.0b0

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for aixand_carrier_mcp-0.1.0b0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

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
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