portfolio-optimiser/docs/research/2026-06-24-maf-vs-claude-agent-sdk.md
Kjell Tore Guttormsen 25bb07a46e docs(research): MAF vs Claude Agent SDK comparison + D7 sibling-impl decision
Verified comparison of Microsoft Agent Framework (ground-truth introspection of
installed agent-framework-core 1.9.0 + Microsoft Learn) and Claude Agent SDK
(Anthropic docs + npm/PyPI). Grounds decision D7: rebuild the same method on
Claude Agent SDK as a separate sibling repo, in sequence, sharing only the
spec + golden/conformance suite — not orchestration code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fif1r1En5W542HbZV88yMH
2026-06-24 06:51:24 +02:00

192 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Microsoft Agent Framework (MAF) vs. Claude Agent SDK — a practitioner's comparison
> **Date:** 2026-06-24 · **Audience:** people who know agent frameworks but not these two specifically.
> **Why this exists:** portfolio-optimiser is built on MAF; the same method will be rebuilt on Claude
> Agent SDK as a separate sibling repo (decision D7). This doc grounds that work and is a standalone
> reference.
>
> **Verification status:** MAF facts were ground-truth-introspected against the installed
> `agent-framework-core==1.9.0` in this repo + Microsoft Learn. Claude Agent SDK facts were verified
> against Anthropic docs (code.claude.com) + npm/PyPI. Items the research could not confirm are marked
> **(unverified)** and must not be repeated as fact.
---
## TL;DR — they are two different *kinds* of thing
- **MAF is a multi-agent *orchestration framework*.** You declare the topology. It ships named,
first-class collaboration patterns (Sequential, Concurrent, GroupChat, Handoff, Magentic) and an
explicit, deterministic graph workflow engine. The control plane is **explicit and yours**. It is
**model-agnostic** (provider abstraction), Azure/enterprise heritage (the merger of Semantic Kernel
+ AutoGen), with OpenTelemetry observability built in.
- **Claude Agent SDK is a single autonomous *agent harness*.** It exposes the same agentic loop that
powers Claude Code (gather context → act → verify → repeat). You steer **one** capable agent with
tools, hooks, permissions, and the ability to delegate to subagents. The control plane is
**implicit** — Claude decides the sequence; you constrain it with hooks. It is **Claude-only** (but
multi-cloud), coding/ops heritage, observability is DIY.
**Mental model:** MAF = you are the *conductor writing the score*; the agents are players executing
your composition. Claude Agent SDK = you hire one very capable *generalist*, give it tools and
guardrails, and let it decide how to get the job done — delegating to *specialists* (subagents) when
it judges it useful.
The single sharpest axis: **MAF makes orchestration explicit and declarative; Claude Agent SDK makes
it implicit and emergent, bounded by hooks.**
---
## Side-by-side
| Dimension | Microsoft Agent Framework (MAF) | Claude Agent SDK |
|---|---|---|
| **What it is** | Orchestration framework for agents + workflows | Programmatic harness over the Claude Code agent loop |
| **Lineage** | Successor to & merger of Semantic Kernel + AutoGen | Renamed from "Claude Code SDK"; evolved beyond coding |
| **Maturity** | **GA 1.0.0** (Python, 2026-04-02); installed here: core 1.9.0 | Pre-1.0, very fast cadence (TS at 0.3.x, 225+ npm releases) |
| **Languages** | Python ≥3.10 and .NET (C#) — not full parity | Python ≥3.10 and TypeScript — TS slightly richer |
| **Models** | **Model-agnostic** via provider packages (Azure OpenAI, Foundry, OpenAI, Ollama, Anthropic*, Gemini*, Bedrock*) | **Claude only**, but multi-cloud: Anthropic API / Bedrock / Vertex / Azure Foundry |
| **Core primitive** | `Agent` (LLM loop) + `WorkflowBuilder` (graph of `Executor` nodes) | `query()` async generator + `ClaudeSDKClient` (persistent) |
| **Multi-agent** | **Named patterns**: Sequential, Concurrent, GroupChat, Handoff, Magentic | **No named patterns** — compose yourself via subagents / N calls |
| **Determinism** | Native: `FunctionExecutor` node + conditional edges in the graph | Via `PreToolUse` hook that denies until a step has run |
| **Tools / MCP** | MCP **client** (stdio/HTTP/WS) + agent can be exposed **as** MCP server | Built-in tools + custom tools via in-process MCP server; MCP **client** |
| **HITL** | `ToolApprovalMiddleware`, `RunContext.request_info()`, Magentic plan-review | `permission_mode`, `allowed_tools`, `PreToolUse`/`PermissionRequest` hooks |
| **Observability** | **Built-in OpenTelemetry** (GenAI semantic conventions, metrics, evals) | **DIY** via `PostToolUse`/`SubagentStop` hooks; no built-in OTel (unverified) |
| **State / memory** | Sessions, file/Redis history, workflow checkpointing, compaction strategies | Session resume (JSONL on your FS), auto-compaction, `PreCompact` hook |
| **License** | MIT | "SEE LICENSE IN README" + Anthropic Commercial ToS (FOSS license **unverified**) |
| **Runs where** | Your process; Azure/Foundry-first for hosted tools | Your process / filesystem; bundles a Claude Code binary |
\* beta provider packages — require `--pre`, not production-ready.
---
## The dimensions that matter most
### 1. Multi-agent orchestration — the biggest divide
**MAF** gives you the topology as a first-class, named object. All five live in a separate package,
`agent-framework-orchestrations` (GA 1.0.0; **not** bundled with core):
| Pattern | Builder | Behaviour |
|---|---|---|
| Sequential | `SequentialBuilder` | A → B → C, output chained |
| Concurrent | `ConcurrentBuilder` | Same input fan-out, results aggregated |
| GroupChat | `GroupChatBuilder` | Star topology; an orchestrator picks the next speaker (round-robin / prompt / custom selection fn) |
| Handoff | `HandoffBuilder` | Point-to-point control transfer between agents |
| Magentic | `MagenticBuilder` | Magentic-One manager that plans, tracks a progress ledger, and coordinates specialists dynamically |
> Note: Microsoft itself warns Magentic is "untested … outside of the original Magentic-One design."
> Treat as beta for novel use cases.
**Claude Agent SDK** has **no** equivalent. The multi-agent mechanism is **subagents**: your main
agent spawns separate agent instances (via the `Agent` tool) defined programmatically through
`AgentDefinition` (or `.claude/agents/*.md`). Key properties:
- The main agent decides *when* to delegate, from each subagent's `description`. You can also ask
explicitly ("use the X agent").
- A subagent does **not** inherit the parent's history; the only channel in is the spawn prompt, and
it returns **only its final answer** (not intermediate steps).
- Subagents can run in **parallel** and **nest** (up to 5 levels, since Claude Code 2.1.172).
- There is **no built-in coordination protocol** between peer agents — no debate loop, no consensus,
no voting. To coordinate N peers you run N `query()` calls and aggregate yourself, or have one
orchestrator agent fan out to subagents. (A `Workflow` tool for 10100-agent scale exists in the
**TypeScript** SDK; availability in Python is **unverified**.)
**So:** if your design *is* a named multi-agent topology (a maker-checker debate, a planner-manager),
MAF hands it to you. On Claude Agent SDK you hand-roll it from subagents + your own glue.
### 2. Determinism / forcing a blocking step
This is MAF's headline differentiator and directly relevant to a "mandatory deterministic validator."
- **MAF:** add a `FunctionExecutor` — a pure Python function, no LLM — as a node in `WorkflowBuilder`,
with conditional edges (`EdgeCondition`, `SwitchCaseEdgeGroup`). It blocks the graph until it
returns. The sequence is **declared in code**; the validator is a graph node by construction.
- **Claude Agent SDK:** there is no declarative graph. You force a sequence with a `PreToolUse` hook
that returns `permissionDecision: "deny"` for any tool until your validator tool has run. You can
also rewrite tool input (`updatedInput`), inject context, or replace tool output before the model
sees it. Powerful, but the determinism is *enforced by interception*, not *expressed as structure*.
**Net:** stronger, more legible deterministic control → MAF. More flexible autonomy with guardrails →
Claude Agent SDK.
### 3. Model coupling
- **MAF** abstracts the provider behind `BaseChatClient`; you swap Azure OpenAI ↔ OpenAI ↔ Foundry ↔
local OpenAI-compatible endpoints ↔ (beta) Anthropic/Gemini/Bedrock. Instructions, tools, and
middleware are portable; provider-specific run-options (`OpenAIChatOptions`, `FoundryChatOptions`)
are not.
- **Claude Agent SDK** runs **Claude only** — but across Anthropic API, Amazon Bedrock, Google Vertex,
and Azure AI Foundry via env vars (`CLAUDE_CODE_USE_BEDROCK/VERTEX/FOUNDRY`), with no code change.
Model *aliases* (`opus`/`sonnet`/`haiku`/`fable`/`inherit`) or full IDs in `ClaudeAgentOptions` /
`AgentDefinition`.
### 4. Tools, MCP, and data access
Both are MCP **clients**. The difference is symmetry:
- **MAF:** MCP client over three transports (`MCPStdioTool`, `MCPStreamableHTTPTool`,
`MCPWebsocketTool`); and crucially `agent.as_mcp_server()` exposes an agent **as** an MCP server for
other clients. Function tools via `@tool`/`FunctionTool`.
- **Claude Agent SDK:** built-in tools (`Read`/`Write`/`Edit`/`Bash`/`Grep`/`Glob`/`WebSearch`/
`WebFetch`/`Agent`/`AskUserQuestion`); custom tools via `@tool` + `create_sdk_mcp_server` running
**in-process** (exposed as `mcp__{server}__{tool}`); external MCP servers via `mcp_servers`.
### 5. Observability
- **MAF:** OpenTelemetry is built in (`agent_framework.observability`, GenAI semantic conventions,
token-usage histograms, workflow/edge/MCP spans) plus an experimental eval harness
(`evaluate_agent`, `evaluate_workflow`).
- **Claude Agent SDK:** you build it from hooks (`PostToolUse`, `SubagentStop`, `parent_tool_use_id`),
asynchronously if you want non-blocking logging. No first-class OTel integration found **(unverified)**.
---
## What each is best at
**MAF wins when** you need explicit, auditable orchestration; a *blocking deterministic step* between
LLM turns expressed as structure; named multi-agent debate/handoff patterns out of the box; model
portability; built-in telemetry from day one; or you are migrating from Semantic Kernel / AutoGen.
**Claude Agent SDK wins when** you want one strong agent that lives on *your* infrastructure (your
filesystem, your secrets, no vendor sandbox); coding/DevOps automation (its native turf); fast
prototype → production with `query()`; rich MCP-client integration; and you are happy to be on Claude
models across multiple clouds. Its orchestration is emergent, not declared — great for open-ended
autonomy, weaker for "prove the exact sequence ran."
---
## Implication for portfolio-optimiser (decision D7)
The product is a **maker-checker debate** that generates candidate measures, gated by a **mandatory,
blocking deterministic validator**, with a HITL learning loop.
- On **MAF** (this repo): debate = `GroupChatBuilder`; validator = a `FunctionExecutor` graph node —
blocking by construction. Direct fit; this is *why* MAF was chosen.
- On **Claude Agent SDK** (sibling repo, D7): debate = an orchestrator agent fanning out to maker /
checker subagents (or N `query()` calls + your aggregation); the validator is enforced by a
`PreToolUse` hook that denies any action until the validator tool has run. Fully possible, but you
hand-roll the orchestration and the "blocking" is interception, not a declared edge.
**Because the two paradigms differ this much, a single shared runtime abstraction is the wrong move.**
The portable, framework-neutral asset is the **method**, not the orchestration code: the Agent Skill,
the IR contract, the validator rules, the synthetic reference domain, and — most valuable for a
fair comparison — a **shared golden/conformance suite** (synthetic domain inputs → expected validator
verdicts) that both implementations must pass. That is what makes "the same method on two frameworks"
a measurable claim rather than two vaguely similar projects.
---
## Open / unverified items (do not repeat as fact)
- Exact date of the Claude Code SDK → Claude Agent SDK rename (secondary sources say ~Sept 2025).
- The code's FOSS license (npm says "SEE LICENSE IN README").
- `canUseTool` callback details; `Workflow` tool availability in the **Python** Claude Agent SDK.
- Claude Agent SDK built-in OpenTelemetry support (none found, not definitively absent).
- MAF beta Anthropic provider package name (`agent-framework-claude` assumed, not confirmed).
## Sources
- Microsoft Agent Framework: `learn.microsoft.com/agent-framework`, `github.com/microsoft/agent-framework`,
+ ground-truth introspection of installed `agent-framework-core` 1.9.0.
- Claude Agent SDK: `code.claude.com/docs/en/agent-sdk` (overview, migration-guide, subagents, hooks,
custom-tools), `github.com/anthropics/claude-agent-sdk-{python,typescript}`, npm/PyPI version checks.