Skip to content

v0.2.0 — Phase 2: Differentiate, go beyond

Latest

Choose a tag to compare

@tommasobolis tommasobolis released this 30 Oct 15:58
· 0 commits to develop since this release
882f51d

donkey-kit 0.2.0: Phase 2, Differentiate, go beyond. This release adds the capabilities a stock OpenAI client with a base_url can't offer, because they depend on the platform behind the gateway. You get governed MCP tool access, publication to Exchange from your code and CI, on-behalf-of user identity, and human-in-the-loop approvals, including the gateway's own CIBA step-up. You also get an A2A server built on the official a2a-sdk, declarative refusal handlers, typed custom policies, and the OpenAI Agents SDK as the second deep adapter.

pip install --upgrade "donkey-kit==0.2.0"
pip install --upgrade "donkey-kit[all]==0.2.0"   # now includes mcp and a2a

Breaking changes

From 0.1.3. No name was removed from donkey_kit, and no extra was dropped. These changes can still affect existing code. Each has its own section, with the fix, in MIGRATION.md.

  • New refusal types. A CIBA pending 401 is now GatewayApprovalPending, which except AuthError: no longer catches, and a CIBA denial 403 is GatewayApprovalDenied (#1320). Google Model Armor and CrowdStrike AIDR blocks are ContentSafetyBlocked instead of PolicyViolation(policy="unknown"), and their donkey.policy.type reads content_safety (#1178, #1176).
  • classify is a Classifier object. Calling it works as before, but inspect.isfunction(classify) is False. To type a custom policy, use classify.register() instead of wrapping it (#1152).
  • Calls that raised NotImplementedError now run. donkey publish and donkey verify are visible and do the work: they exit 0, 1 or 2, and no longer exit 3 (#218, #1352). discover(), donkey.registry.search(), resolve_mcp() / resolve_agent(), explain(), Publication.verify(), the ToolSet bindings and derive_descriptor(mode="auto…") make control-plane calls. An except NotImplementedError: fallback around them is no longer reached. A gateway credential is held back from a base_url set in the project files that isn't a regional Anypoint host (ADR 0019, #1479).
  • donkey.tools.discover() defaults to governed=True. Pass governed=None for the unfiltered list (ADR 0017, #1424).
  • GovernanceCriteria is imported from donkey_kit again. from donkey_kit.experimental import GovernanceCriteria still works, but it emits a DeprecationWarning (#1425).
  • Install changes. [cli] needs typer>=0.26.0 and rich>=13.8.0, and no longer installs click (#1196). [all] adds mcp and a2a, and [adk] installs litellm[mcp] (see Extras changes).
  • AgentKilled is terminal for the Donkey that saw it. Build a new Donkey to carry on (#220).
  • refusal_translator leaves the __all__ of the Agent Framework and Strands adapters. A direct import still works (#1422).
  • A configured URL with a user name or password is refused with ConfigError before anything is sent. Before, httpx sent those credentials to the host as Authorization: Basic (#1478).
  • AgentFrameworkAdapter(...).policy_middleware() without agent_framework installed raises the install-hint ImportError instead of NotImplementedError (#1503).

From a 0.2.0 pre-release (0.2.0.devN, 0.2.0rc1). These surfaces first shipped in the 0.2.0 pre-releases, and they changed before the final with no deprecation cycle. The full list is in MIGRATION.md. The changes most likely to need a code edit:

  • Identity: donkey.as_user(id_token=...) is donkey.as_user(token=...), and token_exchange_path is gone. as_user() refuses an llm_proxy_url from the working-directory config files. Set it in the environment or in code, or set DONKEY_TRUST_PROJECT_CONFIG=1.
  • Tools: ToolSet.openai() is ToolSet.openai_agents(). A per-tool binding raises AuthError under a different as_user() user, so bind inside the block that runs the agent. A gateway with a plain http:// public URL is refused unless DONKEY_ALLOW_HTTP=1 is set.
  • Approvals: donkey.approvals is per client, and the module-level instance is gone (ADR 0018). An unknown approval id raises ApprovalNotFound, which is still a KeyError. ApprovalRejected and GatewayApprovalDenied share the new ApprovalError base. A resumed approval runs as its requester. The adapter helpers return Approval objects, with the framework object on .native. A Strands approval covers exactly one call. require_approval() on a whole-server binding raises ConfigError.
  • CLI and Action: donkey dev --version is --agent-version. The --json documents carry their own format key (publish_format, verify_format, expose_format). A failed publish --json prints its outcome and exits 1. donkey verify exits 2 with no gateway credential. Pass the Action's client-secret from secrets.*; its skipped output is gone.
  • Errors: the ADK, LangGraph and Agent Framework helpers raise the install-hint ImportError instead of a bare ModuleNotFoundError.

Highlights

  • Governed MCP tool access: donkey.tools.discover() finds governed MCP servers in Exchange. ToolSet binds them to LangGraph, the OpenAI Agents SDK, ADK, Strands, MS Agent Framework, Anthropic, CrewAI and LlamaIndex through the governed gateway (#221, #1308, #1310). A server that isn't governed is refused with UngovernedMcpServer (#1199), and the gateway's tool-access denial is typed as ToolNotInProfile (#1362). donkey.tools.warm() pre-fills the discovery cache (#1333), and discovery emits a span with cache hit and miss counters (#1351).
  • Publication to Exchange: donkey scan derives a deterministic model of your agents, tools and MCP servers (#1192). donkey publish registers MCP servers and agents in Exchange with a full asset page, including a "Changes since" section (#1259, #1270, #1277, #1291, #1358). donkey verify checks what you published against the live tools and agent card (#1355, #1361). The scan-and-publish GitHub Action runs this from CI (#1263).
  • On-behalf-of identity: donkey.as_user(token=...) forwards the user's token to the gateway's OBO policy on model calls and MCP bindings. It never falls back to the service identity (#1290, #1293, #1353, #1369, #1472).
  • Human in the loop: @donkey.tool(approval="required") and ToolSet.require_approval() pause gated tools with each framework's own mechanism: LangGraph interrupt(), OpenAI Agents needs_approval, ADK tool confirmation and Strands interrupts (#1154, #1155, #1201, #1233, #1239, #1241). The gateway's CIBA step-up arrives as typed, waitable GatewayApprovalPending / GatewayApprovalDenied (#1329, #1376).
  • A2A: donkey.serve() wraps the official a2a-sdk, with a governed inbound (#1212, #1225). donkey dev runs a simulated A2A ingress, and donkey expose / donkey preflight put the gateway instance in front of your agent (#1287, #1330).
  • Refusals: declarative refusal handlers (donkey.on(...)) react but never un-refuse (#1146). A classification registry turns custom gateway policies into typed exceptions (#1152). The kill switch stops runs cleanly (#1151), and Google Model Armor and CrowdStrike AIDR blocks are typed as ContentSafetyBlocked (#1180, #1461).
  • A second deep adapter: the OpenAI Agents SDK joins LangGraph as a conformance-gated adapter (#1164).
  • Around them: an inbound correlation ID is carried across MCP and A2A hops (#1258), and donkey.run() keeps a run ledger (#1379). You can set the business group per request (#1190) and send a provider key for Dynamic-key routes (#1253). The budget has a stated concurrency contract (#1189) and reads the upstream provider's rate-limit window (#1246).
  • Hardening from the 0.2.0 code review:
    • Credentials are stripped on cross-origin MCP redirects (#1459).
    • donkey dev, serve() and scanned endpoints are hardened for non-loopback use (#1458).
    • An out-of-range budget reset header no longer crashes send() (#1456).
    • The 0.2.0 API stability tiers are settled, with a snapshot of the stable surface (#1460).

Extras changes (§8.4)

  • mcp is back (mcp>=1.24) for ToolSet binding. It was dropped in 0.1.2 and last shipped as mcp>=1.2.
  • a2a is back (a2a-sdk[http-server]>=1.2.0, uvicorn>=0.30) for donkey.serve(). It was dropped in 0.1.2 and last shipped as a2a-sdk>=0.1.
  • all now installs llm, langgraph, otel, cli, local, a2a, mcp, so it adds mcp and a2a (#1436).
  • adk: litellm>=1.84 becomes litellm[mcp]>=1.84. From litellm 1.106, model() with tools needs mcp (#1289).
  • cli: typer>=0.15.4 becomes typer>=0.26.0, and rich>=13.0 becomes rich>=13.8.0 (#1200).
  • The base dependencies, the Python floor (3.10), the donkey console script and the pytest plugin entry point are unchanged. Extras stay floors only, with no upper pins.

Verification-status changes (§0.3)

In docs/verified-apis.md:

  • MCP discovery and binding. Discovery and ToolSet binding through the governed gateway move from blocked to VERIFIED (LIVE), 2026-10-28 (§9, #221). They run over framework binding classes that are VERIFIED (local round-trip) (#636–#652). The tool-access denial shape is VERIFIED (LIVE) (#1359).
  • MCP and A2A resolution. Endpoint resolution and the governed-state reads behind explain() are VERIFIED (LIVE) (§6, #1297, #1298, #1354).
  • Publication. Exchange search, and the publication of scanned MCP servers and agents, move from blocked to VERIFIED (LIVE) (§7, #53, #218, #1260).
  • donkey verify. The read-back of a published version's MCP tools and agent card, which donkey verify compares against, is VERIFIED (LIVE), 2026-10-29 (§7, #991, #1354).
  • as_user() header. The user-token header is VERIFIED (LIVE), 2026-10-29: Authorization: Bearer <token>, on model calls and MCP bindings (§13, #255).
  • CIBA step-up. The pending (401), denied (403) and initiation-failure (502) shapes are VERIFIED (LIVE), 2026-10-28 and 2026-10-29, on model and MCP tools/call traffic (§4, #1320, #1380).
  • Content safety. The Google Model Armor rejection is VERIFIED (LIVE), 2026-10-19 (§4, #1178).
  • CrowdStrike AIDR. The AIDR rejection is VERIFIED (POLICY SOURCE), a new evidence class (ADR 0016). Its shape is read from crowdstrike-aidr-guardrails-policy 1.0.0, not captured live, because no proxy runs it. classify() types it as ContentSafetyBlocked(provider="crowdstrike-aidr"). The live capture is still open (#1184), and a capture replaces the derived shape if they differ (§4, #1176, #1429).
  • Still blocked, behind a NotImplementedError("blocked on verification: …"):
    • the lockfile (donkey.tools.lock(), discover(locked=True));
    • the governed-state search filters (search(governed=, environment=, domain=));
    • Publication.preview() / export();
    • the hidden donkey status.
  • No row moved from verified to blocked.

Acceptance

donkey-kit==0.2.0 from TestPyPI: passed. This was a live run of all seven groups (./run.sh --version 0.2.0 --index testpypi) on Python 3.12, against the artifact this Release publishes.

  • Proxies: all 24 declared proxies were configured, and none was skipped for missing credentials.
  • Results: 559 passed, 33 skipped, 0 failed.
Session Passed Skipped
[all] + langchain-mcp-adapters (data plane, governance, public surface, blocked, telemetry, frameworks) 415 27
[all,adk] 21 1
[all,strands] 22 1
[all,agent_framework] 12 1
[all,openai-agents] 27 1
[all,anthropic] 11 0
[all,crewai] + crewai-tools[mcp] 12 1
[all,llamaindex] + llama-index-tools-mcp 9 0
packaging 30 1
  • Coverage map: consistent. 108 features are mapped, and 321 of 330 collected tests are claimed by a feature.
  • Offline published gate (groups 3, 4 and 5): passed in the TestPyPI publish run for 0.2.0.
  • Release candidate: 0.2.0rc1 also passed the live run (546 passed, 33 skipped, 0 failed).

Full changelog: v0.1.3...v0.2.0 (promotion PR #1522)