The simplest path: Single Claude Agent + LangMem MCP Tools
+-------------------------------------------------------------------+
| THE SIMPLE ARCHITECTURE |
+-------------------------------------------------------------------+
| |
| User (Telegram, SMS, Web, etc.) |
| | |
| v |
| +-------------+ |
| | Adapter | Extracts: user_id, session_id, message |
| +------+------+ |
| | |
| v |
| +------------------------------------------+ |
| | Claude Agent SDK (Core) | |
| | | |
| | Claude + Tools (including LangMem) | |
| | | |
| +------------------------------------------+ |
| | |
| v |
| +-------------+ |
| | Supabase | Memories stored as embeddings (pgvector) |
| +-------------+ |
| |
+-------------------------------------------------------------------+
That's it. One Claude, one set of tools, one database.
LangMem provides 4 MCP tools that Claude can call:
+-------------------------------------------------------------------+
| LANGMEM TOOL SET |
+-------------------------------------------------------------------+
| |
| mem_store |
| +-----------------------------------------------------------+ |
| | Store a memory for later retrieval | |
| | | |
| | Claude calls this when user says something worth | |
| | remembering: preferences, facts, decisions, etc. | |
| | | |
| | "Remember I prefer morning meetings" | |
| | --> mem_store("User prefers morning meetings") | |
| +-----------------------------------------------------------+ |
| |
| mem_search |
| +-----------------------------------------------------------+ |
| | Search memories by semantic similarity | |
| | | |
| | Claude calls this when context would be helpful. | |
| | Returns relevant memories ranked by similarity. | |
| | | |
| | "What time works for our meeting?" | |
| | --> mem_search("meeting time preferences") | |
| | --> Returns: "User prefers morning meetings" | |
| +-----------------------------------------------------------+ |
| |
| mem_extract |
| +-----------------------------------------------------------+ |
| | Extract memories from conversation history | |
| | | |
| | Called at end of conversation (or periodically) to | |
| | identify and persist important information. | |
| | | |
| | [conversation ends] | |
| | --> mem_extract(last_n_messages) | |
| | --> Extracts: preferences, facts, decisions | |
| +-----------------------------------------------------------+ |
| |
| mem_profile |
| +-----------------------------------------------------------+ |
| | Get/update user profile (long-term preferences) | |
| | | |
| | Structured data about the user that persists across | |
| | all sessions. Think: communication style, expertise, | |
| | timezone, language preferences. | |
| | | |
| | "I'm a data scientist who prefers technical explanations" | |
| | --> mem_profile(update: {expertise: "data science", | |
| | style: "technical"}) | |
| +-----------------------------------------------------------+ |
| |
+-------------------------------------------------------------------+
Where LangMem fits in the conversation lifecycle:
+-------------------------------------------------------------------+
| CONVERSATION LIFECYCLE |
+-------------------------------------------------------------------+
| |
| 1. CONVERSATION START |
| +----------------------------------------------------------+ |
| | | |
| | Adapter receives message | |
| | | | |
| | v | |
| | Load user profile (mem_profile) | |
| | | | |
| | v | |
| | Search relevant memories (mem_search with context) | |
| | | | |
| | v | |
| | Inject into system prompt: | |
| | "User profile: {profile}" | |
| | "Relevant memories: {memories}" | |
| | | |
| +----------------------------------------------------------+ |
| |
| 2. DURING CONVERSATION |
| +----------------------------------------------------------+ |
| | | |
| | Claude processes message | |
| | | | |
| | v | |
| | Claude DECIDES when to use memory tools: | |
| | | |
| | - "Let me remember that..." --> mem_store | |
| | - "What do I know about..." --> mem_search | |
| | - User states preference --> mem_store | |
| | - User asks about history --> mem_search | |
| | | |
| | Claude has AGENCY over memory management | |
| | | |
| +----------------------------------------------------------+ |
| |
| 3. CONVERSATION END |
| +----------------------------------------------------------+ |
| | | |
| | Conversation concludes (timeout, user ends, etc.) | |
| | | | |
| | v | |
| | Extract memories from conversation (mem_extract) | |
| | | | |
| | v | |
| | Update user profile if needed (mem_profile) | |
| | | | |
| | v | |
| | Persist to Supabase | |
| | | |
| +----------------------------------------------------------+ |
| |
+-------------------------------------------------------------------+
All memories live in Supabase, using pgvector for semantic search:
+-------------------------------------------------------------------+
| STORAGE ARCHITECTURE |
+-------------------------------------------------------------------+
| |
| Supabase (Postgres + pgvector) |
| +-----------------------------------------------------------+ |
| | | |
| | memories table | |
| | +------------------------------------------------------+ | |
| | | id | user_id | content | embedding | created_at | ... | | |
| | +------------------------------------------------------+ | |
| | | | | | [1536 dim]| | | | |
| | +------------------------------------------------------+ | |
| | | |
| | profiles table | |
| | +------------------------------------------------------+ | |
| | | user_id | preferences | expertise | style | ... | | |
| | +------------------------------------------------------+ | |
| | | |
| +-----------------------------------------------------------+ |
| |
| NAMESPACE STRATEGY |
| +-----------------------------------------------------------+ |
| | | |
| | All memories scoped by user_id: | |
| | | |
| | user_123/ | |
| | +-- memories/ # Semantic memories | |
| | +-- profile # User preferences/profile | |
| | +-- sessions/ # Optional: session-specific | |
| | +-- sess_abc/ | |
| | | |
| | Simple. Flat. Queryable. | |
| | | |
| +-----------------------------------------------------------+ |
| |
+-------------------------------------------------------------------+
What every adapter must provide:
+-------------------------------------------------------------------+
| ADAPTER CONTRACT |
+-------------------------------------------------------------------+
| |
| Every platform adapter MUST pass these to the core: |
| |
| REQUIRED |
| +-----------------------------------------------------------+ |
| | | |
| | user_id: string | |
| | Unique identifier for the user across sessions | |
| | (phone number, telegram ID, email, etc.) | |
| | | |
| | message: string | |
| | The user's message content | |
| | | |
| +-----------------------------------------------------------+ |
| |
| OPTIONAL |
| +-----------------------------------------------------------+ |
| | | |
| | session_id: string | |
| | Groups messages into conversations | |
| | (generated if not provided) | |
| | | |
| | platform: string | |
| | "telegram" | "sms" | "web" | "slack" | etc. | |
| | (for platform-specific behavior) | |
| | | |
| | metadata: object | |
| | Platform-specific context (timezone, locale, etc.) | |
| | | |
| +-----------------------------------------------------------+ |
| |
| EXAMPLE FLOW |
| +-----------------------------------------------------------+ |
| | | |
| | Telegram Message Arrives | |
| | | | |
| | v | |
| | Telegram Adapter extracts: | |
| | user_id: "tg_12345" | |
| | message: "What's the status of my project?" | |
| | session_id: "tg_12345_1705432800" | |
| | platform: "telegram" | |
| | | | |
| | v | |
| | Core Agent receives standardized input | |
| | | |
| +-----------------------------------------------------------+ |
| |
+-------------------------------------------------------------------+
Putting it all together:
+-------------------------------------------------------------------+
| END-TO-END FLOW |
+-------------------------------------------------------------------+
| |
| [User on Telegram] [User on SMS] [User on Web] |
| | | | |
| v v v |
| +-----------+ +-----------+ +-----------+ |
| | Telegram | | SMS | | Web | |
| | Adapter | | Adapter | | Adapter | |
| +-----------+ +-----------+ +-----------+ |
| | | | |
| +------------------+---------------+ |
| | |
| v |
| +----------------------------------------------------+ |
| | STANDARDIZED INPUT | |
| | { user_id, message, session_id, platform, ... } | |
| +----------------------------------------------------+ |
| | |
| v |
| +----------------------------------------------------+ |
| | | |
| | CLAUDE AGENT SDK CORE | |
| | | |
| | +------------+ +------------+ +------------+ | |
| | | Claude | | LangMem | | Other | | |
| | | LLM | | Tools | | Tools | | |
| | +------------+ +------------+ +------------+ | |
| | | | | | |
| | +---------------+---------------+ | |
| | | | |
| +-------------------------+-------------------------+ |
| | |
| v |
| +----------------------------------------------------+ |
| | | |
| | SUPABASE | |
| | | |
| | +------------+ +------------+ +------------+ | |
| | | Memories | | Profiles | | Sessions | | |
| | | (pgvector) | | | | | | |
| | +------------+ +------------+ +------------+ | |
| | | |
| +----------------------------------------------------+ |
| |
+-------------------------------------------------------------------+
- Claude has agency - Claude decides when to store/search memories, not the system
- Simple namespacing - Everything scoped by user_id, no complex hierarchies
- Adapters are thin - Just extract user_id/message, pass to core
- One source of truth - All memories in Supabase, all platforms share them
- Semantic search - pgvector enables "find memories about X" not just exact match
Intentionally excluded for simplicity:
- Voice/streaming sessions (different architecture needed)
- Multi-agent patterns (Fast Agent, Supervisor, etc.)
- Decision Packs (shared memory across users)
- Complex memory promotion (session to user level)
- Real-time sync between agents
When you need those, see the full 11_LANGMEM_INTEGRATION.md.
This is the 80/20 solution: 80% of the value with 20% of the complexity.