> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Agent Framework

> Memory for both Microsoft Agent Framework surfaces — context and history providers for the agent SDK, and MemoryStore and AgentFileStore for the Agent Harness.

Microsoft Agent Framework (MAF) has two distinct product surfaces, and this package covers both.

The **agent SDK** is the one most people start with: `client.as_agent(...)` with `context_providers`. The **Agent Harness** (`create_harness_agent`) is a separate runtime with its own memory subsystem — a topic notebook, an extraction pass per turn, and a periodic consolidation rewrite — plugged in through two storage interfaces it accepts as constructor arguments.

<Note>
  Requires Python 3.11+ and `agent-framework>=1.0`. The harness surfaces need `agent-framework>=1.13` and are imported lazily, so an older install keeps working for the SDK surfaces and raises a clear error only if you reach for a harness class.
</Note>

## Overview

| Class                           | Surface | Purpose                                                                      |
| ------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `SynapContextProvider`          | SDK     | Injects relevant memories before each turn; records the turn after           |
| `SynapHistoryProvider`          | SDK     | Persists and reloads the verbatim conversation transcript                    |
| `SynapShortTermContextProvider` | SDK     | Compacted history of the current conversation, refreshed each turn           |
| `SynapMemoryStore`              | Harness | Backs the harness topic notebook — `MEMORY.md`, topic records, consolidation |
| `SynapAgentFileStore`           | Harness | Backs the `file_memory_*` tools and the agent's file access                  |
| `create_synap_harness_memory`   | Harness | Builds the memory provider wired correctly. Use this                         |

All of them take an already-constructed `MaximemSynapSDK` — your app owns the SDK and its credentials.

## Setup

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-microsoft-agent agent-framework
  ```

  ```bash uv theme={null}
  uv add maximem-synap-microsoft-agent agent-framework
  ```
</CodeGroup>

<Note>
  The pip package is `maximem-synap-microsoft-agent`, but the import drops the `maximem-` prefix and uses underscores: `from synap_microsoft_agent import ...`.
</Note>

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
```

```python theme={null}
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
```

See [SDK Initialization](/sdk/initialization) for the full lifecycle.

***

# The agent SDK

## Basic integration

`SynapContextProvider` handles both halves of memory on a normal MAF agent — the read before the turn and the write after it:

```python theme={null}
from synap_microsoft_agent import SynapContextProvider

agent = client.as_agent(
    name="MemoryAgent",
    instructions="You are a helpful assistant.",
    context_providers=[
        SynapContextProvider(
            sdk=sdk,
            user_id="alice",
            customer_id="acme",   # required on B2B instances
            max_results=6,
        ),
    ],
)

response = await agent.run("What were the outcomes from my last meeting?")
```

**Reads degrade gracefully** on a Synap outage — empty context is injected, the error is logged, and the agent still answers. **Writes surface failures** so silent data loss is impossible.

## Core concepts

### Context provider

`SynapContextProvider` runs on MAF's `before_run` and `after_run` hooks. Before the turn it builds a query from the incoming messages, fetches from Synap, and appends the result to the agent's instructions. After the turn it records the exchange back to Synap.

### History provider

`SynapHistoryProvider` handles the verbatim transcript, through `get_messages` and `save_messages`, which MAF calls for you.

```python theme={null}
import uuid
from synap_microsoft_agent import SynapHistoryProvider

# conversation_id must be a valid UUID — Synap validates it client-side.
hist = SynapHistoryProvider(
    sdk=sdk,
    user_id="alice",
    conversation_id=str(uuid.uuid4()),
)
```

It is orthogonal to `SynapContextProvider`: one stores semantic memory, the other the literal transcript.

### Short-term context

`SynapShortTermContextProvider` injects a compacted summary of the *current* conversation, refreshed each turn. Use it when the transcript is too long to replay but its shape still matters.

***

# The Agent Harness

The harness ships its own memory subsystem: a `MEMORY.md` index of pointer lines, one record per topic, an LLM extraction pass per turn, and a periodic consolidation rewrite. It is genuinely good, and this integration does **not** replace it.

What it replaces is the two things underneath — where memory is stored, and how it is retrieved. Topic selection in the stock harness is lexical: relevance is the size of the word overlap between your question and the topic's title and summary. A topic filed under "billing" is invisible to a question about "invoices". That is the gap.

### When this is worth it

We benchmarked it rather than asserting it, and the result is narrower than a blanket "upgrade".

