SDK v1.0 migration guide
Migrate from SDK v0.x to v1.0 with breaking changes and migration patterns.
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.types import AgentType, LlmConfig+ from letta_client.types.agents import BlockClient 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)
// 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.retrieve(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.toolCallId+ message.tool_call_id
- message.toolReturn+ message.tool_returns // Note: plural
// tool_calls is now always an array+ message.tool_calls
// 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, {+ before: "msg_123",+ after: "msg_456",+ limit: 50,+ order: "asc"+ });+ 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,+ before="msg_123",+ after="msg_456",+ limit=50,+ order="asc"+ )+ 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(archiveId, { agent_id: agentId });
// Detach archive from agentawait client.agents.archives.detach(archiveId, { agent_id: agentId });
// 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(archive_id, agent_id=agent_id)client.agents.archives.detach(archive_id, agent_id=agent_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(identityId, { agent_id: agentId });
// Detach identity from agentawait client.agents.identities.detach(identityId, { agent_id: agentId });
// Attach identity to blockawait client.blocks.identities.attach(identityId, { block_id: blockId });
// Detach identity from blockawait client.blocks.identities.detach(identityId, { block_id: blockId });
// 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(identity_id, agent_id=agent_id)client.agents.identities.detach(identity_id, agent_id=agent_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”Model-specific settings are configured via the model_settings object:
// Create agent with OpenAI model settingsconst agent = await client.agents.create({ model: "openai/gpt-4", model_settings: { provider_type: "openai", temperature: 0.7, max_output_tokens: 4096, reasoning: { reasoning_effort: "medium" } }, context_window_limit: 128000});
// Create agent with Anthropic model settingsconst agent2 = await client.agents.create({ model: "anthropic/claude-3-5-sonnet-20241022", model_settings: { provider_type: "anthropic", temperature: 0.7, max_output_tokens: 4096, thinking: { type: "enabled", budget_tokens: 1024 } }, context_window_limit: 128000});
// Update agent configurationawait client.agents.update(agentId, { model_settings: { provider_type: "openai", temperature: 0.5 }, context_window_limit: 64000});# Create with OpenAI configurationagent = client.agents.create( model="openai/gpt-4", model_settings={ "provider_type": "openai", "temperature": 0.7, "max_output_tokens": 4096, "reasoning": { "reasoning_effort": "medium" } }, context_window_limit=128000)
# Create with Anthropic configurationagent2 = client.agents.create( model="anthropic/claude-3-5-sonnet-20241022", model_settings={ "provider_type": "anthropic", "temperature": 0.7, "max_output_tokens": 4096, "thinking": { "type": "enabled", "budget_tokens": 1024 } }, context_window_limit=128000)
# Update configurationclient.agents.update( agent_id, model_settings={ "provider_type": "openai", "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.mcpServers.list();
// Create MCP server (automatically syncs tools)+ const server = await client.mcpServers.create(serverConfig);+ // Returns: { id: "mcp_server_123", name: "...", ... }
// Get MCP server by ID+ const server = await client.mcpServers.retrieve(serverId);
// Update MCP server by ID+ await client.mcpServers.update(serverId, updateConfig);
// Delete MCP server by ID+ await client.mcpServers.delete(serverId);
// List tools from a server by ID+ const tools = await client.mcpServers.tools.list(serverId);
// Get specific tool by ID+ const tool = await client.mcpServers.tools.retrieve(serverId, toolId);
// Run/execute tool by ID+ const result = await client.mcpServers.tools.run(serverId, toolId, {+ args: { key: "value" }+ });
// Refresh tools (replaces resync)+ await client.mcpServers.refresh(serverId);
// Connect to server (for OAuth) - now uses server ID+ await client.mcpServers.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.mcpServers(TypeScript) orclient.mcp_servers(Python) - Identification: Use server IDs and tool IDs instead of names
- Old:
serverName(string) → New:serverId(ID string like"mcp_server-12ab34cd") - Old:
toolName(string) → New:toolId(UUID string like"tool-123e4567-e89b-42d3-8456-426614174000")
- 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.mcpServers.list();const myServer = servers.find(s => s.name === "my-server");const serverId = myServer.id; // Get ID from server objectconst tools = await client.mcpServers.tools.list(serverId);const myTool = tools.find(t => t.name === "my-tool");const toolId = myTool.id; // Get ID from tool objectawait client.mcpServers.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.retrieve()
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 )
# Note: Most types are exported from letta_client top-level # e.g., from letta_client import Block, AgentType, etc.
# Method renames content = content.replace('.modify(', '.update(') content = content.replace('.create_stream(', '.create(') # Add streaming=True parameter
# 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””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)”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";”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
TypeScript:
- await client.tools.mcp.servers.list()+ await client.mcpServers.list()Python:
- client.tools.mcp.servers.list()+ 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
TypeScript:
// Get server ID from nameconst servers = await client.mcpServers.list();const myServer = servers.items.find(s => s.name === "my-server");const serverId = myServer.id;
// Use ID for subsequent operationsawait client.mcpServers.tools.list(serverId);Python:
# Get server ID from nameservers = client.mcp_servers.list()my_server = next(s for s in servers.items if s.name == "my-server")server_id = my_server.id
# Use ID for subsequent operationsclient.mcp_servers.tools.list(server_id)“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
TypeScript:
const tools = await client.mcpServers.tools.list(serverId);const myTool = tools.items.find(t => t.name === "my-tool");const toolId = myTool.id;
await client.mcpServers.tools.run(serverId, toolId, { args });Python:
tools = client.mcp_servers.tools.list(server_id)my_tool = next(t for t in tools.items if t.name == "my-tool")tool_id = my_tool.id
client.mcp_servers.tools.run(server_id, tool_id, args=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
TypeScript:
- await client.mcpServers.tools.execute(serverId, toolId, { args })+ await client.mcpServers.tools.run(serverId, toolId, { args })Python:
- client.mcp_servers.tools.execute(server_id, tool_id, args=args)+ client.mcp_servers.tools.run(server_id, tool_id, args=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 support@letta.com for enterprise support