AI Infrastructure

Microsoft Agent Framework 1.12.1: Caching and Replay

Upgrade Microsoft Agent Framework Python 1.12.1 safely: test GPT-5.6 cache breakpoints, Gemini tool replay, stateless reasoning, and MCP headers.

Three-part diagram of prompt blocks entering a cache checkpoint, a tool-call replay loop, and an MCP server protected at a redirect boundary
AgentPedia diagram of the three upgrade surfaces in Microsoft Agent Framework Python 1.12.1: explicit prompt caching, reasoning-aware tool replay, and origin-scoped MCP authentication. View image source.

Microsoft published the Python 1.12.1 release on July 23, 2026. It is not a matching .NET release and it does not redesign Agent Framework. The useful changes are narrower: cache control now reaches GPT-5.6 requests, Gemini function-call signatures survive reconstructed history, stateless reasoning and tool groups replay safely, and the MCP API documentation states how custom clients must scope sensitive headers across redirects.

This guide uses the tagged release, its four implementation pull requests, current Microsoft Learn guidance, Google’s thought-signature documentation, and package artifacts downloaded from PyPI on July 23, 2026. AgentPedia also ran no-network serialization and dependency checks against agent-framework-core 1.12.1, agent-framework-openai 1.11.0, and OpenAI Python 2.44.0/2.45.0. No live model call or cost benchmark was run, so the examples below are regression tests and accounting patterns rather than performance claims.

The upgrade verdict

Upgrade now when one of these paths is in your production graph:

WorkloadWhy 1.12.1 mattersAcceptance gate
Repeated long prompts on GPT-5.6Explicit breakpoints and request-wide cache policy now reach OpenAI payloadsSecond identical prefix reports cache reads; write and read token counts are visible for pricing calculations
Gemini 3 function toolsReconstructed function calls retain required thought_signature metadataApproval and persisted-history loops complete past the second model turn
OpenAI or Foundry stateless reasoningReasoning-paired function and MCP groups no longer become orphaned during replayCross-agent and middleware-terminated histories avoid replay HTTP 400s
Authenticated local MCP over HTTPThe docs now distinguish origin-scoped header_provider from headers attached to a custom clientA cross-origin redirect receives no secret header

You can defer if none of these paths is active and Python 1.12.0 is stable in your environment. If you do upgrade, do not validate only a one-turn text response. Every fix in this release sits at a state, tool, provider, or network boundary.

For a broader framework comparison, the OpenAI Agents Python guide covers orchestration primitives and tradeoffs. This article stays on the Microsoft Agent Framework 1.12.1 delta.

What shipped in Python 1.12.1

The release notes name five Python changes. Four affect this upgrade plan; the fifth promotes agent-framework-ag-ui from release candidate to stable.

AreaTagged changeOperational consequenceSource
OpenAI connectorAdds prompt_cache_options and per-content prompt_cache_breakpoint forwarding for GPT-5.6Caching is opt-in and measurable instead of silently dropping the controlsPR #7163
Gemini connectorPreserves thought_signature when a function call is reconstructedApproval, MCP, and rebuilt-history paths can continue a Gemini 3 tool loopPR #7095
Core/OpenAI/FoundryRepairs stateless replay of reasoning-paired client function calls and hosted MCP callsReconstructable encrypted reasoning groups replay; missing protected state is rejected before transportPR #7233
Core MCPDocuments custom-client header leakage risk on cross-origin redirectsApplications that supply their own HTTP client must enforce origin scopingPR #7245
AG-UIPromotes the Python package from RC to stableLifecycle status changes; it is not evidence that every host or adapter is GAPython 1.12.1 release

The release tag contains Python and .NET commits because both live in one repository. Scope the adoption decision to the Python packages named in the release notes; do not infer equivalent .NET cache, Gemini, replay, or MCP behavior from this tag.

Install the right package cohort

Create an isolated environment and record the resolver result. Python 1.12.1 requires Python 3.10 or newer.

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install \
  "agent-framework==1.12.1" \
  "openai>=2.45.0,<3"