We filed a topic under **billing** — customers charged monthly in arrears, failed charges retried three times — then asked *"When do we send invoices to customers, and what happens if one fails?"*. The stock file-backed store answered **"I don't know."** The topic was in its own memory directory the whole time; "billing" and "invoices" simply share no words, so it was never loaded. With Synap underneath, the same agent answered both halves correctly.

That is the case this integration is for: **reach**, not economy. On fixtures where the question already shares vocabulary with the topic, the two are equally correct — and the file store is faster.

The cost is a retrieval round trip on every turn. Assembling the memory block took roughly **2 seconds** with Synap against **5 milliseconds** from local files. Prompt size barely moves, because the harness already loads a *selection* of topics rather than everything.

So: use Synap where memory outgrows one vocabulary — long-lived agents, many sessions, memory shared across projects or across agents. On a handful of topics phrased the way you ask about them, `MemoryFileStore` is the better tool and we would rather say so.

## Basic integration

```python theme={null}
from agent_framework import create_harness_agent
from synap_microsoft_agent import create_synap_harness_memory

agent = create_harness_agent(
    client,
    history_provider=create_synap_harness_memory(
        sdk,
        user_id="alice",
        customer_id="acme",
    ),
)
```

<Warning>
  `create_harness_agent` accepts exactly **one** `history_provider`. Both `SynapHistoryProvider` and the harness memory provider are `HistoryProvider`s, so passing both silently keeps whichever came last and drops the other — no error, no warning. Pick one:

  * `history_provider=SynapHistoryProvider(...)` — Synap owns the transcript, the harness topic subsystem is off.
  * `history_provider=create_synap_harness_memory(...)` — the harness owns extraction and consolidation, Synap is the memory beneath it.
</Warning>

## Core concepts

### What Synap holds, and what stays local

This is the part worth understanding before you deploy it.

`MemoryStore` is a **record** store. The harness reads a topic record, adds a line, and writes it back — so a record has to come back exactly as it went in. Synap deliberately does not work that way: `memories.create` runs an extraction pipeline that rewrites, splits, and merges what you submit. In our testing, a topic record submitted as JSON came back as four extracted memories with no JSON envelope and the text rephrased into the third person. The substance survived; the record did not.

Reading records back from Synap would therefore feed the harness a rewritten record, which it would rewrite again next turn, and again at the next consolidation. So the split is:

| What                             | Where                | Why                                                  |
| -------------------------------- | -------------------- | ---------------------------------------------------- |
| Topic records, maintenance state | A `TopicRecordStore` | Read-modify-write needs exact fidelity               |
| Topic content                    | Synap                | Durable, semantic, shared across sessions and agents |
| Transcripts                      | Synap                | No local files                                       |

The default record store lives for the life of the process. After a restart it is cold, and a topic that is not in it reports as not-found — which the harness handles by starting a fresh record. The topic looks new; **no corrupted record ever enters the loop**. Meanwhile everything written on previous runs is still in Synap and still reaches the prompt, through the recall block described below.

Pass your own `record_store` to survive restarts:

```python theme={null}
from synap_microsoft_agent import TopicRecordStore, create_synap_harness_memory

class RedisTopicRecordStore:      # implements TopicRecordStore
    def put(self, owner, slug, payload): ...
    def get(self, owner, slug): ...
    def delete(self, owner, slug): ...
    def list(self, owner): ...
    def put_state(self, owner, state): ...
    def get_state(self, owner): ...

provider = create_synap_harness_memory(
    sdk, user_id="alice", record_store=RedisTopicRecordStore(),
)
```

### The recall block

`MEMORY.md` is assembled fresh on every turn: MAF's pointer lines, unchanged, plus a Synap recall block underneath.

```
# Memory Index

- [deployment workflow](topics/deployment-workflow.md): How this person ships software.

## Durable memory (Synap)

## User Context
### Preferences
- The user prefers to reach for PostgreSQL first
```

That block is the reason to use this integration. It carries memory the record layer never had — written on a previous run, by a previous process, or by a different agent against the same scope. It costs one retrieval per turn (about half a second, measured against production). Turn it off with `include_recall=False` if you would rather not pay that.

### Semantic transcript search

The harness exposes a `search_memory_transcripts` tool. On the file store that is a substring match over saved turn files, so a question worded differently from the transcript finds nothing. Here it becomes a Synap retrieval call, so wording does not have to match.

### File memory

