Skip to main content

action-marshall

Action-level release control for AI agents. Preview, approve, canary, halt, and audit tool actions before they touch production.

action-marshall is the Python SDK for Action Marshall. Wrap the tools or functions your agent already calls — Action Marshall runs preview, policy, approval, canary, breaker, and signed audit before the action is released.

Status: 0.1.0 — pre-1.0 alpha. The API may change before 1.0.0.


Install

pip install action-marshall

Python 3.9+.

You also need a backend

action-marshall is the client SDK. It talks to an Action Marshall backend that runs the policy engine, signs receipts, and stores audit history. To try it end-to-end you have two paths:

Option A — local backend via Docker (recommended for evaluation)

git clone https://github.com/SatwikReddySripathi/action-marshall.git
cd action-marshall
docker compose up --build

Backend lives at http://localhost:8000, dashboard at http://localhost:3000. Wait until curl http://localhost:8000/ready returns 200. Default API key is am_test_demo_key_001.

Option B — point at a hosted backend (planned — hosted Action Marshall is not live yet)

Once hosted Action Marshall opens, you'll get an API key and a base_url. Join the waitlist.

Optional framework adapters:

pip install "action-marshall[langchain]"     # available now (experimental)
pip install "action-marshall[langgraph]"     # planned
pip install "action-marshall[crewai]"        # planned
pip install "action-marshall[autogen]"       # planned
pip install "action-marshall[mcp]"           # planned
pip install "action-marshall[llamaindex]"    # planned
pip install "action-marshall[openai]"        # planned
pip install "action-marshall[all]"

Quickstart

Assuming the backend is up at http://localhost:8000:

from action_marshall import MarshallClient, Action, ActionParams

ks = MarshallClient(
    api_key="am_test_demo_key_001",
    base_url="http://localhost:8000",
)

result = ks.run(Action(
    tool="servicenow",
    action_type="bulk_update",
    params=ActionParams(
        connector="servicenow_sim",
        query={"state": "open", "priority": {"op": "in", "value": ["P3", "P4"]}},
        changes={"state": "in_progress"},
    ),
))

print(result.decision_value)   # AUTO | CANARY | BLOCK | APPROVAL_REQUIRED
print(result.status)           # completed | contained | blocked | awaiting_approval | observed
print(result.blast_radius)     # 17
print(result.proof_url)        # /v1/actions/<id>/proof

Open result.ui_urls["detail"] in your browser to see the full action in the dashboard.


Wrap an existing function

The killer pattern — every call becomes a governed action:

@ks.wrap_function(
    tool="servicenow",
    action_type="update_incident",
    connector="servicenow_sim",
    agent_id="incident-triage",
)
def update_incident(payload: dict) -> dict:
    # your existing implementation
    ...

result = update_incident({"incident_id": "INC001", "status": "resolved"})
  • AUTO → the wrapped function runs normally.
  • BLOCK → wrapped function is not called; MarshallDenied is raised.
  • APPROVAL_REQUIRED → wrapped function is not called; MarshallApprovalRequired is raised. Approve in Slack or the web UI, the next call goes through.

Pass on_denied=... / on_approval_required=... callbacks if you want to handle these without exceptions:

@ks.wrap_function(
    tool="servicenow", action_type="update_incident",
    connector="servicenow_sim", agent_id="incident-triage",
    on_denied=lambda err: {"status": "blocked-by-policy", "reasons": err.result.decision.get("reasons")},
)
def update_incident(payload: dict) -> dict: ...

Preview without executing

preview = ks.preview(Action(...))
print(preview.decision_value, preview.blast_radius, preview.preview_hash)

Same lifecycle as run but no writes happen — the policy and canary phases evaluate and a receipt is signed for the dry run.


Wrap a LangChain tool

available now (experimental). Requires pip install "action-marshall[langchain]".

from langchain_core.tools import tool
from action_marshall import MarshallClient
from action_marshall.adapters.langchain import wrap_langchain_tool

@tool
def update_incident(payload: dict) -> dict:
    ...

ks = MarshallClient(api_key="am_...", base_url="http://localhost:8000")

protected = wrap_langchain_tool(
    update_incident, ks=ks,
    tool="servicenow", action_type="update_incident",
    connector="servicenow_sim", agent_id="incident-triage",
)
# protected.invoke({...}) is now governed.

Verify a signed receipt

receipt = ks.verify_receipt("act_abc123")
print(receipt.verified)   # True if the HMAC signature matches
print(receipt.signature)

Server-side verification — the backend re-signs the receipt body and compares with constant-time HMAC. For fully offline verification with a local PROOF_SECRET, see the security docs.


Decisions

Decision Meaning
AUTO Allowed without approval. The wrapped function runs.
CANARY Allowed, but a canary subset runs first; the breaker can halt.
APPROVAL_REQUIRED Human approval needed before execution.
BLOCK Disallowed. The wrapped function is not called.

Decision hierarchy: BLOCK > APPROVAL_REQUIRED > CANARY > AUTO. Policies escalate, never de-escalate.

Connectors

Connector Use for Status
servicenow_sim Simulated ServiceNow incidents (demo / dev) available now
servicenow_real Live ServiceNow instance available now
email_generic Outbound email actions available now
jira_real Live Jira Cloud planned

Public API

from action_marshall import (
    MarshallClient,
    Action, ActionParams, Actor, Approval, Approver,
    ActionResult, PreviewResult, Receipt,
    MarshallError, MarshallAPIError, MarshallDenied, MarshallApprovalRequired,
)

The type-checker-friendly py.typed marker ships in the wheel — mypy and pyright will pick up our annotations automatically.


CLI

action-marshall is also installed as a CLI binary. Useful for one-off operations, CI smoke checks, and tools that aren't easily called from Python.

action-marshall --help
action-marshall init                            # write ~/.marshall/config.json
action-marshall preview action.json
action-marshall run action.json
action-marshall receipts list
action-marshall receipts verify act_abc123

Full reference: docs/cli.md.


Local development

git clone https://github.com/SatwikReddySripathi/action-marshall.git
cd action-marshall/sdk
pip install -e ".[dev]"
pytest

License

MIT. See LICENSE.

Release files for action-marshall 0.1.0

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

Source distribution (sdist)

Source distribution for action-marshall 0.1.0
File Size Uploaded
action_marshall-0.1.0.tar.gz 22.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for action-marshall 0.1.0
File Interpreter ABI Platform
action_marshall-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 39.4 kB

Release files / action_marshall-0.1.0.tar.gz

Download URL action_marshall-0.1.0.tar.gz
Size 22.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e53c85dd2cbb89c60b3cabf7184beadd2c9fcee5c9bdaf483b677be6bfd8942c
BLAKE2b-256 checksum
How to use checksums
24b952be8b6f3020666a835a3eb44ba53a1cccf44634d5dc97024bab3a079b1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 26, 2026.

Transparency log

Release files / action_marshall-0.1.0-py3-none-any.whl

Download URL action_marshall-0.1.0-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2bfac6a6deb1d8651f8f074828b0ac509126cd78547aeb4f21018eef83d9822
BLAKE2b-256 checksum
How to use checksums
a156689a10329098c43e10f43b180cab8d868019cf188f36b040bed93d9a45ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 May 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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