MCP Python SDK
autociso-mcp-client is the first-party Python SDK for the AutoCISO MCP server. It gives you a typed method per tool, sync and async clients, and built-in retries.
Install
pip install autociso-mcp-client
The import name is autociso_mcp:
from autociso_mcp import AutocisoMcpClient
Create a client
base_url is required — there is no default. Pass the full URL including /mcp:
import os
from autociso_mcp import AutocisoMcpClient
client = AutocisoMcpClient(
base_url="https://api.autociso.io/mcp",
token=os.environ["AUTOCISO_MCP_TOKEN"], # mcp:read scope
)First call
Each tool maps 1:1 to a namespaced, snake_case method. Use the sync client for scripts or the async client inside an event loop:
import os
from autociso_mcp import AutocisoMcpClient
client = AutocisoMcpClient(
base_url="https://api.autociso.io/mcp",
token=os.environ["AUTOCISO_MCP_TOKEN"],
)
posture = client.posture.summary()
print("Overall score:", posture.overall_score)
stale = client.access.stale_accounts()
print("Stale accounts:", len(stale))import asyncio, os
from autociso_mcp import AsyncAutocisoMcpClient
async def main():
client = AsyncAutocisoMcpClient(
base_url="https://api.autociso.io/mcp",
token=os.environ["AUTOCISO_MCP_TOKEN"],
)
posture = await client.posture.summary()
print("Overall score:", posture.overall_score)
asyncio.run(main())Results are PII-masked — emails appear as a***@domain.com and personal names are redacted (to resolve an employeeId to a name, see Employee identity):
{
"overallScore": 72,
"staleAccounts": 14,
"openRisks": 6,
"isoGapsOpen": 23,
"topConcern": "14 stale accounts pending review"
}
Typed results
Methods for typed tools return TypedToolResult[XResult]. .data, .is_error and .raw work as before and never raise. .result validates .data lazily into a generated Pydantic model and raises ResultSchemaError on a mismatch; the error carries the tool name and field paths only, never values. Tools without a published schema return a plain result. The package ships py.typed, and the client prefers structuredContent, falling back to the text block.
res = await client.access.anomaly_discovery()
res.data["totalCount"] # unchanged dict access
res.result.total_count # typed, validated on first access
Patterns
- Typed method per tool.
client.access.stale_accounts(),client.risk.get(id="risk_…"),client.iso.gap_status()map to the same tools as the TypeScript SDK. See the Tool Catalog. - No
org_id, ever. Your org is bound to the token on the server (see Org binding). There is no org argument — you can’t query another tenant. By design. - Retries on reads. Idempotent reads retry automatically on
429and transient errors with exponential backoff and jitter (capped at ~3 attempts). - Writes are not auto-retried. There are five write tools (
risk_create_draft,iso_audit_simulate,supplier_questionnaire_clone,supplier_questionnaire_update_question,supplier_questionnaire_update_option), for exampleclient.risk.create_draft(title="…"). None is ever retried automatically: the server has no idempotency key, so a blind retry could create a duplicate or spend a second AI request. - Timeout override. Pass
timeout=per call when a tool is slow. call_rawescape hatch. For a tool the typed surface doesn’t cover, call it by name; the result is an untypeddict:
raw = client.call_raw("audit_trail_search", {"limit": 25, "offset": 0})
# raw is a dict — validate before useError handling
Tool failures raise typed exceptions (e.g. AutocisoScopeError, AutocisoRateLimitError). See MCP Errors and Retries for the full mapping and retry behavior.
Was this page helpful?