`SynapAgentFileStore` backs the seven `file_memory_*` tools the harness gives the model:

```python theme={null}
from synap_microsoft_agent import SynapAgentFileStore

agent = create_harness_agent(
    client,
    file_memory_store=SynapAgentFileStore(sdk, user_id="alice"),
)
```

`file_memory_grep` searches **both** ways: a regex over files written this session, and a meaning-based lookup against Synap attributed to `MEMORY.md`. The regex half is exact and is what the model expects from a tool named grep; the semantic half finds memories no regex could match.

`file_memory_delete` is real — a file written through this store resolves to the memories it produced and deletes them.

<Note>
  **`file_memory_ls` only lists files this process wrote.** There is no list-memories-by-scope API in Synap today, so after a restart the agent can still `read` its files by name and `grep` them by meaning, but it cannot browse them. Within a session — which is what file memory is scoped to — this is invisible.
</Note>

## Complete example: both harness surfaces

```python theme={null}
from agent_framework import create_harness_agent
from maximem_synap import MaximemSynapSDK
from synap_microsoft_agent import SynapAgentFileStore, create_synap_harness_memory

sdk = MaximemSynapSDK()
await sdk.initialize()

agent = create_harness_agent(
    client,
    history_provider=create_synap_harness_memory(
        sdk,
        user_id="alice",
        customer_id="acme",
        recent_turns=4,
        selection_limit=3,
    ),
    file_memory_store=SynapAgentFileStore(sdk, user_id="alice", customer_id="acme"),
)
```

<Warning>
  Every harness API is marked experimental upstream and lives behind private module paths that Microsoft says may move. Pin a tested `agent-framework` version and re-run your tests on each minor release. This package never suppresses the `ExperimentalWarning` — if MAF wants you to know the surface is unstable, hiding that would not be doing you a favour.
</Warning>

## Advanced patterns

### Scoping

Both harness stores take `user_id` and `customer_id`; at least one is required. `customer_id` is required on B2B instances. See [Memory Scopes](/concepts/memory-scopes).

For multi-tenant hosts, `SynapMemoryStore` also takes a `scope_resolver` callable over the session, which partitions **records** per tenant. It does not re-scope the Synap calls themselves — build one store per tenant for that.

`FileMemoryProvider(scope=...)` decides which folder file memory lands in: `None` isolates per session, an explicit value groups across sessions. Line it up with the store's scope, or the tool surface and the memory will disagree about whose files they are.

### A new scope needs a corpus before recall is useful

Recall is only as good as what is in the scope, and a nearly-empty scope returns nothing at all rather than a little. In testing, a scope holding three memories returned an empty context on every setting, while the same scope at a dozen returned content consistently.

That matters because of how it looks from the outside: the recall block is absent and the integration appears broken. It isn't — there is genuinely nothing to return yet. Let the agent accumulate a corpus over several sessions before judging recall quality.

### Failure semantics

| Path                                                          | Behaviour                                                                       |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Context and recall reads                                      | Degrade to empty, logged at `ERROR`                                             |
| `get_index_text`                                              | Degrades to pointer lines with no recall block — it feeds the prompt every turn |
| `search_transcripts`, `file_memory_grep`                      | Degrade to no results                                                           |
| Writes (`write_topic`, `file_memory_write`, transcript saves) | Raise `SynapIntegrationError`                                                   |
| A topic that is not held                                      | Raises `FileNotFoundError`, which the harness handles as "new topic"            |

Read failures must not break a user-facing turn. Silent write failures would corrupt the memory pool, so they raise.

***

## Going further

* [Patterns overview](/patterns/overview): reusable memory patterns across frameworks.
* [Cookbook overview](/cookbook/overview): end-to-end worked examples.

## Next steps

<CardGroup cols={2}>
  <Card title="Semantic Kernel" icon="microsoft" href="/integrations/semantic-kernel">
    Kernel plugin for Microsoft Semantic Kernel.
  </Card>

  <Card title="deepagents" icon="https://github.com/langchain-ai.png" href="/integrations/deepagents">
    The same harness pattern for LangChain's deepagents.
  </Card>

  <Card title="Context Fetch" icon="search" href="/sdk/context-fetch">
    The retrieval API behind every surface here: modes, scopes, and response shapes.
  </Card>

  <Card title="Memory Scopes" icon="layer-group" href="/concepts/memory-scopes">
    How `user_id`, `customer_id`, and `conversation_id` interact across reads.
  </Card>
</CardGroup>
