How to Persist AI Agent Context Deterministically

Heather Downing

You did the responsible thing. You connected your agent to your memory store, so its context would persist: what it worked on, what it decided, and why. Your MCP server is wired up, the tools are attached, and somewhere in the system prompt, it says to remember important things (right?)

Time goes by. Eventually, something that persists doesn’t seem quite right, and you start to wonder – what did this agent do last week, and why?

You check the data source. Some of the context made it in, but not everything. There’s no specific signal to discern “nothing important happened” from “the model didn’t call the tool.” The context you meant to keep was only ever persisted when the model decided to.

That’s standard MCP wiring in action. The tools sit in the loop, and the model invokes them only when it decides they’re needed, which keeps the context window smaller.

Small is good. We like small.

That’s the right design for a general-purpose client; it’s just built for a different situation than this one, where you need persistence to be always on.

In this blog, we share why standard MCP wiring makes context persistence optional (the model calls the tool only when it decides to) and what to do instead. We walk through how to treat persistence as control flow rather than reasoning, using your own code as the MCP client, so the write always happens. Finally, we prove this with a working Strands agent that logs every step to Meko, and audit the trace from a separate process to confirm the context survived.

Who Decides When Your Agent’s Context Gets Saved?

Every MCP tutorial teaches the same pattern:

  1. Connect the server
  2. Hand tool calls to the model
  3. Let the LLM decide when to call them

For a chat client like Claude Desktop, that’s correct. The client can’t know your intent in advance, so the model has to choose what to use and when, even with a strongly worded skill file (still searching for the magic recipe there; some models are more obliging than others).

But if you’re building a custom agent, you’re not a chat client. You’re writing the control flow, and you know exactly which calls have to happen and when. Which means your code can be the MCP client. Same server and tools, called directly from code on your schedule instead of the model’s judgment.

These two paths carry completely different guarantees:

  1. Through the tool loop: the model might make the call. This is the right choice when the decision genuinely requires reasoning.
  2. Directly from your code: the call will happen. This is the right choice for anything that must happen every time, like persisting your agent’s context.

If you’ve seen 12-Factor Agents from Dex Horthy (credited with coining the term ‘Context Engineering’), this is a familiar approach:

  1. Own your control flow
  2. Put the LLM only where reasoning is actually needed.

For example, persisting context into Meko is control flow, not reasoning. It’s also a prerequisite for advanced architectures like Dex’s recent mention of team-wide “context shards”. A supervisor agent can’t extract team-wide rules from past sessions if half of those sessions were never logged. Deterministic tracing is the foundation that kind of memory relies on.

Developers often ask: “How do I programmatically access Meko?”

In Meko’s case, the connect flows are built for chat clients and coding harnesses – just add the connector, authenticate, and you’re done. When you’re writing your own agent, you don’t want a connector; you want to make the calls yourself from code, deterministically.

Meko doesn’t ship a client SDK or REST API (yet), but you don’t need one. The MCP server is the programmatic surface, and you can call its tools deterministically if you control the hooks around the model run.

Here’s what the direct path looks like. A plain function your code deterministically calls, preventing the model from skipping it:

def _log_step(client, env, convo_id, *, step, inp, out, reasoning, plan):
    """Record one agent step (input/output + why + plan) into the Meko trace."""
    client.call_tool_sync(
        tool_use_id=f"step-{step}",
        name="conversation_add_message",
        arguments={
            "scope": "write",
            "agent_id": env["agent_id"],
            "datapack_id": env["datapack_id"],
            "conversation_id": convo_id,
            "input": inp,
            "output": out,
            "reasoning": reasoning,
            "plan": plan,
        },
    )

The model never sees this function. It runs because Python executed it.

Proving the Pattern in Code: Using Meko Programmatically

To show how this deterministic routing looks in practice, check out the meko-agent-strands-sample repo. It’s a small agent built with Strands Agents (the open-source agent SDK from AWS) that triages a batch of Discord-style messages and writes a digest.

The digest is boring on purpose (and not really the focus). The point is that every step of the agent’s context lands in Meko through direct, deterministic calls:

  1. conversation_create opens a trace
  2. _log_step("observe", ...) records the raw input before any model call, so the trail shows exactly what the agent saw, independent of what it later concluded
  3. the agent does its work (the one place the model belongs)
  4. _log_step("decide", ...) records the output, reasoning, and plan
  5. memory_add writes durable facts

Notice that the agent doesn’t need Meko tools attached to do its job. Meko wraps around the agent, persisting context from the outside, rather than sitting inside the tool loop, where the model could ignore it.

Does the Context Survive After the Agent Stops?

That’s the test, isn’t it? The sample repo walks you through exactly that. The audit subcommand is a completely separate process. It has no shared state with the original run, reads the trail back from Meko with memory_search, and reconstructs what the agent did and why.

Regardless of when you run the audit, you’ll get the complete picture. There’s also digest-only, the same agent with the direct calls removed, so you can compare exactly what you’d lose.

How To Get Inference into the Meko Sample App

The sample above keeps data and inference separate. Your Meko API key authenticates the data plane: conversations, memories, and search against your datapack.

Inference (the LLM powering the agent) is bring-your-own. The default path is Vertex AI through LiteLLM with standard gcloud application-default credentials, and Anthropic or Bedrock are one environment variable away if you want to change it to a service provider you prefer.

Meko never sees your model key, and your model provider never sees your context.

Try This With Your Own Custom Agent

Nothing about the architectural pattern presented in the sample repo depends on Strands. It ports to LangGraph, Pydantic AI, or a bare while loop, because it’s just Python calling Meko’s MCP server deterministically.

The repo is written to be agent-readable: point your coding agent at it and pull the tool signatures and auth pattern from tested code instead of piecing them together from docs.

Try it: clone the repo, run the agent, then run audit from a fresh process and watch it reconstruct a run that no longer exists. Then look at your own agent and ask which of its calls should never have been the model’s decision.

Next steps:

If you get stuck on setup, just drop into the #Meko Discord channel and ask a question – our experts will be glad to help. Please also share any feedback (good or bad), and look out for more sample apps organized by use case.

Heather Downing

Related Posts

Explore Distributed SQL and YugabyteDB in Depth

Discover the future of data management.
Learn at Yugabyte University
Get Started
Browse Yugabyte Docs
Explore docs
PostgreSQL For Cloud Native World
Read for Free