MrProbe / Agent Guard customer observation SDK — ship your agent's response back to MrProbe in 6 lines.
Project description
agentguard-observe
Customer-side observation SDK for MrProbe / Agent Guard (POST /api/v1/observe).
Ship your agent's response back to MrProbe in 6 lines so red-team campaigns get live, signed, retry-safe, secret-redacted observations from your agent runtime — wherever it runs (FastAPI, Vertex AI, Bedrock Lambda, Azure Function, custom HTTP).
Status: v0.1.0 — design-partner ready for Email Agents on Microsoft 365 Outlook and Google Workspace Gmail. The same SDK is the Pillar 4 surface for every other agent-type epic (Document/RAG, Salesforce, ServiceNow, etc.) — internally tracked as MRPRBE-303. Pre-1.0 because the wire-shape (e.g.
llm_responsefield name) may evolve through design-partner feedback before we commit to the 1.0 stability contract.
Install
From PyPI (recommended)
pip install agentguard-observe
Pin in requirements.txt:
agentguard-observe==0.1.0
From the GitHub release (alternative — hash-pinned)
pip install \
https://github.com/QDEXConsulting/agent-guard/releases/download/agentguard-observe-v0.1.0/agentguard_observe-0.1.0-py3-none-any.whl \
--hash=sha256:<copy-from-the-release-page>
The SHA-256 of every published wheel + sdist is printed in the release notes
on the GitHub Releases page. Include the --hash line for supply-chain
verification.
From source (during development / dogfooding)
git clone https://github.com/QDEXConsulting/agent-guard.git
pip install -e ./agent-guard/sdk/agentguard_observe
Six-line happy path
import os
from agentguard_observe import Client
client = Client(
api_key=os.environ["AGENTGUARD_API_KEY"], # bearer identifier (shown once at agent registration)
agent_id=os.environ["AGENTGUARD_AGENT_ID"], # the agent's UUID
signing_secret=os.environ["AGENTGUARD_SIGNING_SECRET"], # HMAC key — see "Fetching your signing secret" below
)
# ... your agent processes the inbound and produces a reply ...
client.observe(
attack_id=inbound_headers["X-AgentGuard-ID"], # echoed back from the test message
response_text=reply.text, # what your LLM said
action="email_forward", # what your agent DID (or "none")
action_status="executed", # "executed" | "blocked" | "none"
action_target="alice@example.com", # who/what the action affected
)
That's the entire integration. The SDK signs the request, retries on
transient failures, and redacts common secret shapes from response_text /
action_target before they leave your VPC.
Asyncio variant (FastAPI / Vertex AI Agents / asyncio runtimes)
from agentguard_observe import AsyncClient
async with AsyncClient(
api_key=KEY,
agent_id=AGENT_ID,
signing_secret=SIGNING_SECRET,
) as client:
await client.observe(attack_id="...", response_text=reply.text)
The surface is identical — every method is awaitable.
Fetching your signing secret
The signing secret is a SEPARATE value from your API key. The API key is your bearer identifier (transmitted in the X-AgentGuard-Key header on every request); the signing secret is the HMAC key used to compute the request signature and is never transmitted on the wire. Together they give MrProbe cryptographic proof that a /v1/observe payload originated from your customer deployment and wasn't tampered with in transit.
One-time fetch (after registering your agent):
curl -H "Authorization: Bearer $YOUR_JWT" \
https://api.mrprobe.dev/api/v1/agents/$AGENT_ID/webhook/signing-secret
The response is:
{
"agent_id": "...",
"signing_secret": "<64 hex chars>",
"derivation": "HKDF-SHA256(master_key, api_key, info='agentguard-observe-v1')"
}
Store the value in your secret manager (AWS Secrets Manager / GCP Secret Manager / HashiCorp Vault / Kubernetes Secret) and load it as AGENTGUARD_SIGNING_SECRET in your runtime. The endpoint is idempotent — calling it again returns the same value, so a customer who lost the secret can re-fetch it without rotating the api_key.
Why not just use the API key as the HMAC secret? Because then capturing one signed request (TLS-terminating proxy logs, mistakenly logged headers, etc.) gives an attacker the secret AND the ability to forge any future request — defeating the whole point of HMAC. With the values separated, capturing a signed request reveals only the api_key + ts + nonce + sig + body, none of which let the attacker recover the signing secret.
What the SDK does for you
| Concern | What we do | Why |
|---|---|---|
| Authentication | Adds X-AgentGuard-Key header from your API key (bearer identifier) |
BE looks up the agent row by api_key |
| Request signing | HMAC-SHA256 over {ts, nonce, method, path, sha256(body)} keyed by signing_secret; X-AgentGuard-Signature: v1=<hex> |
Replay protection (BE rejects requests outside ±5 min) and tamper detection. signing_secret is held only by you, never transmitted |
| Retry on transient failure | 3 attempts, full-jitter exponential backoff (250 ms → 8 s cap) | Handles MrProbe deploys + DNS blips without manual retry code |
| Outbound redaction | Regex-strips JWTs, AWS keys, GCP private keys, OpenAI / Anthropic / Google / Stripe / GitHub / Slack tokens before send | Prevents your agent from accidentally exfiltrating secrets to MrProbe |
| Eager validation | Length + enum checks at the call site | 422-round-trip turns into a ValueError with a clear message |
| Connection pooling | One httpx.Client (or AsyncClient) per Client instance |
Single keep-alive socket; sub-50ms calls |
What the SDK does NOT do (intentional v1 scope):
- No custom in-memory queue / batch / drain semantics — every
observe()is a single immediate POST. If you need batching, wrap the SDK in your own queue. - No automatic correlation between inbound + outbound. You pass
attack_idexplicitly so a future SDK refactor can't invent a hidden-state bug. - No DLP — the redaction bank is intentionally small (well-known token shapes only). Layer your own DLP in front for full coverage.
Testing your integration
After registering an Email Agent in MrProbe, run a canary attack against your registered agent:
- In the MrProbe UI, open the agent → Connection check → "Send canary".
- MrProbe sends a benign test message to your mailbox.
- Your agent processes it (no harm — the canary contains no instructions).
- Your agent calls
client.observe(...)with the canary'sattack_id. - MrProbe shows a green tick on the End-to-end ready card.
Both halves of the loop must be green before you run a real security campaign.
Connection check from agentguard_observe
from agentguard_observe import Client
with Client(
api_key=KEY,
agent_id=AGENT_ID,
signing_secret=SIGNING_SECRET,
) as client:
# No-op observation against a sentinel attack id — verifies auth +
# signing without polluting any real campaign.
try:
client.observe(attack_id="canary-sdk-selftest")
print("✓ SDK can reach MrProbe and authenticate")
except Exception as exc:
print(f"✗ SDK self-test failed: {exc}")
Configuration
| Argument | Default | Description |
|---|---|---|
api_key |
(required) | The webhook key surfaced at agent registration. Treat as a secret. |
agent_id |
(required) | The agent's UUID. |
base_url |
https://api.mrprobe.dev |
Override for on-prem / EU-residency / staging. |
timeout_s |
10.0 |
Per-request timeout. |
retry |
RetryPolicy(max_attempts=3, base_delay_s=0.25, max_delay_s=8.0) |
Tune via from agentguard_observe.retry import RetryPolicy. |
redact |
True |
Set False to disable outbound secret redaction (NOT RECOMMENDED). |
sign |
True |
Set False to send unsigned requests during the 30-day deprecation window. |
http_client |
None |
Pass your own httpx.Client / httpx.AsyncClient to share its connection pool. |
Sample apps
The samples/ directory ships two complete reference integrations that
mirror what the v1 customer base actually deploys:
samples/fastapi_middleware/— a FastAPI app that wraps any async agent handler and callsAsyncClient.observe(...)automatically.samples/vertex_cloud_function/— a 1st-gen GCP Cloud Function that wraps a Vertex AI Agent.
More samples (Bedrock Lambda, Azure Function, Express middleware) ship in
v1.1 — file an issue or ping support@sniffr.ai if you need them sooner.
Versioning + wire-format guarantee
The SDK follows semver. The wire format (request body, headers, signing
algorithm) is part of the SDK's public API — any incompatible change
on either the SDK or the BE bumps the major version of both, and the
SDK ships a vN+1 signing-string variant alongside vN for at least
30 days of overlap.
Licence
MIT — vendor freely.
Support
- Bugs / feature requests: GitHub Issues (label
sdk:python) - Email: support@sniffr.ai
- Security disclosures: security@sniffr.ai (PGP-encrypted preferred)
Project details
Release history Release notifications | RSS feed
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 agentguard_observe-0.1.0.tar.gz.
File metadata
- Download URL: agentguard_observe-0.1.0.tar.gz
- Upload date:
- Size: 45.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df45e9dcbf75fe9fbc24d976ad0653c0036f9524bb4a6615ac2413e3eca0309d
|
|
| MD5 |
4ed3722a673aaf34c68fcf2ba7b6b217
|
|
| BLAKE2b-256 |
ac4d8ed368e8a11208b14fb3734513589f5c114fad9e0afa50240568cec2e610
|
File details
Details for the file agentguard_observe-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agentguard_observe-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e654e121f537e44a863dd99245cfb6494ce9a54fb964e24c000efe502fe0f70e
|
|
| MD5 |
5794f2b056331278b168c1b48c1b1163
|
|
| BLAKE2b-256 |
bd547dea44b398c2e5f14331bfb5f98062c5b710e8ae42220f73c5b6f99ea097
|