# 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.

- **Published**: 2026-07-23
- **Category**: AI Infrastructure
- **URL**: https://agentpedia.codes/blog/microsoft-agent-framework-1-12-1-caching-tool-replay-guide

---

> **Important callout**

**Bottom line:** Microsoft Agent Framework Python 1.12.1 is a focused reliability upgrade for agents that cache long GPT-5.6 prompts, replay Gemini 3 tool calls, move reasoning history across stateless boundaries, or authenticate local MCP HTTP tools. Upgrade as a pinned cohort, require `openai>=2.45.0` for explicit cache options, measure both cache writes and reads, and run the replay matrix in this guide before production rollout.

Microsoft published the [Python 1.12.1 release](https://github.com/microsoft/agent-framework/releases/tag/python-1.12.1) 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:

| Workload | Why 1.12.1 matters | Acceptance gate |
| --- | --- | --- |
| Repeated long prompts on GPT-5.6 | Explicit breakpoints and request-wide cache policy now reach OpenAI payloads | Second identical prefix reports cache reads; write and read token counts are visible for pricing calculations |
| Gemini 3 function tools | Reconstructed function calls retain required `thought_signature` metadata | Approval and persisted-history loops complete past the second model turn |
| OpenAI or Foundry stateless reasoning | Reasoning-paired function and MCP groups no longer become orphaned during replay | Cross-agent and middleware-terminated histories avoid replay HTTP 400s |
| Authenticated local MCP over HTTP | The docs now distinguish origin-scoped `header_provider` from headers attached to a custom client | A 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](/blog/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.

| Area | Tagged change | Operational consequence | Source |
| --- | --- | --- | --- |
| OpenAI connector | Adds `prompt_cache_options` and per-content `prompt_cache_breakpoint` forwarding for GPT-5.6 | Caching is opt-in and measurable instead of silently dropping the controls | [PR #7163](https://github.com/microsoft/agent-framework/pull/7163) |
| Gemini connector | Preserves `thought_signature` when a function call is reconstructed | Approval, MCP, and rebuilt-history paths can continue a Gemini 3 tool loop | [PR #7095](https://github.com/microsoft/agent-framework/pull/7095) |
| Core/OpenAI/Foundry | Repairs stateless replay of reasoning-paired client function calls and hosted MCP calls | Reconstructable encrypted reasoning groups replay; missing protected state is rejected before transport | [PR #7233](https://github.com/microsoft/agent-framework/pull/7233) |
| Core MCP | Documents custom-client header leakage risk on cross-origin redirects | Applications that supply their own HTTP client must enforce origin scoping | [PR #7245](https://github.com/microsoft/agent-framework/pull/7245) |
| AG-UI | Promotes the Python package from RC to stable | Lifecycle status changes; it is not evidence that every host or adapter is GA | [Python 1.12.1 release](https://github.com/microsoft/agent-framework/releases/tag/python-1.12.1) |

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.

```bash
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:

| Distribution | Version in the Python 1.12.1 cohort | Published dependency relevant here |
| --- | ---: | --- |
| `agent-framework` | 1.12.1 | `agent-framework-core[all]==1.12.1` |
| `agent-framework-core` | 1.12.1 | Core runtime |
| `agent-framework-openai` | 1.11.0 | `agent-framework-core>=1.11.0,<2`; `openai>=2.25.0,<3` |
| OpenAI Python SDK | Pin at least 2.45.0 for this feature | `PromptCacheOptions` 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:

```text
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.

```python
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:

```python
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:

```text
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 path | Test action | Pass condition |
| --- | --- | --- |
| Automatic function loop | Let Gemini call a harmless deterministic tool, return its result, and continue | Final response completes without `INVALID_ARGUMENT` |
| Tool approval middleware | Require approval, approve the call, then continue | Reconstructed function call still carries the signature |
| MCP `always_require` | Approve one read-only MCP call and replay it | The continuation turn succeeds |
| Persisted or rebuilt history | Serialize approved framework history, load it into a new in-process client, and continue | Signature survives the supported history representation |
| Corrupted history | Alter or remove the stored signature in a disposable fixture | The 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](/blog/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:

```text
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:

```python
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 case | Expected credential behavior |
| --- | --- |
| Same scheme, host, and port | Send only if the application's policy permits that path |
| Host changes | Strip the credential |
| HTTPS redirects to HTTP | Reject the downgrade; do not send the credential |
| Port changes | Treat as a different origin and strip the credential |
| Redirect chain returns to original origin | Re-evaluate each hop; never forward a secret through an untrusted intermediate |

The [secure MCP tunnels guide](/blog/openai-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.

| Test | Fixture | Evidence to save | Block rollout when |
| --- | --- | --- | --- |
| Dependency floor | OpenAI 2.44.0 negative and 2.45.0 positive environments | Exact versions and runtime guard output | Resolver can still install an older SDK in production |
| Cache payload | No-network request preparation | `prompt_cache_options` and per-part breakpoint in sanitized payload | Either field disappears |
| Cache accounting | Two or more live requests with an unchanged 1,024+ token prefix | Write, read, input, output, reasoning, and task outcome | Writes are invisible or later requests never read the cache |
| Cache invalidation | Change one stable-prefix revision | New revision is separately identified and billed | Old and new policy cannot be distinguished |
| Gemini approval replay | Deterministic read-only function | Second-turn completion and replay metadata presence | `INVALID_ARGUMENT` or missing signature |
| Rebuilt Gemini history | Supported persistence round trip | Continuation after reload | Serialization drops protected metadata |
| Stateless reasoning | Cross-agent second turn with storage off | Sanitized item-type/call-ID trace and completed response | Orphaned call/result or HTTP 400 |
| Middleware termination | Parallel or single function batch stopped before assistant follow-up | Durable termination marker behavior | Historical group replays partially |
| MCP redirect | Local two-origin HTTP fixture with fake token | Headers observed at each origin | Fake secret reaches redirected origin |
| Existing paths | Text-only, non-reasoning, non-caching regression suite | Previous response shapes and results | Opt-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.


---

[Join the Agentpedia newsletter](https://agentpedia.codes/blog)

## Official sources

### Release and package source

- [Microsoft Agent Framework Python 1.12.1 release](https://github.com/microsoft/agent-framework/releases/tag/python-1.12.1)
- [Python 1.12.0 to 1.12.1 source comparison](https://github.com/microsoft/agent-framework/compare/python-1.12.0...python-1.12.1)
- [Tagged OpenAI prompt-caching sample](https://github.com/microsoft/agent-framework/blob/python-1.12.1/python/samples/02-agents/providers/openai/client_prompt_caching.py)
- [Tagged OpenAI connector package metadata](https://github.com/microsoft/agent-framework/blob/python-1.12.1/python/packages/openai/pyproject.toml)

### Implementation and regression coverage

- [GPT-5.6 explicit prompt cache breakpoints, PR #7163](https://github.com/microsoft/agent-framework/pull/7163)
- [Gemini 3 thought-signature replay, PR #7095](https://github.com/microsoft/agent-framework/pull/7095)
- [Stateless reasoning-paired tool replay, PR #7233](https://github.com/microsoft/agent-framework/pull/7233)
- [MCP custom HTTP-client security guidance, PR #7245](https://github.com/microsoft/agent-framework/pull/7245)

### Provider and MCP documentation

- [Microsoft Learn: using local MCP tools](https://learn.microsoft.com/en-us/agent-framework/agents/tools/local-mcp-tools)
- [Google: Gemini thought signatures](https://ai.google.dev/gemini-api/docs/thought-signatures)
- [OpenAI prompt caching and cache breakpoints](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-breakpoints)

### Related AgentPedia guides

- [OpenAI Agents Python SDK guide](/blog/openai-agents-python-guide)
- [Gemini 3.6 Flash developer guide](/blog/gemini-3-6-flash-developer-guide)
- [Secure MCP tunnels guide](/blog/openai-secure-mcp-tunnels-guide)

[Browse related Agentpedia articles](https://agentpedia.codes/blog)


---

- [All articles](https://agentpedia.codes/blog)