Give agents tools.
Define their boundaries.
Make useful operations available to your agent. Select its tools, apply configured rules at the action boundary, and inspect the result.
A useful tool.
An explicit boundary.
An agent requests an operation. Configured validation and policy decide whether the selected adapter can run. The result identifies what happened.
- Choose which tools the agent can discover.
- Apply policy at configured action boundaries.
- Identify mock and real adapter results.
- Retain records with a configured audit writer.
Send the report to operations.
Illustrative walkthrough · no message is sent
Claude Code in 2 minutes.
Install PyActuator 0.0.17, the current PyPI release, connect Claude Code over MCP, and refuse one order before the adapter runs. The refusal is written to a local audit file. PyActuator is its own package. This path does not start another Optophi package.
Install the current release.
These commands match PyPI pyactuator 0.0.17, the current release.
python -m pip install 'pyactuator[mcp]==0.0.17'Register the MCP server with Claude Code.
This is the command in the 0.0.17 guide docs/guides/mcp-server.md. The profile communications is a built-in profile. pyactuator mcp serve --help accepts --profile, and pyactuator tools profiles lists communications.
claude mcp add pyactuator -- pyactuator mcp serve --profile communicationsThe command adds the PyActuator MCP server and offers the built-in communications profile.
A risky order stops before the adapter runs.
Save the program below as refuse.py, or download the same file, then run it. A limit of 10000 refuses a DEMO order of 100 at 250. The policy gate returns POLICY_DENIED and does not call the broker adapter.
python refuse.py# PyActuator 0.0.17. python -m pip install 'pyactuator[mcp]==0.0.17'
import json
from decimal import Decimal
from pyactuator.core.audit import AuditQueryFilters
from pyactuator.core.policy import LimitNotional
from pyactuator.store.sqlite_audit import SqliteAuditWriter
from pyactuator.sync import invoke, run
audit = SqliteAuditWriter("audit.sqlite")
result = invoke(
"pyactuator.trading.place_order",
{
"symbol": "DEMO",
"side": "buy",
"quantity": "100",
"order_type": "limit",
"limit_price": "250",
"client_order_id": "demo-order-001",
},
policy_enforcer=LimitNotional(Decimal("10000")),
audit_writer=audit,
)
record = run(audit.query(AuditQueryFilters(tag="policy_denied", limit=1))).records[0]
print(
json.dumps(
{
"success": result["success"],
"code": result["error"]["code"],
"message": result["error"]["message"],
"adapter_class": result["adapter"]["class"],
"adapter_real": result["adapter"]["real"],
"audit_operation": record.operation,
"audit_success": record.success,
"audit_tags": list(record.tags),
},
indent=2,
)
){
"success": false,
"code": "POLICY_DENIED",
"message": "notional 25000 exceeds limit 10000 (qty=100, price=250)",
"adapter_class": "MockExecutionClient",
"adapter_real": false,
"audit_operation": "pyactuator.trading.place_order",
"audit_success": false,
"audit_tags": [
"policy",
"policy_denied"
]
}adapter_real is false. The envelope names MockExecutionClient so a mock configuration stays visible. The same run prints two lines on stderr: the analytics extra is not installed, and the policy denial. The order call does not use the analytics extra.
The refusal is a row in the audit file.
refuse.py writes audit.sqlite through SqliteAuditWriter and reads the row tagged policy_denied. That row is the end of this flow. Durable audit is optional: with no writer configured, the default is to drop the record.
A small tool surface.
A clear place to begin.
This profile exposes the Slack messaging operation over local MCP. Save the file, install the MCP extra, and start the server.
pip install "pyactuator[mcp]"
pyactuator mcp serve --profile communications-demo --profiles-file profiles.jsonA profile limits discovery. Policy and credentials remain host configuration. Follow the selected runtime's guide for its supported operations, adapters and execution behavior.
Read the PyActuator package guide{
"profiles": {
"communications-demo": {
"include": ["pyactuator.comms.slack_send"]
}
}
}
This native PyActuator profile controls tool discovery. Policy, credentials, and durable audit are configured by the host runtime.