SDK v1.0 migration guide
Overview
Section titled “Overview”SDK v1.0 introduces breaking changes to improve consistency and align with modern API design patterns:
- Naming convention: All properties now use
snake_caseinstead ofcamelCase - Client initialization: Simplified client constructor with renamed parameters
- Method names: Several methods renamed for clarity
- Type imports: Types moved to subpath exports for better organization
- Enums: Replaced with string literal types
- Tool calls: Changed from single object to array structure
- Pagination: List methods now return page objects
Quick Reference
Section titled “Quick Reference”Package Update
Section titled “Package Update”Update your package dependency:
{ "dependencies": {- "@letta-ai/letta-client": "0.1.211"+ "@letta-ai/letta-client": "1.0.0" }}[tool.poetry.dependencies]-letta-client = "0.1.324"+letta-client = "1.0.0"Import Changes
Section titled “Import Changes”// Old- import { LettaClient, Letta } from "@letta-ai/letta-client";
// New+ import Letta from "@letta-ai/letta-client";+ import type {+ Block,+ CreateBlock,+ AgentType+ } from "@letta-ai/letta-client/resources/agents/agents";+ import type {+ LettaMessageUnion,+ ApprovalCreate+ } from "@letta-ai/letta-client/resources/agents/messages";+ import type {+ LlmConfig+ } from "@letta-ai/letta-client/resources/models/models";# Old- from letta_client import Letta, LettaClient
# New+ from letta_client import Letta+ from letta_client.schemas.agent import Block, CreateBlock, AgentType+ from letta_client.schemas.message import LettaMessageUnion, ApprovalCreate+ from letta_client.schemas.llm_config import LlmConfigClient Instantiation
Section titled “Client Instantiation”// Old- const client = new LettaClient({- token: process.env.LETTA_API_KEY,- baseUrl: "https://api.letta.com"- });
// New+ const client = new Letta({+ apiKey: process.env.LETTA_API_KEY,+ baseURL: "https://api.letta.com"+ });# Old- client = LettaClient(- token=os.environ["LETTA_API_KEY"],- base_url="https://api.letta.com"- )
# New+ client = Letta(+ api_key=os.environ["LETTA_API_KEY"],+ base_url="https://api.letta.com"+ )Breaking Changes by Category
Section titled “Breaking Changes by Category”1. Pagination
Section titled “1. Pagination”All list endpoints now use cursor-based pagination with consistent parameters:
// Old - various pagination stylesconst messages = await client.agents.messages.list(agentId, { sort_by: "created_at", ascending: true});
// New - standardized cursor paginationconst messagesPage = await client.agents.messages.list(agentId, { before: "msg_123", // cursor (message ID) after: "msg_456", // cursor (message ID) limit: 50, order: "asc" // or "desc"});const messages = messagesPage.items;# Oldmessages = client.agents.messages.list( agent_id=agent_id, sort_by="created_at", ascending=True)
# Newmessages_page = client.agents.messages.list( agent_id=agent_id, before="msg_123", after="msg_456", limit=50, order="asc")messages = messages_page.itemsAffected endpoints:
agents.list()- renamedsort_by→order_by,ascending→orderagents.messages.list()agents.tools.list()agents.blocks.list()agents.files.list()agents.folders.list()agents.groups.list()blocks.list()folders.list()folders.files.list()folders.passages.list()folders.agents.list()groups.list()groups.messages.list()identities.list()providers.list()runs.list()runs.messages.list()runs.steps.list()jobs.list()steps.list()tags.list()tools.list()batches.list()batches.messages.list()
2. Method Renames and Endpoint Restructuring
Section titled “2. Method Renames and Endpoint Restructuring”Many methods were reorganized for better SDK structure:
// Agent updates- await client.agents.modify(agentId, updates)+ await client.agents.update(agentId, updates)
// Message operations- await client.agents.summarize_agent_conversation(agentId)+ await client.agents.messages.compact(agentId)
- await client.agents.cancel_agent_run(agentId)+ await client.agents.messages.cancel(agentId)
- await client.agents.messages.preview_raw_payload(agentId, messages)+ await client.agents.messages.preview(agentId, messages)
// Agent file operations- await client.agents.list_agent_files(agentId)+ await client.agents.files.list(agentId)
// Export/Import- await client.agents.export_agent_serialized(agentId)+ await client.agents.exportFile(agentId)
- await client.agents.import_agent_serialized(file)+ await client.agents.importFile(file)
// Folder operations- await client.folders.get_agents_for_folder(folderId)+ await client.folders.agents.list(folderId)
- await client.folders.retrieve_folder_metadata(folderId)+ await client.folders.retrieve_metadata(folderId)
// Provider operations- await client.providers.check_provider(providerId)+ await client.providers.check(providerId)
// Telemetry- await client.telemetry.retrieve_provider_trace(stepId)+ await client.steps.trace(stepId)
// Step metrics- await client.steps.retrieve_step_metrics(stepId)+ await client.steps.metrics.retrieve(stepId)
// Batch messages- await client.messages.list_batch_messages(batchId)+ await client.batches.messages.list(batchId)
// Multi-agent groups- agent.multi_agent_group+ agent.managed_group# Agent updates- client.agents.modify(agent_id, **updates)+ client.agents.update(agent_id, **updates)
// Message operations- client.agents.summarize_agent_conversation(agent_id)+ client.agents.messages.compact(agent_id)
- client.agents.cancel_agent_run(agent_id)+ client.agents.messages.cancel(agent_id)
// Export/Import- client.agents.export_agent_serialized(agent_id)+ client.agents.export_file(agent_id)
- client.agents.import_agent_serialized(file)+ client.agents.import_file(file)
// Folder operations- client.folders.get_agents_for_folder(folder_id)+ client.folders.agents.list(folder_id)
// Provider operations- client.providers.check_provider(provider_id)+ client.providers.check(provider_id)3. Deprecations
Section titled “3. Deprecations”Several endpoints and fields are now deprecated:
Deprecated endpoints:
client.agents.search()- useclient.agents.list()with filtersclient.messages.search()- useclient.agents.messages.list()with filtersclient.runs.list_active()- useclient.runs.list(active=True)client.jobs.list_active()- useclient.jobs.list(active=True)client.folders.get_by_name()- useclient.folders.list(name="...")- MCP routes under
/tools/mcp/servers- replaced with new/mcp-serversendpoints- All old MCP methods moved from
client.tools.mcp.serverstoclient.mcp_servers - Now use server IDs and tool IDs instead of names
- All old MCP methods moved from
- Sources-related routes - replaced with folders
- Passages routes - replaced with archives
- Legacy agent architecture routes
- All
/countendpoints
Deprecated fields:
agent.memory- useagent.blocksstep.messages- useclient.steps.messages.list(step_id)agent.identity_ids- replaced withagent.identities(full objects)agent.multi_agent_group- renamed toagent.managed_groupuse_assistant_messageparameter - no longer neededtool_exec_environment_variables- renamed tosecrets
Deprecated on agent/block objects:
- Template-related fields:
is_template,base_template_id,deployment_id entity_id,preserve_on_migration,hiddennameon blocks (uselabel)
4. Property Names (camelCase → snake_case)
Section titled “4. Property Names (camelCase → snake_case)”All API properties now use snake_case:
// Agent properties- agent.llmConfig+ agent.llm_config
- agent.contextWindowLimit+ agent.context_window_limit
- agent.blockIds+ agent.block_ids
- agent.includeBaseTools+ agent.include_base_tools
- agent.includeBaseToolRules+ agent.include_base_tool_rules
- agent.initialMessageSequence+ agent.initial_message_sequence
// Message properties- message.messageType+ message.message_type
- message.toolCallId+ message.tool_call_id
- message.toolReturn+ message.tool_return
- message.toolCall+ message.tool_calls // Also changed to array!
// API parameters- streamTokens: true+ stream_tokens: true // Also add streaming: true for streaming requests
- approvalRequestId: id+ approval_request_id: id# Agent properties- agent.llm_config # Already snake_case+ agent.llm_config # No change needed
- agent.context_window_limit+ agent.context_window_limit # No change needed
# Python SDK was already using snake_case# Most changes affect TypeScript/JavaScript only2. Agent Type Specification
Section titled “2. Agent Type Specification”// Old- agentType: Letta.AgentType.LettaV1Agent
// New+ agent_type: "letta_v1_agent" as AgentType# Old- agent_type=AgentType.LETTA_V1_AGENT
# New+ agent_type="letta_v1_agent"3. Method Renames
Section titled “3. Method Renames”// Agent updates- await client.agents.modify(agentId, { model, llmConfig })+ await client.agents.update(agentId, { model, llm_config })
// Message streaming- client.agents.messages.createStream(agentId, { messages, streamTokens: true })+ client.agents.messages.stream(agentId, { messages, stream_tokens: true })# Agent updates- client.agents.modify(agent_id, model=model, llm_config=config)+ client.agents.update(agent_id, model=model, llm_config=config)
# Message streaming- client.agents.messages.create_stream(agent_id, messages=messages)+ client.agents.messages.create(agent_id, messages=messages, streaming=True)4. Message Roles and Stop Reasons
Section titled “4. Message Roles and Stop Reasons”Enums replaced with string literals:
// Message roles- role: Letta.MessageCreateRole.User+ role: "user"
// Stop reasons- if (stopReason === Letta.StopReasonType.EndTurn)+ if (stopReason === "end_turn")
- if (stopReason === Letta.StopReasonType.RequiresApproval)+ if (stopReason === "requires_approval")# Message roles- role=MessageRole.USER+ role="user"
# Stop reasons- if stop_reason == StopReasonType.END_TURN:+ if stop_reason == "end_turn":
- if stop_reason == StopReasonType.REQUIRES_APPROVAL:+ if stop_reason == "requires_approval":5. Tool Calls Structure
Section titled “5. Tool Calls Structure”Tool calls changed from single object to array:
// Old - single tool_call- if (message.messageType === "approval_request_message") {- const toolCall = message.toolCall;- const id = toolCall.toolCallId;- const name = toolCall.name;- }
// New - tool_calls array+ if (message.message_type === "approval_request_message") {+ const toolCalls = message.tool_calls || [];+ if (toolCalls.length > 0) {+ const toolCall = toolCalls[0];+ const id = toolCall.tool_call_id;+ const name = toolCall.name;+ }+ }# Old - single tool_call- if message.message_type == "approval_request_message":- tool_call = message.tool_call- id = tool_call.tool_call_id- name = tool_call.name
# New - tool_calls array+ if message.message_type == "approval_request_message":+ tool_calls = message.tool_calls or []+ if len(tool_calls) > 0:+ tool_call = tool_calls[0]+ id = tool_call.tool_call_id+ name = tool_call.name6. Pagination
Section titled “6. Pagination”List methods now return page objects:
// Old- const messages = await client.agents.messages.list(agentId);
// New+ const messagesPage = await client.agents.messages.list(agentId);+ const messages = messagesPage.items;# Old- messages = client.agents.messages.list(agent_id=agent_id)
# New+ messages_page = client.agents.messages.list(agent_id=agent_id)+ messages = messages_page.items7. Date Handling
Section titled “7. Date Handling”// Old- date: new Date()
// New+ date: new Date().toISOString()# Python handles this automaticallyfrom datetime import datetimedate = datetime.now() # Works in both versions8. Archive Management (New APIs)
Section titled “8. Archive Management (New APIs)”New endpoints for managing archival memory:
// Create archiveconst archive = await client.archives.create({ name: "my-archive", description: "Project knowledge base"});
// List archivesconst archives = await client.archives.list();
// Get archive by IDconst archive = await client.archives.retrieve(archiveId);
// Update archiveawait client.archives.update(archiveId, { name: "updated-name" });
// Delete archiveawait client.archives.delete(archiveId);
// Attach archive to agentawait client.agents.archives.attach(agentId, archiveId);
// Detach archive from agentawait client.agents.archives.detach(agentId, archiveId);
// List agents using an archiveconst agents = await client.archives.agents.list(archiveId);
// Create passages in archiveawait client.archives.passages.create(archiveId, { text: "Important fact" });# Create archivearchive = client.archives.create( name="my-archive", description="Project knowledge base")
# Attach/detachclient.agents.archives.attach(agent_id, archive_id)client.agents.archives.detach(agent_id, archive_id)
# Create passagesclient.archives.passages.create(archive_id, text="Important fact")9. Identity and Block Management
Section titled “9. Identity and Block Management”New attach/detach patterns for identities and blocks:
// Attach identity to agentawait client.agents.identities.attach(agentId, identityId);
// Detach identity from agentawait client.agents.identities.detach(agentId, identityId);
// Attach identity to blockawait client.blocks.identities.attach(blockId, identityId);
// Detach identity from blockawait client.blocks.identities.detach(blockId, identityId);
// Agent now returns full identity objectsconst agent = await client.agents.retrieve(agentId);// Old: agent.identity_ids = ["id1", "id2"]// New: agent.identities = [{ id: "id1", name: "Alice", ... }, ...]# Attach/detach identitiesclient.agents.identities.attach(agent_id, identity_id)client.agents.identities.detach(agent_id, identity_id)
# Full identity objectsagent = client.agents.retrieve(agent_id)for identity in agent.identities: print(identity.name)10. Agent Configuration Updates
Section titled “10. Agent Configuration Updates”New parameters available for agent creation and updates:
// Temperature, top_p, reasoning_effort now available at top levelconst agent = await client.agents.create({ model: "openai/gpt-4", temperature: 0.7, top_p: 0.9, reasoning_effort: "medium", max_tokens: 4096, context_window_limit: 128000});
// Update agent configurationawait client.agents.update(agentId, { temperature: 0.5, context_window_limit: 64000});# Create with configurationagent = client.agents.create( model="openai/gpt-4", temperature=0.7, top_p=0.9, reasoning_effort="medium", max_tokens=4096, context_window_limit=128000)
# Update configurationclient.agents.update( agent_id, temperature=0.5)11. Message Input Shorthand
Section titled “11. Message Input Shorthand”Simplified syntax for sending simple user messages:
// Old - verboseconst response = await client.agents.messages.create(agentId, { messages: [{ role: "user", content: "Hello!" }]});
// New - shorthand availableconst response = await client.agents.messages.create(agentId, { input: "Hello!" // Automatically creates user message});
// Both forms still supported# Oldresponse = client.agents.messages.create( agent_id, messages=[{"role": "user", "content": "Hello!"}])
# New shorthandresponse = client.agents.messages.create( agent_id, input="Hello!")12. Attach/Detach Return Values
Section titled “12. Attach/Detach Return Values”All attach/detach endpoints now return None instead of agent/object state:
// Old - returned updated agentconst updatedAgent = await client.agents.tools.attach(agentId, toolId);
// New - returns void/Noneawait client.agents.tools.attach(agentId, toolId);// Fetch agent separately if neededconst agent = await client.agents.retrieve(agentId);# Oldupdated_agent = client.agents.tools.attach(agent_id, tool_id)
# Newclient.agents.tools.attach(agent_id, tool_id)agent = client.agents.retrieve(agent_id)Affected methods:
agents.tools.attach/detachagents.blocks.attach/detachagents.folders.attach/detachagents.archives.attach/detachagents.identities.attach/detachblocks.identities.attach/detach
13. Agent Import/Export Changes
Section titled “13. Agent Import/Export Changes”Import endpoint now supports name overriding:
// Old - append_copy_suffix parameterconst agent = await client.agents.import(file, { append_copy_suffix: true // Deprecated});
// New - override_name parameterconst agent = await client.agents.importFile(file, { override_name: "my-imported-agent" // Optional, exact name to use});# Oldagent = client.agents.import_agent( file, append_copy_suffix=True)
# Newagent = client.agents.import_file( file, override_name="my-imported-agent")14. Query Parameter to Request Body Changes
Section titled “14. Query Parameter to Request Body Changes”Several endpoints moved from query parameters to request body:
// Tool approval settings (was query params, now request body)await client.agents.tools.update_approval(agentId, toolName, { require_approval: true});
// Reset messages (was query param, now request body)await client.agents.messages.reset(agentId, { add_default_initial_messages: false});
// Steps feedback (was query params, now request body)await client.steps.feedback.create(stepId, { rating: "positive", tags: ["helpful", "accurate"]});# Tool approvalclient.agents.tools.update_approval( agent_id, tool_name, require_approval=True)
# Reset messagesclient.agents.messages.reset( agent_id, add_default_initial_messages=False)15. Tags Endpoint
Section titled “15. Tags Endpoint”Tags list endpoint now uses name parameter instead of query_text:
// Oldconst tags = await client.tags.list({ query_text: "important" });
// Newconst tags = await client.tags.list({ name: "important" });# Oldtags = client.tags.list(query_text="important")
# Newtags = client.tags.list(name="important")16. Project ID Handling
Section titled “16. Project ID Handling”Project ID is now passed in the client constructor or headers:
// Pass in constructorconst client = new Letta({ apiKey: process.env.LETTA_API_KEY, projectId: "proj_123"});
// No longer in URL paths- await client.templates.agents.create("proj_123", templateVersion, data)+ await client.templates.agents.create(templateVersion, data)# Pass in constructorclient = Letta( api_key=os.environ["LETTA_API_KEY"], project_id="proj_123")17. MCP (Model Context Protocol) Server Management
Section titled “17. MCP (Model Context Protocol) Server Management”MCP routes have been completely restructured with new endpoints under /mcp-servers:
// OLD ROUTES (under /tools/mcp/servers - DEPRECATED)// Using server names and tool names
// List MCP servers- const servers = await client.tools.mcp.servers.list();
// Add MCP server- await client.tools.mcp.servers.create(serverConfig);
// Update MCP server by name- await client.tools.mcp.servers.update(serverName, updateConfig);
// Delete MCP server by name- await client.tools.mcp.servers.delete(serverName);
// List tools from a server by name- const tools = await client.tools.mcp.servers.tools.list(serverName);
// Add individual tool by name- await client.tools.mcp.servers.tools.add(serverName, toolName);
// Resync tools- await client.tools.mcp.servers.resync(serverName);
// Execute tool by names- await client.tools.mcp.servers.tools.execute(serverName, toolName, { args });
// Connect to server (for OAuth)- await client.tools.mcp.servers.connect(serverConfig);
// NEW ROUTES (under /mcp-servers)// Using server IDs and tool IDs
// List MCP servers (returns array of server objects with IDs)+ const servers = await client.mcp_servers.list();
// Create MCP server (automatically syncs tools)+ const server = await client.mcp_servers.create(serverConfig);+ // Returns: { id: "mcp_server_123", name: "...", ... }
// Get MCP server by ID+ const server = await client.mcp_servers.retrieve(serverId);
// Update MCP server by ID+ await client.mcp_servers.update(serverId, updateConfig);
// Delete MCP server by ID+ await client.mcp_servers.delete(serverId);
// List tools from a server by ID+ const tools = await client.mcp_servers.tools.list(serverId);
// Get specific tool by ID+ const tool = await client.mcp_servers.tools.retrieve(serverId, toolId);
// Run/execute tool by ID+ const result = await client.mcp_servers.tools.run(serverId, toolId, {+ args: { key: "value" }+ });
// Refresh tools (replaces resync)+ await client.mcp_servers.refresh(serverId);
// Connect to server (for OAuth) - now uses server ID+ await client.mcp_servers.connect(serverId);# OLD ROUTES (DEPRECATED)- servers = client.tools.mcp.servers.list()- client.tools.mcp.servers.create(server_config)- client.tools.mcp.servers.update(server_name, update_config)- client.tools.mcp.servers.delete(server_name)- tools = client.tools.mcp.servers.tools.list(server_name)- client.tools.mcp.servers.tools.add(server_name, tool_name)- client.tools.mcp.servers.resync(server_name)- client.tools.mcp.servers.tools.execute(server_name, tool_name, args=args)
# NEW ROUTES+ servers = client.mcp_servers.list()+ server = client.mcp_servers.create(server_config)+ server = client.mcp_servers.retrieve(server_id)+ client.mcp_servers.update(server_id, update_config)+ client.mcp_servers.delete(server_id)+ tools = client.mcp_servers.tools.list(server_id)+ tool = client.mcp_servers.tools.retrieve(server_id, tool_id)+ result = client.mcp_servers.tools.run(server_id, tool_id, args={"key": "value"})+ client.mcp_servers.refresh(server_id)+ client.mcp_servers.connect(server_id)Key Changes:
- Namespace: Moved from
client.tools.mcp.serverstoclient.mcp_servers - Identification: Use server IDs and tool IDs instead of names
- Old:
serverName(string) → New:serverId(ID string like"mcp_server_123") - Old:
toolName(string) → New:toolId(ID string like"tool_456")
- Old:
- Tool Management: Tools are now automatically synced when creating a server
- No longer need to manually “add” individual tools
- Use
refresh()to resync tools (replacesresync())
- Tool Execution: Method renamed from
execute()torun() - Server Retrieval: New
retrieve()method to get individual server by ID - Tool Retrieval: New
retrieve()method to get individual tool by ID
Migration Example:
// Before: Using namesconst servers = await client.tools.mcp.servers.list();const myServer = servers.find(s => s.name === "my-server");const tools = await client.tools.mcp.servers.tools.list("my-server");const myTool = tools.find(t => t.name === "my-tool");await client.tools.mcp.servers.tools.execute("my-server", "my-tool", { args: { query: "hello" }});
// After: Using IDsconst servers = await client.mcp_servers.list();const myServer = servers.find(s => s.name === "my-server");const serverId = myServer.id; // Get ID from server objectconst tools = await client.mcp_servers.tools.list(serverId);const myTool = tools.find(t => t.name === "my-tool");const toolId = myTool.id; // Get ID from tool objectawait client.mcp_servers.tools.run(serverId, toolId, { args: { query: "hello" }});# Beforeservers = client.tools.mcp.servers.list()my_server = next(s for s in servers if s.name == "my-server")tools = client.tools.mcp.servers.tools.list("my-server")my_tool = next(t for t in tools if t.name == "my-tool")client.tools.mcp.servers.tools.execute( "my-server", "my-tool", args={"query": "hello"})
# Afterservers = client.mcp_servers.list()my_server = next(s for s in servers if s.name == "my-server")server_id = my_server.idtools = client.mcp_servers.tools.list(server_id)my_tool = next(t for t in tools if t.name == "my-tool")tool_id = my_tool.idclient.mcp_servers.tools.run( server_id, tool_id, args={"query": "hello"})Notes:
- MCP servers and tools now have persistent IDs in the database
- Server names are no longer unique identifiers - use IDs instead
- Tool schemas are automatically kept in sync via the
refresh()endpoint - The old routes under
/tools/mcp/serversare deprecated and will be removed
Migration Examples
Section titled “Migration Examples”Complete Agent Creation
Section titled “Complete Agent Creation”// Beforeconst agent = await client.agents.create({ agentType: Letta.AgentType.LettaV1Agent, model: "openai/gpt-4", contextWindowLimit: 200_000, blockIds: ["block-1", "block-2"], includeBaseTools: false, includeBaseToolRules: false, initialMessageSequence: [],});
// Afterconst agent = await client.agents.create({ agent_type: "letta_v1_agent" as AgentType, model: "openai/gpt-4", context_window_limit: 200_000, block_ids: ["block-1", "block-2"], include_base_tools: false, include_base_tool_rules: false, initial_message_sequence: [],});# Beforeagent = client.agents.create( agent_type=AgentType.LETTA_V1_AGENT, model="openai/gpt-4", context_window_limit=200_000, block_ids=["block-1", "block-2"], include_base_tools=False, include_base_tool_rules=False, initial_message_sequence=[],)
# Afteragent = client.agents.create( agent_type="letta_v1_agent", model="openai/gpt-4", context_window_limit=200_000, block_ids=["block-1", "block-2"], include_base_tools=False, include_base_tool_rules=False, initial_message_sequence=[],)Streaming Messages
Section titled “Streaming Messages”// Beforeconst stream = await client.agents.messages.createStream(agentId, { messages: [{ role: Letta.MessageCreateRole.User, content: "Hello" }], streamTokens: true,});
// Afterconst stream = await client.agents.messages.stream(agentId, { messages: [{ role: "user", content: "Hello" }], stream_tokens: true,});# Beforestream = client.agents.messages.create_stream( agent_id=agent_id, messages=[{"role": "user", "content": "Hello"}], stream_tokens=True,)
# Afterstream = client.agents.messages.create( agent_id=agent_id, messages=[{"role": "user", "content": "Hello"}], streaming=True, stream_tokens=True,)Handling Approvals
Section titled “Handling Approvals”// Beforeif (message.messageType === "approval_request_message") { const toolCall = message.toolCall; await client.agents.messages.create(agentId, { messages: [{ type: "approval", approvalRequestId: toolCall.toolCallId, approve: true, }], });}
// Afterif (message.message_type === "approval_request_message") { const toolCalls = message.tool_calls || []; if (toolCalls.length > 0) { const toolCall = toolCalls[0]; await client.agents.messages.create(agentId, { messages: [{ type: "approval", approval_request_id: toolCall.tool_call_id, approve: true, }], }); }}# Beforeif message.message_type == "approval_request_message": tool_call = message.tool_call client.agents.messages.create( agent_id=agent_id, messages=[{ "type": "approval", "approval_request_id": tool_call.tool_call_id, "approve": True, }], )
# Afterif message.message_type == "approval_request_message": tool_calls = message.tool_calls or [] if len(tool_calls) > 0: tool_call = tool_calls[0] client.agents.messages.create( agent_id=agent_id, messages=[{ "type": "approval", "approval_request_id": tool_call.tool_call_id, "approve": True, }], )Updating Agent Configuration
Section titled “Updating Agent Configuration”// Beforeawait client.agents.modify(agentId, { model: "openai/gpt-4", llmConfig: { temperature: 0.7 }});const agent = await client.agents.retrieve(agentId);const config = agent.llmConfig;
// Afterawait client.agents.update(agentId, { model: "openai/gpt-4", llm_config: { temperature: 0.7 }});const agent = await client.agents.retrieve(agentId);const config = agent.llm_config;# Beforeclient.agents.modify( agent_id=agent_id, model="openai/gpt-4", llm_config={"temperature": 0.7})agent = client.agents.retrieve(agent_id=agent_id)config = agent.llm_config
# Afterclient.agents.update( agent_id=agent_id, model="openai/gpt-4", llm_config={"temperature": 0.7})agent = client.agents.retrieve(agent_id=agent_id)config = agent.llm_configMigration Checklist
Section titled “Migration Checklist”Use this checklist to ensure a complete migration:
Core SDK Changes:
- Update package version to
1.0.0-alpha.10or later - Update all imports (client and types)
- Replace
LettaClientwithLetta - Update client constructor params:
token→apiKey,baseUrl→baseURL - Add
projectIdto client constructor if using multi-project setup - Convert all property names from
camelCasetosnake_case - Replace enum references with string literals
- Convert
Dateobjects to ISO strings where required - Update type annotations to use new import paths
Method Renames:
- Update
modify()calls toupdate() - Update
createStream()calls tocreate()withstreaming: true - Rename
summarize_agent_conversation()→messages.compact() - Rename
cancel_agent_run()→messages.cancel() - Rename
preview_raw_payload()→messages.preview() - Rename
list_agent_files()→files.list() - Rename
export_agent_serialized()→export_file() - Rename
import_agent_serialized()→import_file() - Rename folder/provider method names (see section 2)
- Update telemetry routes to use
steps.trace()
Pagination:
- Update all list methods to access
.itemsproperty - Replace
sort_bywithorder_byinagents.list() - Replace
ascendingwithorderparameter - Update pagination parameters:
before,after,limit,order - Handle cursor-based pagination for all list endpoints
Message Handling:
- Handle
tool_callsas an array instead of single object - Update
identity_idsreferences to useidentities(full objects) - Replace
agent.memorywithagent.blocks - Update
step.messagesto usesteps.messages.list() - Consider using new
inputshorthand for simple messages
Deprecations:
- Remove usage of deprecated search endpoints
- Replace
list_active()withlist(active=True) - Remove
use_assistant_messageparameter - Replace
tool_exec_environment_variableswithsecrets - Remove template-related fields from agent/block objects
- Replace sources endpoints with folders
- Replace passages endpoints with archives
New Features:
- Update attach/detach methods (now return
None) - Use new archive management APIs if needed
- Update agent import to use
override_nameinstead ofappend_copy_suffix - Move query parameters to request body for affected endpoints
- Use new agent configuration parameters (
temperature,top_p, etc.)
MCP (Model Context Protocol) Changes:
- Migrate from
client.tools.mcp.serverstoclient.mcp_servers - Update MCP server references to use IDs instead of names
- Update MCP tool references to use IDs instead of names
- Remove manual tool “add” operations (tools auto-sync on server create)
- Replace
resync()calls withrefresh() - Replace
execute()calls withrun() - Add server/tool ID lookup logic if using names
- Update OAuth connection flow to use server IDs
Testing:
- Test all agent operations (create, update, message)
- Test streaming and approval flows
- Verify memory block operations still work
- Test pagination on list endpoints
- Test archive management if used
- Verify identity/block attach/detach operations
- Test agent import/export
Automated Migration Tools
Section titled “Automated Migration Tools”Find and Replace Script
Section titled “Find and Replace Script”Use this script to help automate common replacements:
# Install dependenciesnpm install -g jscodeshift
# Run find-and-replace (adjust paths as needed)find src -name "*.ts" -o -name "*.tsx" | xargs sed -i '' \ -e 's/LettaClient/Letta/g' \ -e 's/\.modify(/.update(/g' \ -e 's/\.createStream(/.create(/g' \ -e 's/\.messageType/.message_type/g' \ -e 's/\.toolCall/.tool_calls/g' \ -e 's/\.toolCallId/.tool_call_id/g' \ -e 's/\.toolReturn/.tool_return/g' \ -e 's/llmConfig/llm_config/g' \ -e 's/streamTokens/stream_tokens/g' \ -e 's/\.tools\.mcp\.servers/\.mcp_servers/g' \ -e 's/\.resync(/\.refresh(/g'
# Note: MCP server/tool name -> ID migration requires manual intervention# as you need to fetch IDs from the APIimport refrom pathlib import Path
def migrate_file(filepath: Path): """Apply SDK v1.0 migration patterns to a Python file""" content = filepath.read_text()
# Import updates content = re.sub( r'from letta_client import LettaClient', r'from letta_client import Letta', content )
# Method renames content = content.replace('.modify(', '.update(') content = content.replace('.create_stream(', '.stream(')
# MCP namespace changes content = content.replace('.tools.mcp.servers', '.mcp_servers') content = content.replace('.resync(', '.refresh(')
# Already using snake_case in Python, but fix any camelCase content = re.sub(r'messageType', 'message_type', content) content = re.sub(r'toolCall([^_])', r'tool_calls\1', content)
filepath.write_text(content) print(f"✓ Migrated {filepath}")
# Usagefor py_file in Path('src').rglob('*.py'): migrate_file(py_file)Troubleshooting
Section titled “Troubleshooting””Property ‘llmConfig’ does not exist” (TypeScript)
Section titled “”Property ‘llmConfig’ does not exist” (TypeScript)”Cause: Property renamed to llm_config
Fix: Update all references to use snake_case
- agent.llmConfig+ agent.llm_config”Cannot read property ‘toolCallId’ of undefined”
Section titled “”Cannot read property ‘toolCallId’ of undefined””Cause: tool_call changed to tool_calls (array)
Fix: Access the first element of the array
- const id = message.toolCall.toolCallId;+ const toolCalls = message.tool_calls || [];+ const id = toolCalls[0]?.tool_call_id;”items is not iterable”
Section titled “”items is not iterable””Cause: Trying to iterate over page object instead of items array
Fix: Access the .items property first
- for (const message of messages) {+ const messagesPage = await client.agents.messages.list(agentId);+ for (const message of messagesPage.items) {”Cannot find module ‘@letta-ai/letta-client/resources/…’”
Section titled “”Cannot find module ‘@letta-ai/letta-client/resources/…’””Cause: Types moved to subpath exports
Fix: Update imports to use new subpaths
- import { Letta } from "@letta-ai/letta-client";+ import type { Block } from "@letta-ai/letta-client/resources/agents/agents";”Method ‘modify’ does not exist”
Section titled “”Method ‘modify’ does not exist””Cause: Method renamed to update
Fix: Update all modify calls
- await client.agents.modify(agentId, updates)+ await client.agents.update(agentId, updates)”Cannot access property ‘identity_ids’”
Section titled “”Cannot access property ‘identity_ids’””Cause: Field renamed to identities and now returns full objects
Fix: Access the identities array and extract IDs if needed
- const ids = agent.identity_ids;+ const identities = agent.identities;+ const ids = identities.map(i => i.id);”Pagination parameters ‘sort_by’ or ‘ascending’ not recognized”
Section titled “”Pagination parameters ‘sort_by’ or ‘ascending’ not recognized””Cause: Pagination parameters standardized to order_by and order
Fix: Update parameter names
- await client.agents.list({ sort_by: "created_at", ascending: true })+ await client.agents.list({ order_by: "created_at", order: "asc" })”Attach/detach methods return undefined”
Section titled “”Attach/detach methods return undefined””Cause: These methods now return None/void instead of updated state
Fix: Fetch the object separately if you need the updated state
await client.agents.tools.attach(agentId, toolId);const agent = await client.agents.retrieve(agentId); // Get updated state”Cannot find method ‘summarize_agent_conversation’”
Section titled “”Cannot find method ‘summarize_agent_conversation’””Cause: Method moved to messages subresource
Fix: Use the new path
- await client.agents.summarize_agent_conversation(agentId)+ await client.agents.messages.compact(agentId)”Query parameter ‘add_default_initial_messages’ not working”
Section titled “”Query parameter ‘add_default_initial_messages’ not working””Cause: Parameter moved from query to request body
Fix: Pass as request body parameter
- await client.agents.messages.reset(agentId, { params: { add_default_initial_messages: false } })+ await client.agents.messages.reset(agentId, { add_default_initial_messages: false })”Cannot find ‘client.tools.mcp.servers’”
Section titled “”Cannot find ‘client.tools.mcp.servers’””Cause: MCP routes moved to new namespace
Fix: Use new MCP server methods
- await client.tools.mcp.servers.list()+ await client.mcp_servers.list()”MCP server not found by name”
Section titled “”MCP server not found by name””Cause: MCP methods now use server IDs instead of names
Fix: Lookup server ID from name first
// Get server ID from nameconst servers = await client.mcp_servers.list();const myServer = servers.find(s => s.name === "my-server");const serverId = myServer.id;
// Use ID for subsequent operationsawait client.mcp_servers.tools.list(serverId);”MCP tool ‘toolName’ not found”
Section titled “”MCP tool ‘toolName’ not found””Cause: MCP tool execution now uses tool IDs instead of names
Fix: Lookup tool ID from name first
const tools = await client.mcp_servers.tools.list(serverId);const myTool = tools.find(t => t.name === "my-tool");const toolId = myTool.id;
await client.mcp_servers.tools.run(serverId, toolId, { args });”Method ‘execute’ not found on mcp_servers.tools”
Section titled “”Method ‘execute’ not found on mcp_servers.tools””Cause: Method renamed from execute() to run()
Fix: Use the new method name
- await client.mcp_servers.tools.execute(serverId, toolId, { args })+ await client.mcp_servers.tools.run(serverId, toolId, { args })Additional Resources
Section titled “Additional Resources”- Architecture Migration Guide - For migrating agent architectures
- API Reference - Complete SDK documentation
- Changelog - All SDK changes
- GitHub - Source code and issues
- Discord - Get help from the community
Getting Help
Section titled “Getting Help”If you encounter issues during migration:
- Check the Changelog for detailed release notes
- Search GitHub Issues for known problems
- Ask in Discord #dev-help for community support
- Contact [email protected] for enterprise support