python -m pip check

The explicit OpenAI constraint matters. The downloaded PyPI artifacts showed this version map:

DistributionVersion in the Python 1.12.1 cohortPublished dependency relevant here
agent-framework1.12.1agent-framework-core[all]==1.12.1
agent-framework-core1.12.1Core runtime
agent-framework-openai1.11.0agent-framework-core>=1.11.0,<2; openai>=2.25.0,<3
OpenAI Python SDKPin at least 2.45.0 for this featurePromptCacheOptions types arrived in 2.45.0

Provider packages do not all share the umbrella release number. Seeing agent-framework-openai==1.11.0 after installing the 1.12.1 cohort is expected.

There is also a packaging edge to catch in automation. The connector’s metadata still allows OpenAI 2.25.0 through 2.44.x, while its implementation checks for the newer type at runtime. AgentPedia reproduced the exact behavior without a network call:

openai 2.44.0
ChatClientInvalidRequestException: prompt_cache_options requires openai>=2.45.0;
upgrade the openai package to use it.

With OpenAI 2.45.0, the same preparation path retained both the request option and the content-part breakpoint. Add the SDK floor to your lock file or constraints file rather than relying on the transitive requirement.

Configure GPT-5.6 cache breakpoints

Use these two controls when you want only the breakpoints you place to participate in caching:

  1. prompt_cache_options={"mode": "explicit"} disables the implicit breakpoint on the latest message.
  2. Content.additional_properties["prompt_cache_breakpoint"] marks the end of a stable reusable prefix.

A per-content breakpoint can also be sent while the default implicit policy remains active. The explicit mode is useful when you need the request to use your marked boundaries and no automatic latest-message breakpoint. A stable prompt_cache_key gives repeated requests an application-controlled routing key; version it when the reusable policy or reference block changes.

The tagged Microsoft sample says the prefix before a breakpoint must contain at least 1,024 tokens. Put the breakpoint after stable instructions, reference material, or a catalog—not after the user-specific question.

import asyncio

from agent_framework import Content, Message
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions

STABLE_CONTEXT = (
    "Use the approved support policy. Quote the matching section and "
    "do not invent product identifiers. "
) * 80


def request_messages(question: str) -> list[Message]:
    stable = Content.from_text(
        STABLE_CONTEXT,
        additional_properties={
            "prompt_cache_breakpoint": {"mode": "explicit"}
        },
    )
    return [
        Message(role="user", contents=[stable]),
        Message(role="user", contents=[Content.from_text(question)]),
    ]


async def run() -> None:
    client = OpenAIChatClient[OpenAIChatOptions](model="gpt-5.6-luna")
    options: OpenAIChatOptions = {
        "prompt_cache_options": {"mode": "explicit"},
        "prompt_cache_key": "support-policy-v1",
    }

    for question in ("Summarize returns.", "Who approves a return?"):
        response = await client.get_response(
            request_messages(question),
            options=options,
        )
        print(response.text)
        await asyncio.sleep(2)


asyncio.run(run())

The framework applies the breakpoint only to content parts accepted by each OpenAI surface. For the Responses client these include input text, image, and file parts. Chat Completions supports its corresponding text, image, audio, and file parts. Text messages with a breakpoint stay in list form because a flattened string cannot carry part metadata. Requests without a breakpoint preserve their previous shapes.

Do not enable this on an older model and assume the framework will ignore it. PR #7163 reports that unsupported models return the API’s own 400 error: prompt_cache_breakpoint is not supported on this model.

Measure cache writes and reads

A cache hit is not the same as a saving. GPT-5.6 cache writes are billed, the stable prefix must be long enough, and a useful result depends on enough later requests reading that entry. Record four values per request:

  • total input tokens;
  • input tokens written to cache;
  • input tokens read from cache;
  • output and reasoning tokens.

In the 1.12.1 cohort, Agent Framework normalizes cached reads to cache_read_input_token_count. OpenAI Python 2.45.0 also exposes cache_write_tokens inside the raw Responses usage object, but the connector does not currently copy that field to cache_creation_input_token_count. Read both layers explicitly:

