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

# Google ADK

> FunctionTool factory that adds memory search and storage to Google Agent Development Kit agents.

Add persistent, per-user memory to a Google ADK agent in one factory call. Synap exposes itself as two `FunctionTool` instances, and the agent decides when to recall or remember.

<Note>
  Requires Python 3.11+.
</Note>

## Overview

This guide shows how to add Synap to a Google ADK application to build agents that:

* Recall user-specific facts, preferences, and past conversations
* Persist new information surfaced during a conversation
* Stay multi-user-safe by binding scope at tool construction

The Synap Google ADK integration ships a single factory: it returns the two `FunctionTool` instances ready to drop onto an `Agent`.

| Export               | Returns                         | Purpose                                                        |
| -------------------- | ------------------------------- | -------------------------------------------------------------- |
| `create_synap_tools` | `[search_memory, store_memory]` | Two ADK `FunctionTool` instances for memory recall and storage |

## Setup

Install the package alongside the Google ADK:

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-google-adk google-adk
  ```

  ```bash uv theme={null}
  uv add maximem-synap-google-adk google-adk
  # pip-compatible (existing venv): uv pip install maximem-synap-google-adk google-adk
  ```
</CodeGroup>

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

Configure your API key. Generate one from the [Synap Dashboard](https://synap.maximem.ai).

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
GOOGLE_API_KEY=your-google-api-key
```

Initialize the SDK once at application startup:

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

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

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

## Basic integration

The smallest useful integration calls `create_synap_tools` and passes the result straight into an `Agent`. The factory returns a list, so it fits the `tools=` parameter without unpacking:

```python theme={null}
# pip install maximem-synap-google-adk google-adk
from maximem_synap import MaximemSynapSDK
from google.adk.agents import Agent
from synap_google_adk import create_synap_tools

sdk = MaximemSynapSDK()
await sdk.initialize()

tools = create_synap_tools(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",   # optional, required for B2B instances
)

agent = Agent(
    name="MemoryAgent",
    model="gemini-2.0-flash",
    instruction=(
        "Use the synap_search tool to recall context about the user. "
        "Use synap_store to remember new facts."
    ),
    tools=tools,
)
```

The factory binds `user_id` and `customer_id` into the closures it returns. The model only ever sees `query`, `max_results`, and `content`, never the user identity. This prevents prompt-injection attempts from spoofing scope.

***

## Core concepts

### create\_synap\_tools

`create_synap_tools` returns a two-element list: `[search_memory, store_memory]`. Both are ADK `FunctionTool` instances and can be passed directly to `Agent(tools=...)`:

```python theme={null}
from synap_google_adk import create_synap_tools

tools = create_synap_tools(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",
)
# tools[0] = search_memory
# tools[1] = store_memory
```

### search\_memory

Tool signature exposed to the model:

```text theme={null}
search_memory(query: str, max_results: int = 5) -> list[dict]
```

Returns a list of memory objects with the shape `{"content": "...", "type": "...", "confidence": float}`. The agent sees this as JSON and can reason over it directly.

**Search failures degrade gracefully**: the tool returns `[]` and logs an error so the agent continues without recall rather than aborting.

### store\_memory

Tool signature exposed to the model:

```text theme={null}
store_memory(content: str, memory_type: str = "fact") -> dict
```

Returns `{"status": "stored", "id": "..."}` on success. **Store failures surface explicitly**: the tool raises `SynapIntegrationError` so the agent (and you) know if persistence failed.

***

## Complete example: per-user memory agent

In a multi-user service, build a fresh tool set per request. Each agent run gets a scope-bound tool list, so two concurrent users cannot see each other's memories:

```python theme={null}
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
from synap_google_adk import create_synap_tools

APP_NAME = "memory-agent"
session_service = InMemorySessionService()


def build_agent_for_user(sdk, user_id: str, customer_id: str | None = None) -> Agent:
    tools = create_synap_tools(sdk=sdk, user_id=user_id, customer_id=customer_id)

    return Agent(
        name="MemoryAgent",
        model="gemini-2.0-flash",
        instruction=(
            "You are a personal assistant with long-term memory.\n"
            "1. Always call search_memory FIRST for any question about the user.\n"
            "2. When the user shares a fact, preference, or decision, "
            "call store_memory before responding.\n"
            "3. If search_memory returns nothing, say so honestly."
        ),
        tools=tools,
    )


async def handle_request(sdk, user_id: str, message: str) -> str:
    agent = build_agent_for_user(sdk, user_id=user_id, customer_id="acme")

    # ADK's Runner needs an app name and a session service; run_async takes
    # keyword args and a structured Content message, and yields events.
    # It is not `run_async(message)`. See ADK's runtime docs for the full loop.
    runner = Runner(app_name=APP_NAME, agent=agent, session_service=session_service)
    session = await session_service.create_session(app_name=APP_NAME, user_id=user_id)
    content = types.Content(role="user", parts=[types.Part.from_text(text=message)])

    async for event in runner.run_async(
        user_id=user_id, session_id=session.id, new_message=content
    ):
        if event.is_final_response():
            return event.content.parts[0].text
    return ""


# Usage
await handle_request(sdk, user_id="alice", message="I just upgraded to the Pro plan.")
# Later:
reply = await handle_request(sdk, user_id="alice", message="What plan am I on?")
# → "You're on the Pro plan."
```

Three things to notice in this pattern:

1. **Per-request agent construction is the safety pattern.** Each request gets its own scope-bound tool list, so no shared state can leak between users.
2. **The instruction is the policy.** Telling the model to always call `search_memory` first is what produces recall behavior; the tools are just plumbing.
3. **Failure modes split.** Search is best-effort (empty list on failure); store is strict (raises on failure) so silent data loss is impossible.

***

## Advanced patterns

### Multi-tenant scoping

`create_synap_tools` accepts the standard scoping triple: `user_id` (required), optional `customer_id`, optional `conversation_id`. `customer_id` is required on B2B Synap instances and ignored on single-tenant ones. See [Memory Scopes](/concepts/memory-scopes).

```python theme={null}
# User-scoped only
tools = create_synap_tools(sdk=sdk, user_id="alice")

# Organization-scoped (user sees org-shared memories too)
tools = create_synap_tools(sdk=sdk, user_id="alice", customer_id="acme-corp")
```

### Multi-agent setups

Each `Agent` can carry its own tool list. Give a "memory-keeper" agent both tools and a "consumer" agent only `search_memory` to keep the write surface small:

```python theme={null}
[search, _store] = create_synap_tools(sdk=sdk, user_id="alice", customer_id="acme")
reader_only = Agent(name="Reader", model="gemini-2.0-flash", tools=[search])
```

### Failure semantics

The integration follows the Synap-wide contract:

* **`search_memory` degrades gracefully**: returns `[]` and logs an error if Synap is unreachable.
* **`store_memory` surfaces failures**: raises `SynapIntegrationError` so the agent and caller know persistence failed.

This is by design: read failures shouldn't break a user-facing answer, but silent write failures would let the memory drift away from reality.

***

## 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="Haystack" icon="filter" href="/integrations/haystack">
    Pipeline components for Haystack.
  </Card>

  <Card title="Agno" icon="bolt" href="/integrations/agno">
    `InMemoryDb` replacement for Agno agents.
  </Card>

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

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