Understanding Storage, Memory, and State in Amazon Bedrock AgentCore
How AI Agents Persist Files, Remember Conversations, and Learn from Experience
TL;DR: AgentCore provides four complementary persistence layers—Persistent Session Storage for files and project state, Short-Term Memory for conversation context, Long-Term Memory with episodic learning for accumulated knowledge, and MCP State for tool-level interactivity. Together, these enable agents to maintain sophisticated state across sessions, tools, and interactive workflows.
The Four Pillars of Agent Persistence

When building production AI agents, one of the most critical challenges is state management. How do agents remember what they've done? How do they learn from past interactions? How do they maintain context across long-running tasks? How do they interact with users mid-execution?
Amazon Bedrock AgentCore addresses this through four distinct but complementary mechanisms:
- Persistent Session Storage - The filesystem layer
- Short-Term Memory - Conversation context
- Long-Term Memory - Extracted knowledge with episodic learning
- MCP State - Tool-level interactivity (elicitation, sampling, progress)
Let's break down each layer and understand how they work together.
Layer 1: Persistent Session Storage (The Filesystem)
What It Is
Persistent Session Storage provides agents with a durable filesystem at
/mnt/workspace (or any configured mount path) that survives session stop/resume cycles. Think of it as giving your agent a "hard drive" that persists between executions.What It Stores
text
/mnt/workspace/├── main.py # Source code├── auth.py├── requirements.txt # Dependencies├── .venv/ # Virtual environments│ └── node_modules/├── .git/ # Version control history└── .sessions/ # Conversation state (if using FileSessionManager)└── session-123.json
File types stored:
- ✅ Application code, artifacts, build outputs
- ✅ Configuration files, environment setups
- ✅ Installed packages (node_modules, .venv, etc.)
- ✅ Git repositories with full history
- ✅ Intermediate results, logs, state files
Characteristics
- 14-day retention: Files automatically persist for 14 days of inactivity
- Survives stop/resume: Agent can be stopped, compute terminated, then resumed with complete filesystem intact
- Session-isolated: Each
runtimeSessionIdgets isolated storage; sessions can't access each other's files
- S3-backed: Automatically replicated to AgentCore-managed S3 buckets
- POSIX-compatible: Works with standard tools (
git,npm,pip,ls,cat, etc.)
Use Case Example: Coding Agent
Python
from strands import Agentfrom strands.session import FileSessionManagerfrom bedrock_agentcore.runtime import BedrockAgentCoreAppapp = BedrockAgentCoreApp()WORKSPACE = "/mnt/workspace"@app.entrypointdef handle_request(payload):# Files written to /mnt/workspace persist across sessionssession_manager = FileSessionManager(session_id=payload.get("session_id"),storage_dir=f"{WORKSPACE}/.sessions")agent = Agent(model=model,tools=[file_read, file_write, shell],session_manager=session_manager,system_prompt="Project files are in /mnt/workspace.")return agent(payload.get("prompt"))
Invocation pattern:
Python
# First invocation - create projectinvoke("Set up a FastAPI app in /mnt/workspace with authentication")# Stop session (compute terminates, files flush to S3)client.stop_runtime_session(runtimeSessionId="session-123")# Hours/days later - resume with same session-idinvoke("Add rate limiting to the API you created")# Agent resumes with complete project: main.py, auth.py, requirements.txt, .venv/
Why this matters: The agent doesn't need to recreate the project structure, reinstall dependencies, or lose context about what was built. It's like picking up exactly where you left off.
Layer 2: Short-Term Memory (Conversation Context)
What It Is
Short-Term Memory stores raw conversation events—every user message, assistant response, and tool invocation within the current session. This is the agent's "working memory" for the active conversation.
What It Stores
JSON
Raw Conversation Events:{"events": [{"role": "user", "content": "Create FastAPI app..."},{"role": "assistant", "content": "I've created main.py..."},{"role": "user", "content": "Sprint planning 60min timebox"},{"role": "assistant", "content": "Meeting completed on time"}]}
Characteristics:
- Ephemeral by default: Conversation state is session-scoped
- Full fidelity: Every message, tool call, and response preserved
- Context for follow-ups: Enables "Add authentication" after "Create API" without repeating context
- API access:
ListEvents()retrieves conversation history
Persistence Options
Option A: In-Memory (Default)
Python
from strands.session import InMemorySessionManagersession_manager = InMemorySessionManager(session_id="conv-001")# Lost when session stops
Option B: Filesystem (Persistent)
Python
from strands.session import FileSessionManagersession_manager = FileSessionManager(session_id="conv-001",storage_dir="/mnt/workspace/.sessions")# Conversation history persists alongside project files
Option C: DynamoDB (Shared)
Python
from strands.session import DynamoDBSessionManagersession_manager = DynamoDBSessionManager(session_id="conv-001",table_name="agent-conversations")# Conversation accessible across multiple agent instances
The AgentCore Memory API
AgentCore provides a Memory API for storing and retrieving conversation context:
Python
# Store conversation contextmemory_client.create_session(sessionId="conv-001",context={"user_id": "u-123", "project": "FastAPI-auth"})# Retrieve conversation historyevents = memory_client.list_events(sessionId="conv-001",maxResults=50)
This powers the Short-Term Memory box in the diagram, enabling agents to maintain conversation continuity.
Layer 3: Long-Term Memory (Extracted Knowledge)
What It Is
Long-Term Memory is where agents extract and persist insights from conversations using multiple strategies. Unlike Short-Term Memory (which stores raw events), Long-Term Memory stores structured, searchable knowledge.
The Four Memory Strategies
1. Semantic Memory - Facts & Knowledge
Extracts discrete facts and knowledge from conversations:
text
Examples:- "User prefers Python"- "Team has 5 developers"- "FastAPI project: /mnt/workspace/api-project"
Storage: Vector embeddings for semantic search
Retrieval:
retrieve_memory_records(query="sprint planning patterns") 2. Summary Memory - Conversation Condensation
Distills multi-turn conversations into concise summaries:
text
Example:Original (300 messages): "Create API... Add auth... Fix bug... Deploy..."Summary: "36-min pricing discussion. Decided on Enterprise plan."
Purpose: Compress conversation history while retaining key decisions
3. User Preference Memory - Settings & Personalization
Captures user preferences and settings:
text
Examples:- "User wants dark mode"- "Email digest at 9 AM"- "Prefers concise responses"
Purpose: Personalize agent behavior across sessions
4. Episodic Memory - Experience Sequences ⭐
The most powerful strategy—captures sequences of experiences across multiple episodes:
text
Episodes:- "Sprint planning #1 - 90 min"- "Sprint planning #2 - 60 min ✓"- "Sprint planning #3 - 60 min ✓"Reflections (Cross-Episode):- "60-min timebox optimal"- "Pre-estimate stories first"- "Success rate: 90%"- "Learns what works"
What makes it special:
- Cross-session learning: Generates reflections across multiple conversations
- Pattern detection: Identifies what works ("60-min sprints work best")
- Temporal awareness: Understands sequences ("learned from 5 episodes")
- Compound knowledge: Combines insights from multiple experiences
How Long-Term Memory Works
Python
# Background extraction job runs after conversationextract_memory(session_id="conv-001",strategies=["semantic", "summary", "episodic", "user_preference"])# Later, agent retrieves relevant memoriesmemories = retrieve_memory_records(query="sprint planning patterns")# Returns:# - Semantic: "User prefers 60-min timeboxes"# - Episodic: "60-min sprints work best (learned from 5 episodes)"# - Custom: "Pre-estimate stories before planning"
Memory Retrieval API
Python
retrieve_memory_records(query="sprint planning patterns")# Returns:# - Semantic facts across strategies# - Episodic episodes with reflections# - Includes cross-episode reflections# - Semantic search finds relevant patterns
The diagram shows Long-Term Memory as a collection of Semantic, Summary, User Preference, Episodic, and Custom extraction strategies, all feeding into a unified retrieval API.
Layer 4: MCP State (Tool-Level Interactivity)
What It Is
MCP (Model Context Protocol) State is a framework-managed execution context that enables tools to perform interactive, multi-turn operations. Unlike the previous three layers (which focus on data persistence), MCP State enables behavioral persistence—the ability for tools to pause mid-execution, collect user input, request LLM generations, and report progress.
The Critical Distinction
AgentCore State | MCP State |
|---|---|
Files (Persistent Storage) | Tool execution context |
Conversations (Short-Term Memory) | Interactive workflows |
Knowledge (Long-Term Memory) | Suspended operations |
What you persist | How you interact |
The Three MCP Stateful Features
1. Elicitation 🔄 - Interactive User Input
Tools can pause execution to collect structured user input via forms.
Real-World Example: Exploring Workspace Projects
Python
from mcp import Contextfrom pydantic import BaseModel, Fieldclass ProjectQuestionsSchema(BaseModel):"""Schema for collecting project clarification questions"""purpose: str = Field(description="What is the main purpose of this project?")tech_stack: str = Field(description="What technologies/frameworks does it use?")status: str = Field(description="What is the project status?",examples=["Active", "Archived", "Prototype", "Production"])priority: str = Field(description="What is the priority level?",examples=["High", "Medium", "Low"])@mcp.tool()async def ask_project_questions(ctx: Context, project_name: str) -> dict:"""Ask clarifying questions about a project using elicitation."""# Check if project exists in persistent storageproject_path = f"/mnt/workspace/{project_name}"if not os.path.exists(project_path):return {"status": "error", "message": f"Project {project_name} not found"}# ⏸️ TOOL EXECUTION PAUSES HEREresult = await ctx.elicit(message=f"Please provide information about project: {project_name}",schema=ProjectQuestionsSchema)# ▶️ TOOL EXECUTION RESUMES after user respondsif result.action == "accept":project_info = result.data # Validated project data# Store project context in persistent storagecontext_file = f"{project_path}/.project-context.json"with open(context_file, "w") as f:json.dump({"project_name": project_name,"purpose": project_info.purpose,"tech_stack": project_info.tech_stack,"status": project_info.status,"priority": project_info.priority,"questions_asked": True,"timestamp": datetime.now().isoformat()}, f)return {"status": "success","project": project_name,"details": project_info,"context_saved": context_file}return {"status": "cancelled"}
Execution Flow:
Plain text
1. User: "Ask me questions about project-a"2. Tool starts → Checks project exists → Reaches ctx.elicit()3. 🔒 Tool execution PAUSES (local variables preserved)4. User sees form → Answers questions about purpose, tech, status, priority5. 🔓 Tool execution RESUMES with validated answers6. Tool saves context to /mnt/workspace/project-a/.project-context.json7. Returns success with project details
Why MCP State Required:
- ✅ Execution Suspension: The
await ctx.elicit()preserves entire tool state
- ✅ Context Preservation: Local variables (
session) remain in memory
- ✅ SSE Stream: HTTP connection stays open for response
- ❌ Impossible in stateless mode: Tool would be destroyed when request completes
2. Sampling 🤖 - LLM-Powered Content
Tools can request LLM-generated content from the client, enabling server-side agents to leverage client-side AI.
Real-World Example: AI-Powered Project Insights
Python
@mcp.tool()async def generate_project_insights(ctx: Context, project_name: str) -> dict:"""Generate AI-powered project insights using client's LLM (reverse data flow)."""# Read project files from AgentCore persistent storageproject_path = f"/mnt/workspace/{project_name}"# Scan project structurefiles = []for root, dirs, filenames in os.walk(project_path):files.extend([os.path.join(root, f) for f in filenames])# Read key files (README, main code)project_description = f"Project: {project_name}\n"project_description += f"Total files: {len(files)}\n\n"# Read README if existsreadme_path = os.path.join(project_path, "README.md")if os.path.exists(readme_path):with open(readme_path) as f:project_description += f"README:\n{f.read()[:500]}\n\n"# Sample a few Python filespython_files = [f for f in files if f.endswith('.py')][:3]for py_file in python_files:with open(py_file) as f:project_description += f"File: {os.path.basename(py_file)}\n"project_description += f"{f.read()[:300]}...\n\n"# ⏸️ SERVER REQUESTS LLM FROM CLIENT (reverse flow!)sampling_result = await ctx.session.create_message(messages=[{"role": "user","content": {"type": "text","text": f"""You are a software architect assistant analyzing a codebase.{project_description}Provide:1. Project purpose and architecture pattern2. Technologies and frameworks used3. Security concerns (if any)4. Code quality assessmentKeep it concise (4-5 sentences)."""}}],max_tokens=300)# ▶️ TOOL RESUMES with LLM-generated contentai_insights = sampling_result.content.text# Store insights in persistent storageinsights_file = f"{project_path}/.ai-insights.json"with open(insights_file, "w") as f:json.dump({"project": project_name,"insights": ai_insights,"model": sampling_result.model,"timestamp": datetime.now().isoformat()}, f)# Also logged in Short-Term Memory (conversation event)return {"status": "success","project": project_name,"insights": ai_insights,"model_used": sampling_result.model,"saved_to": insights_file}
Execution Flow:
Plain text
1. User: "Generate insights for project-a"2. Server: Reads project files from /mnt/workspace → Builds analysis prompt3. 🔄 Server → Client: Sampling request with project description4. Client routes to LLM (Claude, Nova, GPT, etc.)5. LLM analyzes code structure, identifies patterns, checks security6. 🔄 Client → Server: Sampling response with insights7. Server: Tool resumes → Saves to .ai-insights.json → Returns analysis
Why MCP State Required:
- ✅ Request Correlation: Server must match response to waiting tool
- ✅ Reverse Data Flow: Server has no LLM—must request from client
- ✅ Multi-Turn Support: Advanced sampling can have tool use → results → completion
- ❌ Impossible in stateless mode: No way to correlate response to tool
3. Progress Reporting 📊 - Real-Time Updates
Tools can report progress during long-running operations.
Real-World Example: Codebase Analysis
Python
@mcp.tool()async def analyze_codebase(ctx: Context, project_path: str) -> dict:"""Analyze codebase with real-time progress updates."""# MCP progress: Report startawait ctx.report_progress(progress=0, total=100, message="Starting analysis")# Read files from AgentCore persistent storagefiles = []for root, dirs, filenames in os.walk(f"/mnt/workspace/{project_path}"):files.extend([os.path.join(root, f) for f in filenames if f.endswith('.py')])await ctx.report_progress(progress=20, total=100, message=f"Found {len(files)} files")# Analyze each fileissues = []for i, filepath in enumerate(files):with open(filepath) as f:code = f.read()# MCP sampling: Get LLM code reviewreview = await ctx.sample(messages=f"Review this Python code for security issues:\n{code}",max_tokens=300)issues.extend(review.issues)# Report progress after each fileprogress = 20 + int((i / len(files)) * 70)await ctx.report_progress(progress=progress,total=100,message=f"Analyzed {i+1}/{len(files)} files")# Save results to persistent storagewith open(f"/mnt/workspace/{project_path}/.analysis.json", "w") as f:json.dump({"issues": issues, "files_analyzed": len(files)}, f)await ctx.report_progress(progress=100, total=100, message="Complete")return {"status": "success","issues_found": len(issues),"files_analyzed": len(files)}
Execution Flow:
Plain text
1. User: "Analyze the codebase"2. Tool starts → Reports 0% progress3. Tool scans files → Reports 20% progress4. Tool analyzes file 1 → Reports 30% progress5. Tool analyzes file 2 → Reports 40% progress...N. Tool completes → Reports 100% progress
Why MCP State Helpful:
- ✅ User Feedback: Shows what's happening during long operations
- ✅ Non-Blocking: User sees progress without blocking
- ✅ Debugging: Helps identify where operations stall
Example Classification from Project Explorer:
Tool | Requires MCP State? | Why? |
|---|---|---|
list_projects | ❌ NO | Read-only operation, returns immediately |
add_project_context | ❌ NO | Simple CRUD, could use DB instead |
explore_project_files | ❌ NO | Executes command, returns result |
ask_project_questions | ✅ YES | ELICITATION - pauses for user input |
generate_project_insights | ✅ YES | SAMPLING - requests LLM content |
How MCP State Integrates with AgentCore
The Model Context Protocol (MCP) adds another layer of state management, particularly for tool-level state and server-side context.
MCP Session State
When MCP servers run in stateful mode (
stateless_http=False ), they maintain session state:Python
from mcp import Context@mcp.tool()async def plan_trip(ctx: Context, destination: str) -> str:# ctx.session_id contains the MCP session identifier# Elicitation: Ask user for input during tool executiontrip_type = await ctx.elicit(message="What type of trip? (business/leisure/adventure)",response_type=str)# Progress: Report long-running operation statusawait ctx.report_progress(progress=1, total=5)# Sampling: Request LLM-generated contenttips = await ctx.sample(messages=f"Give 3 tips for {trip_type} trip to {destination}",max_tokens=150)return f"Planned {trip_type} trip to {destination}"
MCP Stateful Features
Feature | Purpose | Requires State? |
|---|---|---|
Elicitation | Server asks user for input during tool execution | ✅ Yes |
Sampling | Server requests LLM-generated content | ✅ Yes |
Progress | Real-time progress reporting | ✅ Yes |
Resources | Expose data via URIs ( travel://destinations ) | ❌ No (stateless) |
Prompts | Reusable templates | ❌ No (stateless) |
Basic Tools | Function calls without state | ❌ No (stateless) |
The Overlap: MCP State + AgentCore Persistent Storage
Here's where it gets interesting—MCP state and AgentCore persistent storage complement each other:
Python
from mcp import Contextimport json@mcp.tool()async def analyze_codebase(ctx: Context, project_path: str) -> str:# MCP state: Track analysis progressawait ctx.report_progress(progress=0, total=100, message="Scanning files...")# AgentCore persistent storage: Access project filesfiles = os.listdir(f"/mnt/workspace/{project_path}")# MCP elicitation: Ask user for focus areafocus = await ctx.elicit(message=f"Found {len(files)} files. What should I analyze?",response_type=str)# AgentCore short-term memory: Store analysis resultsanalysis_result = {"files": files, "focus": focus, "issues": []}# AgentCore persistent storage: Save analysis to filewith open(f"/mnt/workspace/{project_path}/.analysis.json", "w") as f:json.dump(analysis_result, f)# MCP sampling: Generate recommendationsrecommendations = await ctx.sample(messages=f"Suggest improvements for {focus} in codebase...",max_tokens=200)return f"Analysis complete. Results saved to .analysis.json"
What's happening:
- MCP session state (
ctx.session_id) tracks the tool execution context
- AgentCore persistent storage (
/mnt/workspace) stores project files and analysis results
- MCP elicitation enables interactive user input mid-execution
- MCP progress reports long-running operation status
- MCP sampling generates LLM-powered content
- AgentCore short-term memory maintains conversation context
- AgentCore long-term memory extracts patterns from repeated analyses
State Persistence Comparison
State Type | Scope | Persistence | Use Case |
|---|---|---|---|
MCP Session State | Single MCP tool execution | Until MCP session terminates | Multi-turn tool interactions, progress tracking |
AgentCore Session Storage | Agent runtime session | 14 days (survives stop/resume) | Project files, artifacts, dependencies |
AgentCore Short-Term Memory | Conversation session | Session-scoped (configurable) | Conversation history, context for follow-ups |
AgentCore Long-Term Memory | Cross-session knowledge | Persistent (vector DB) | Learned patterns, episodic reflections, facts |
The Complete Picture: How All Four Layers Work Together
Let's walk through a real-world scenario to see how all four persistence layers collaborate:
Scenario: Multi-Day Software Development Project
Day 1 - Initial Setup:
Python
# User: "Create a FastAPI app with authentication"# 1. AgentCore Runtime processes request# 2. Agent uses tools to create files in /mnt/workspace/# - main.py, auth.py, requirements.txt# 3. Short-Term Memory stores conversation:# "Created FastAPI app, added JWT auth"# 4. Persistent Session Storage saves files to S3# 5. Long-Term Memory extraction (background):# Semantic: "User building FastAPI app"# User Preference: "User prefers JWT authentication"
Day 2 - User Stops Session:
Python
client.stop_runtime_session(runtimeSessionId="project-abc")# - Compute terminates# - Files flush from /mnt/workspace to S3# - Conversation state persisted (if using FileSessionManager)
Day 3 - User Resumes:
Python
# User: "Add rate limiting to the API"# 1. AgentCore provisions new compute# 2. Persistent Session Storage restores /mnt/workspace from S3:# main.py, auth.py, requirements.txt, .venv/ all present# 3. Short-Term Memory loaded from /mnt/workspace/.sessions/# Agent knows context: "FastAPI app with JWT auth"# 4. Agent executes task, creates rate_limiter.py# 5. New conversation events added to Short-Term Memory
Day 4 - User Requests Code Review:
Python
# User: "Review the code and suggest improvements"# MCP stateful tool execution:@mcp.tool()async def review_code(ctx: Context) -> str:# Access files from AgentCore persistent storagefiles = ["/mnt/workspace/main.py", "/mnt/workspace/auth.py"]# MCP progress: Report review progressawait ctx.report_progress(progress=1, total=len(files))# Analyze each fileissues = []for file in files:with open(file) as f:code = f.read()# MCP sampling: Get LLM code reviewreview = await ctx.sample(messages=f"Review this code:\n{code}",max_tokens=300)issues.append(review)await ctx.report_progress(progress=len(issues), total=len(files))# MCP elicitation: Ask which issues to fix firstpriority = await ctx.elicit(message="Found 5 issues. Which should I fix first?",response_type=str)return f"Review complete. Focusing on: {priority}"
Day 5 - Learning from Multiple Projects:
Python
# After building 5 FastAPI projects, Long-Term Memory extraction:# Episodic Memory generates cross-episode reflections:{"pattern": "FastAPI projects work best with","insights": ["JWT auth + rate limiting combination (5/5 projects)","Pydantic models prevent bugs early (4/5 projects)","Docker deployment reduces setup time (5/5 projects)"],"learned_from": "5 episodes","confidence": "high"}# Next project, agent retrieves this episodic memory:memories = retrieve_memory_records(query="FastAPI best practices")# Agent proactively suggests:"Based on past projects, I recommend starting with JWT auth,rate limiting, Pydantic models, and Docker deployment."
Architecture Diagram Explained (4 Layers)
Let's decode the updated architecture diagram showing all four persistence layers:

Top: User Interaction
Plain text
User: "Build FastAPI app, learn from meetings"↓AgentCore Runtime (Session: session-123)
The entry point—user sends requests to AgentCore Runtime with a session ID.
Left: Persistent Session Storage (Green)
Plain text
Filesystem: /mnt/workspace├── main.py├── auth.py├── requirements.txt├── .venv/├── .git/└── node_modules/Characteristics:✓ Survives stop/resume✓ 14-day retention✓ Access: open(), ls, cat, git✓ Backend: S3-backed filesystem✓ Use case: Code, artifacts, state
Connection: Runtime performs File I/O to read/write files to persistent storage.
Top Right: Short-Term Memory (Blue)
Plain text
Raw Conversation Events:- "Create FastAPI app..."- "I've created main.py..."- "Sprint planning 60min timebox"- "Meeting completed on time"Access:- ListEvents() - Get conversation history- Session-scoped, persists across restarts
Connection: Runtime creates events during conversation, stores in Short-Term Memory.
Bottom Right: Long-Term Memory (Purple)
Plain text
Multiple Extraction Strategies:[Semantic]Facts & Knowledge- "User prefers Python"- "Team has 5 developers"- "FastAPI project"[Summary]Conversation Condensation- "36-min pricing discussion"- "Decided on Enterprise plan"[User Preference]Settings & Personalization- "User wants dark mode"- "Email digest at 9 AM"[Episodic] ⭐Experience Sequences- "Sprint planning #1 - 90 min"- "Sprint planning #2 - 60 min ✓"- "Sprint planning #3 - 60 min ✓"Reflections (Cross-Episode):- "60-min timebox optimal"- "Pre-estimate stories first"- "Success rate: 90%"- "Learns what works"[Custom]Domain-Specific Logic- "Custom extraction patterns"Memory Retrieval:retrieve_memory_records(query="sprint planning patterns")Returns: Semantic facts + Episodic episodes + Reflections✓ Semantic Search across strategies✓ Cross-Session knowledge retrieval✓ Includes episodic reflections!
Connection: Background Extraction Job processes Short-Term Memory, creates Long-Term Memory records.
Bottom Center: MCP State (Orange)
Plain text
MCP Session State:- ctx.session_id: 'abc-12345'- Tool execution state preserved- Local variables saved- SSE stream kept openStateful Features (REQUIRE MCP State):🔄 Elicitation:Interactive User Input- Tool PAUSES at ctx.elicit()- Requests user address/data- Context preserved (cart, session)- Tool RESUMES with validated data✓ Execution suspension/resume✓ Form-based collection🤖 Sampling:LLM-Powered Content- Server builds prompt- ctx.sample() requests LLM- Tool PAUSES awaiting response- Client routes to LLM- Tool RESUMES with result✓ Reverse data flow✓ Request correlation📊 Progress:Real-Time Updates- ctx.report_progress()- progress=50, total=100- User sees status✓ Long-running operations
Connections:
- MCP tools can read project files from Persistent Storage (dashed green)
- MCP interactions (elicitation responses, sampling outputs) create Short-Term Memory events (dashed blue)
- MCP State enables interactive behaviors impossible in stateless mode
Flow Summary (4 Layers)
Plain text
1. User → Runtime → Creates Events → Short-Term Memory2. Runtime ↔ File I/O ↔ Persistent Session Storage3. Short-Term Memory → Extraction Job → Long-Term Memory (4 strategies)4. Long-Term Memory → retrieve_memory_records() → Runtime (context enrichment)5. Runtime → MCP Tools → Elicitation/Sampling/Progress → Interactive Workflows
Deep Dive: MCP State Integration Patterns
Now that we understand all four layers, let's explore detailed integration patterns showing how MCP State works with the other three layers.
Integration Pattern 1: Elicitation + Persistent Storage
Plain text
User → AgentCore Runtime → Invokes MCP Tool↓MCP Server (Stateful Mode)↓ctx.session_id maintained↓┌─────────────────────┼─────────────────────┐↓ ↓ ↓ctx.elicit() ctx.sample() ctx.report_progress()(User input) (LLM content) (Real-time updates)↓ ↓ ↓Response → Runtime → Short-Term Memory (conversation events)
Key insight: MCP state enables tool-level interactivity that feeds into AgentCore's Short-Term Memory, which later informs Long-Term Memory extraction.
Example: Episodic Learning + MCP State
Python
# Sprint planning tool with MCP state@mcp.tool()async def run_sprint_planning(ctx: Context, team_size: int) -> str:# MCP elicitation: Ask for timeboxtimebox = await ctx.elicit(message="What timebox? (60min/90min/120min)",response_type=str)# MCP progress: Track planning executionawait ctx.report_progress(progress=0, total=100)# Execute planning (stored in persistent storage)result = execute_planning(timebox, team_size)# Store result in /mnt/workspace for persistencewith open("/mnt/workspace/.sprint-results.json", "w") as f:json.dump(result, f)await ctx.report_progress(progress=100, total=100)return f"Sprint planning completed in {result['duration']}"# After 5 sprint planning sessions:# 1. MCP state tracked each execution (timebox choices, progress)# 2. Short-Term Memory captured conversation events# 3. Persistent Storage saved sprint results to files# 4. Long-Term Memory (Episodic) extracted:# "60-min timebox optimal (learned from 5 episodes)"
Best Practices: When to Use What
Use Persistent Session Storage When
- ✅ Agent needs to persist files, code, artifacts
- ✅ Installing dependencies (node_modules, .venv)
- ✅ Multi-day projects requiring state between sessions
- ✅ Git repositories, build outputs, logs
- ✅ Anything that would normally be on disk
Example: Code generation, project exploration, analysis artifacts
Use Short-Term Memory When
- ✅ Maintaining conversation context for follow-up questions
- ✅ Agent needs to reference earlier messages
- ✅ Multi-turn interactions within a session
- ✅ "Remember what I asked 5 messages ago"
Example: Project exploration ("Tell me about project-a" after "List projects"), contextual assistance
Use Long-Term Memory When
- ✅ Agent should learn patterns across sessions
- ✅ Personalization based on user preferences
- ✅ Episodic learning ("what worked in past projects")
- ✅ Cross-session knowledge retrieval
Example: Personal assistants, learning systems, knowledge bases
Use MCP Stateful Features When
- ✅ Tool needs to ask user questions during execution (elicitation)
- ✅ Long-running operations require progress reporting
- ✅ Tool needs LLM-generated content (sampling)
- ✅ Multi-turn tool interactions
Example: Interactive tools, wizards, complex workflows
Code Example: Putting It All Together
Here's a complete example combining all four persistence mechanisms:
Python
from bedrock_agentcore.runtime import BedrockAgentCoreAppfrom strands import Agentfrom strands.session import FileSessionManagerfrom mcp import Contextimport jsonimport osfrom datetime import datetimeapp = BedrockAgentCoreApp()WORKSPACE = "/mnt/workspace"# MCP stateful tools@mcp.tool()async def explore_and_analyze_project(ctx: Context, project_name: str) -> str:"""Complete project exploration with all 4 persistence layers."""# 1. Access AgentCore Persistent Storage (Layer 1)project_path = f"{WORKSPACE}/{project_name}"if not os.path.exists(project_path):return f"Project {project_name} not found in workspace"# 2. MCP Progress: Report start (Layer 4)await ctx.report_progress(progress=0, total=100, message="Starting exploration")# 3. Scan project files from persistent storagefiles = []for root, dirs, filenames in os.walk(project_path):files.extend([os.path.join(root, f) for f in filenames])await ctx.report_progress(progress=20, total=100, message=f"Found {len(files)} files")# 4. MCP Elicitation: Ask user for clarification (Layer 4)questions = await ctx.elicit(message=f"Please provide details about {project_name}",schema=ProjectQuestionsSchema # purpose, tech_stack, status, priority)await ctx.report_progress(progress=40, total=100, message="Questions answered")# 5. Store project context in persistent storage (Layer 1)context_file = f"{project_path}/.project-context.json"with open(context_file, "w") as f:json.dump({"project_name": project_name,"purpose": questions.data.purpose,"tech_stack": questions.data.tech_stack,"status": questions.data.status,"priority": questions.data.priority,"files_count": len(files),"timestamp": datetime.now().isoformat()}, f)await ctx.report_progress(progress=60, total=100, message="Context saved")# 6. MCP Sampling: Get LLM insights (Layer 4)# Read sample files for analysissample_code = ""python_files = [f for f in files if f.endswith('.py')][:3]for py_file in python_files:with open(py_file) as f:sample_code += f"File: {os.path.basename(py_file)}\n{f.read()[:200]}...\n\n"insights = await ctx.session.create_message(messages=[{"role": "user","content": {"type": "text","text": f"""Analyze this {questions.data.tech_stack} project:Purpose: {questions.data.purpose}Status: {questions.data.status}Files: {len(files)}Sample code:{sample_code}Provide:1. Architecture pattern2. Security concerns3. Improvement suggestions"""}}],max_tokens=500)await ctx.report_progress(progress=80, total=100, message="Insights generated")# 7. Save AI insights to persistent storage (Layer 1)insights_file = f"{project_path}/.ai-insights.json"with open(insights_file, "w") as f:json.dump({"project": project_name,"insights": insights.content.text,"model": insights.model,"timestamp": datetime.now().isoformat()}, f)await ctx.report_progress(progress=100, total=100, message="Complete!")# All interactions logged in Short-Term Memory (Layer 2) automatically# Long-Term Memory (Layer 3) will extract patterns across explorationsreturn f"""Project exploration complete!Context saved: {context_file}Insights saved: {insights_file}Files: {len(files)}Tech: {questions.data.tech_stack}Priority: {questions.data.priority}"""# AgentCore entrypoint@app.entrypointdef handle_request(payload):session_id = payload.get("session_id", "default")# Short-Term Memory (Layer 2): Stores conversation in persistent storagesession_manager = FileSessionManager(session_id=session_id,storage_dir=f"{WORKSPACE}/.sessions")# Long-Term Memory (Layer 3): Configure extraction strategiesmemory_config = {"strategies": ["semantic", "episodic", "user_preference", "summary"],"episodic": {"enable_reflections": True,"reflection_threshold": 3 # Generate reflections after 3 explorations}}agent = Agent(model=model,tools=[explore_and_analyze_project, list_projects, read_file, write_file],session_manager=session_manager,memory_config=memory_config,system_prompt="""You are a workspace exploration assistant.You have access to all 4 persistence layers:- Layer 1: Persistent storage at /mnt/workspace (files, contexts, insights)- Layer 2: Short-term memory (conversation history)- Layer 3: Long-term memory (episodic learning from past explorations)- Layer 4: MCP state (elicitation, sampling, progress)Learn from past explorations and proactively suggest project insights.""")# Retrieve relevant long-term memories (Layer 3)memories = retrieve_memory_records(query=payload.get("prompt"),strategies=["semantic", "episodic"])# Inject memories into contextcontext = f"Past knowledge:\n{memories}\n\n{payload.get('prompt')}"response = agent(context)return {"response": response.message["content"][0]["text"]}if __name__ == "__main__":app.run()
What's happening (all 4 layers):
- Layer 1 - Persistent Storage: Project files, contexts, insights saved to
/mnt/workspace
- Layer 2 - Short-Term Memory: Conversation history persisted to
.sessions/
- Layer 3 - Long-Term Memory: Extracts patterns like "User explores 12-15 projects/session"
- Layer 4 - MCP State: Elicitation (questions), Sampling (LLM insights), Progress (real-time updates)
- Integration: Elicitation responses → stored in filesystem, Sampling outputs → saved to files, All interactions → logged in STM
Performance Considerations
Persistent Session Storage
- Latency: First write incurs ~100-200ms for S3 replication; subsequent writes cached
- Throughput: Optimized for typical agent file I/O patterns (< 1GB/session)
- Cost: Storage costs based on S3 Standard pricing (~$0.023/GB/month)
Short-Term Memory
- In-Memory: Fastest, but lost on session stop
- Filesystem: Slight latency (~10-50ms), persists with session storage
- DynamoDB: ~50-100ms latency, enables cross-instance sharing
Long-Term Memory
- Extraction: Background job, doesn't block conversation
- Retrieval: Vector search ~50-200ms depending on corpus size
- Storage: Vector embeddings + metadata in managed vector DB
MCP Stateful Features
- Elicitation: Adds round-trip latency for user input (~1-5 seconds depending on user)
- Progress: Minimal overhead (~1-5ms per update)
- Sampling: Adds LLM inference latency (~500ms-3s depending on model)
Common Patterns and Anti-Patterns
✅ Do This
Pattern 1: Persist conversation with project files
Python
# Store session data alongside project in persistent storagesession_manager = FileSessionManager(storage_dir=f"{WORKSPACE}/.sessions/{project_id}")# Agent resumes with both files AND conversation context
Pattern 2: Use episodic memory for multi-project learning
Python
# After each project completion, extract episodic memoryextract_memory(strategies=["episodic"], session_id=session_id)# Next project retrieves patterns: "Tests first reduces bugs 80%"
Pattern 3: MCP elicitation for ambiguous requests
Python
@mcp.tool()async def deploy_app(ctx: Context, app_name: str):# Ask for deployment target instead of guessingtarget = await ctx.elicit(message="Deploy to? (staging/production)",response_type=str)# Explicit user confirmation for production deployments
❌ Don't Do This
Anti-Pattern 1: Storing large binaries in persistent storage
Python
# ❌ Don't store large ML models, datasets in /mnt/workspace# Use S3 directly and cache locally:boto3.client('s3').download_file('bucket', 'model.bin', '/tmp/model.bin')
Anti-Pattern 2: Relying on MCP state for long-term data
Python
# ❌ MCP session terminates when server restarts# ✅ Use AgentCore persistent storage or long-term memorywith open(f"{WORKSPACE}/mcp-state.json", "w") as f:json.dump(state, f)
Anti-Pattern 3: Not waiting for StopRuntimeSession
Python
# ❌ Don't resume immediately after stoppingclient.stop_runtime_session(runtimeSessionId="session-123")client.invoke_agent_runtime(runtimeSessionId="session-123") # Data loss risk!# ✅ Wait for graceful shutdownwaiter = client.get_waiter('runtime_session_stopped')waiter.wait(runtimeSessionId="session-123")# Now safe to resume
Debugging and Observability
Inspect Persistent Storage
Bash
# SSH into agent container (development/debugging)aws bedrock-agentcore debug-session --runtime-session-id session-123# Inside containerls -la /mnt/workspacecat /mnt/workspace/.sessions/conv-001.jsongit log # If using git in persistent storage
Monitor Memory Extraction
Python
# Check if extraction job completedresponse = client.describe_memory_extraction(sessionId="conv-001",extractionJobId="job-abc")print(response['status']) # COMPLETED, FAILED, IN_PROGRESS
View MCP Session State
Bash
# MCP server logs show session lifecycleINFO: MCP session session-xyz initializedINFO: Elicitation request: "Deploy to staging or production?"INFO: Progress update: 50/100INFO: Session session-xyz terminated
Long-Term Memory Queries
Python
# Query episodic reflectionsmemories = retrieve_memory_records(query="what deployment patterns work best",strategies=["episodic"],include_reflections=True)for memory in memories:print(f"Pattern: {memory['content']}")print(f"Learned from: {memory['metadata']['episode_count']} episodes")print(f"Confidence: {memory['metadata']['confidence']}")
Conclusion: The Power of Four-Layer Persistence
Amazon Bedrock AgentCore's four-layer persistence architecture enables agents to:
- Persist complex state (files, projects, dependencies) across days/weeks via Persistent Session Storage
- Maintain conversation context for natural follow-up interactions via Short-Term Memory
- Learn from experience through episodic reflections across sessions via Long-Term Memory
- Execute interactive workflows with user input, LLM sampling, and progress reporting via MCP State
The key insight is that these layers complement each other to cover different persistence needs:
Layer | Purpose | Scope | Example |
|---|---|---|---|
Persistent Storage | Files & artifacts | 14 days | Code, .venv/, build outputs |
Short-Term Memory | Conversation context | Session | "Add auth" knows about "Create API" |
Long-Term Memory | Accumulated knowledge | Cross-session | "60-min sprints work best" (learned from 5 episodes) |
MCP State | Tool interactivity | Tool execution | Pause for address, request LLM summary |
Together, they enable agents to truly "remember" across four dimensions:
- Filesystem state (what files exist)
- Conversation history (what was discussed)
- Learned patterns (what works)
- Interactive workflows (how to collect input and generate content)
Why MCP State Is the Missing Piece
Without MCP State, agents are limited to:
- ❌ Fire-and-forget tool calls (no interactivity)
- ❌ All data collected upfront (no progressive refinement)
- ❌ No access to client-side AI capabilities (no sampling)
With MCP State, agents can:
- ✅ Collect sensitive information securely (elicitation)
- ✅ Leverage client-side LLMs for generation (sampling)
- ✅ Provide real-time feedback during long operations (progress)
- ✅ Build sophisticated multi-turn workflows
Next Steps
- Try it yourself: AgentCore Getting Started Guide
- Persistent Storage: Runtime Filesystem Documentation
- MCP State: Stateful Features Guide
- Memory API: Long-Term Memory Documentation
AWS Documentation
Model Context Protocol (MCP)
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article