def cache_accounting(response) -> dict[str, int | None]:
    normalized = response.usage_details or {}
    raw_response = response.raw_representation
    raw_usage = getattr(raw_response, "usage", None)
    raw_input = getattr(raw_usage, "input_tokens_details", None)

    return {
        "input_tokens": normalized.get("input_token_count"),
        "cache_write_tokens": getattr(raw_input, "cache_write_tokens", None),
        "cache_read_tokens": normalized.get("cache_read_input_token_count"),
        "output_tokens": normalized.get("output_token_count"),
        "reasoning_tokens": normalized.get("reasoning_output_token_count"),
    }

AgentPedia checked this mapping with a synthetic OpenAI 2.45.0 ResponseUsage: a raw value of 1,500 cache-write tokens remained available at input_tokens_details.cache_write_tokens, while 700 cached tokens appeared as the normalized cache-read count. The normalized cache-creation field was unset. This is a no-network serialization check, not a billing benchmark.

For a pilot, aggregate by stable-prefix hash and workflow outcome:

prefix revision
  -> first request: cache-write tokens, input tokens, completed task
  -> later requests: cache-read tokens, misses, output tokens, completed tasks
  -> total provider charge / accepted task

Invalidate or version the prefix when policy text changes. Otherwise, a good cache-hit rate can hide the reuse of stale instructions.

Regression-test Gemini tool replay

Gemini 3’s generate-content tool flow can attach an opaque thought_signature to a functionCall part. Google requires clients to return the signature when that call is replayed in a later turn. Missing it can produce 400 INVALID_ARGUMENT; disabling thinking does not remove the requirement.

Before 1.12.1, Agent Framework retained the signature when the original Google Part object survived, but could lose it when another layer reconstructed FunctionCallContent from its call ID, name, and arguments. PR #7095 stores a JSON-safe representation in framework content and restores the bytes when it rebuilds the Gemini part.

In the tagged Python 1.12.1 implementation, the carrier is a text_reasoning Content immediately before the function_call content, with the base64-encoded signature stored in protected_data. The Gemini converter consumes that protected value only for the immediately following function call, then clears it. Custom persistence and serialization code must therefore preserve both content items in that order. Saving only FunctionCallContent, or separating or reordering the adjacent reasoning carrier, loses the replay state even if the call ID, name, and arguments survive.

Test all reconstruction paths you use:

Replay pathTest actionPass condition
Automatic function loopLet Gemini call a harmless deterministic tool, return its result, and continueFinal response completes without INVALID_ARGUMENT
Tool approval middlewareRequire approval, approve the call, then continueReconstructed function call still carries the signature
MCP always_requireApprove one read-only MCP call and replay itThe continuation turn succeeds
Persisted or rebuilt historySerialize approved framework history, load it into a new in-process client, and continueSignature survives the supported history representation
Corrupted historyAlter or remove the stored signature in a disposable fixtureThe failure is explicit and the bad history is quarantined

The fix is about opaque metadata, not exposing chain-of-thought. Do not log, edit, derive meaning from, or manufacture signatures. Preserve them as provider-owned protocol state.

For current Gemini model and Interactions API context, see the Gemini 3.6 Flash developer guide. That API has different state handling; do not transfer the generate-content replay shape blindly.

Test stateless reasoning replay

Reasoning responses can bind protected reasoning items to tool calls. In a stateless or cross-agent replay, removing the protected reasoning while retaining its function call and result creates an invalid historical group.

PR #7233 extends the repair beyond hosted mcp_call items to client-side function calls. It covers completed function-call groups, active loops, parallel batches terminated by middleware, encrypted reasoning, Foundry agents, and hosted Responses serialization. The tagged implementation reconstructs and replays a historical group when its encrypted reasoning is available. If required encrypted reasoning is missing, it raises ChatClientInvalidRequestException before sending an unsafe request. A separately configured compaction step may instead omit an entire completed group.

The practical invariant is:

valid historical group
  = reconstructable encrypted reasoning + paired call + paired result

missing required encrypted reasoning
  = reject before provider transport

configured atomic compaction
  = omit the entire completed group, never only one member

active tool loop
  = current call/result state retained so execution can continue

Build a two-turn test that crosses the same boundary as production. A single client call does not exercise the fix.

  1. Run a reasoning model that invokes one deterministic function.
  2. Persist the first response and tool result through your actual history provider.
  3. Start the next turn with service-side storage disabled or unavailable.
  4. Replay through the workflow, Foundry agent, or host used in production.
  5. Assert that no orphaned function_call, function_call_output, or mcp_call reaches the provider.
  6. Repeat with middleware terminating the first function batch before a follow-up assistant response.
  7. Repeat with parallel calls if your application allows them.

Capture redacted item types and call IDs at the serializer boundary. Do not log protected reasoning contents or tool secrets.

Keep MCP headers origin-scoped

The MCP item in 1.12.1 is a documentation and threat-model change. It does not claim every custom HTTP client leaks headers.

The risk appears when an application attaches a credential as a custom client’s default header and that client follows a redirect to another origin. The destination could receive a token intended for the original MCP server unless the application strips or re-scopes it. An origin includes the scheme, host, and port.

Microsoft’s built-in header_provider adds headers only when a request matches the configured MCP server origin. Current Microsoft Learn guidance also recommends passing per-run secrets through function_invocation_kwargs rather than baking them into a shared client:

import os

from agent_framework import MCPStreamableHTTPTool

api_key = os.environ["MCP_API_KEY"]

mcp_tool = MCPStreamableHTTPTool(
    name="inventory",
    url="https://mcp.example.com/service",
    header_provider=lambda kwargs: {
        "Authorization": f"Bearer {kwargs['mcp_api_key']}"
    },
)

# Supply at invocation time:
# function_invocation_kwargs={"mcp_api_key": api_key}

If you must provide a custom httpx.AsyncClient, make redirect policy part of its security review. Test same-origin and cross-origin redirects with a local fixture; never use a real credential in the test.

Redirect caseExpected credential behavior
Same scheme, host, and portSend only if the application’s policy permits that path
Host changesStrip the credential
HTTPS redirects to HTTPReject the downgrade; do not send the credential
Port changesTreat as a different origin and strip the credential
Redirect chain returns to original originRe-evaluate each hop; never forward a secret through an untrusted intermediate

The secure MCP tunnels guide covers the adjacent network boundary. A private tunnel does not replace redirect-safe credential handling inside the client.

Run the regression matrix

Run this matrix against the same package lock, provider surfaces, middleware, and storage mode planned for deployment.

TestFixtureEvidence to saveBlock rollout when
Dependency floorOpenAI 2.44.0 negative and 2.45.0 positive environmentsExact versions and runtime guard outputResolver can still install an older SDK in production
Cache payloadNo-network request preparationprompt_cache_options and per-part breakpoint in sanitized payloadEither field disappears
Cache accountingTwo or more live requests with an unchanged 1,024+ token prefixWrite, read, input, output, reasoning, and task outcomeWrites are invisible or later requests never read the cache
Cache invalidationChange one stable-prefix revisionNew revision is separately identified and billedOld and new policy cannot be distinguished
Gemini approval replayDeterministic read-only functionSecond-turn completion and replay metadata presenceINVALID_ARGUMENT or missing signature
Rebuilt Gemini historySupported persistence round tripContinuation after reloadSerialization drops protected metadata
Stateless reasoningCross-agent second turn with storage offSanitized item-type/call-ID trace and completed responseOrphaned call/result or HTTP 400
Middleware terminationParallel or single function batch stopped before assistant follow-upDurable termination marker behaviorHistorical group replays partially
MCP redirectLocal two-origin HTTP fixture with fake tokenHeaders observed at each originFake secret reaches redirected origin
Existing pathsText-only, non-reasoning, non-caching regression suitePrevious response shapes and resultsOpt-in change alters an unrelated request

Use deterministic tools such as lookup_order("fixture-1") and fixed local HTTP responses. A regression suite should not depend on a live search result or mutable external data.

Adoption checklist

  1. Pin agent-framework==1.12.1 and openai>=2.45.0,<3 in the lock or constraints file.
  2. Record the resolved provider-package versions instead of expecting every package to report 1.12.1.
  3. Inventory every provider, storage mode, approval middleware, MCP transport, and redirect policy in the application.
  4. Add explicit cache breakpoints only around stable prefixes of at least 1,024 tokens.
  5. Export raw cache-write tokens and normalized cache-read tokens before claiming a cost improvement.
  6. Version cached policy and reference content so invalidation is auditable.
  7. Replay one Gemini tool call through every reconstruction path used in production.
  8. Replay reasoning-paired tool history across the actual stateless or cross-agent boundary.
  9. Replace shared default authentication headers with origin-scoped header_provider where possible.
  10. Exercise a fake-secret cross-origin redirect test for every custom MCP HTTP client.
  11. Preserve redacted failure fixtures for 400 responses without storing protected reasoning or credentials.
  12. Roll out to a bounded cohort, monitor provider errors and accepted-task cost, then expand.

Limits and open checks

  • No live cost benchmark was run for this article. PR #7163 reports live cache behavior from its author, but your prefix, request rate, retention, and pricing determine the result.
  • The package metadata does not enforce the feature’s effective OpenAI SDK floor. Keep the explicit 2.45.0 constraint until the published dependency and your resolver make the runtime guard impossible to hit.
  • Normalized cache-write accounting is incomplete in this cohort. Read OpenAI’s raw input_tokens_details.cache_write_tokens or normalize it in your telemetry layer.
  • Gemini signatures are provider state. The fix covers Agent Framework’s supported reconstruction paths; custom serializers can still remove, corrupt, or expose the metadata.
  • Stateless replay has several branches. A passing hosted MCP test does not prove client functions, middleware termination, parallel calls, and cross-agent workflows all pass.
  • MCP guidance is preventive. Audit the actual custom client and redirect behavior instead of labeling every configuration vulnerable.
  • Python-only scope. The release notes do not establish matching behavior for the .NET packages in the repository.

Use 1.12.1 if these boundaries matter and you can run the matrix. Skip or postpone the rollout if cache charges are not observable, history cannot preserve opaque provider state, or custom MCP clients cannot prove origin-safe credential handling.

FAQ

Should I upgrade Microsoft Agent Framework Python to 1.12.1?

Yes if your Python agents use GPT-5.6 prompt caching, Gemini 3 tools, stateless reasoning workflows, or custom authenticated MCP HTTP clients. Pin the cohort and run provider-specific replay and accounting tests before a broad rollout.

Does Agent Framework 1.12.1 automatically reduce GPT-5.6 costs?

No. The release makes explicit cache breakpoints usable, but cache writes are billed and a hit depends on a sufficiently long stable prefix being reused. Measure cache-write tokens, cache-read tokens, total input, and completed-task cost.

Which OpenAI Python SDK version is required for prompt_cache_options?

Use openai 2.45.0 or newer within the supported major version. Agent Framework's published OpenAI connector metadata still permits older 2.x releases, but its runtime guard rejects prompt_cache_options when the installed SDK predates 2.45.0.

What does the Gemini thought_signature fix cover?

It preserves Gemini 3 function-call signatures when Agent Framework reconstructs a call during in-process approval, always-require MCP, or rebuilt-history flows. The fix prevents the next replayed turn from losing required signature metadata.

Does the MCP change prove my custom client is vulnerable?

No. It is security guidance, not a vulnerability finding for every client. The risk exists when a custom HTTP client attaches sensitive default headers and follows a cross-origin redirect without stripping or re-scoping them.

Get the latest on AI, LLMs & developer tools

New MCP servers, model updates, and guides like this one — delivered weekly.

Official sources

Release and package source

Implementation and regression coverage

Provider and MCP documentation

Related AgentPedia guides

Related Guides