From 483a97ee77e60e23daa926e785ba5bb63e5c62cb Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Fri, 7 Nov 2025 13:44:07 -0800 Subject: [PATCH] docs: add migration guide [LET-6085] (#6006) * add migration guide * update mcp --- fern/docs.yml | 2 + fern/pages/api/sdk_migration_guide.mdx | 1507 ++++++++++++++++++++++++ 2 files changed, 1509 insertions(+) create mode 100644 fern/pages/api/sdk_migration_guide.mdx diff --git a/fern/docs.yml b/fern/docs.yml index 15357923..53a0b326 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -455,6 +455,8 @@ navigation: contents: - page: API and SDK Overview path: pages/api/about.mdx + - page: SDK v1.0 Migration Guide + path: pages/api/sdk_migration_guide.mdx - changelog: ./changelog title: Changelog slug: changelog diff --git a/fern/pages/api/sdk_migration_guide.mdx b/fern/pages/api/sdk_migration_guide.mdx new file mode 100644 index 00000000..190214b2 --- /dev/null +++ b/fern/pages/api/sdk_migration_guide.mdx @@ -0,0 +1,1507 @@ +--- +title: SDK v1.0 Migration Guide +subtitle: Upgrading from v0.x to v1.0 +slug: api-reference/sdk-migration-guide +--- + + +This guide covers migrating from Letta SDK v0.x (e.g., `1.0.0-alpha.2`) to v1.0 (e.g., `1.0.0-alpha.10`+). For agent architecture migrations, see the [Architecture Migration Guide](/guides/legacy/migration_guide). + + +## Overview + +SDK v1.0 introduces breaking changes to improve consistency and align with modern API design patterns: + +- **Naming convention**: All properties now use `snake_case` instead of `camelCase` +- **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 + +### Package Update + +Update your package dependency: + + +```json package.json +{ + "dependencies": { +- "@letta-ai/letta-client": "1.0.0-alpha.2" ++ "@letta-ai/letta-client": "1.0.0-alpha.10" + } +} +``` +```toml pyproject.toml +[tool.poetry.dependencies] +-letta-client = "1.0.0a2" ++letta-client = "1.0.0a10" +``` + + +### Import Changes + + +```typescript TypeScript +// 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"; +``` +```python Python +# Old +- from letta_client import Letta, LettaClient + +# New ++ from letta import Letta ++ from letta.schemas.agent import Block, CreateBlock, AgentType ++ from letta.schemas.message import LettaMessageUnion, ApprovalCreate ++ from letta.schemas.llm_config import LlmConfig +``` + + +### Client Instantiation + + +```typescript TypeScript +// 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" ++ }); +``` +```python Python +# 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 + +### 1. Pagination + +All list endpoints now use cursor-based pagination with consistent parameters: + + +```typescript TypeScript +// Old - various pagination styles +const messages = await client.agents.messages.list(agentId, { + sort_by: "created_at", + ascending: true +}); + +// New - standardized cursor pagination +const 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; +``` +```python Python +# Old +messages = client.agents.messages.list( + agent_id=agent_id, + sort_by="created_at", + ascending=True +) + +# New +messages_page = client.agents.messages.list( + agent_id=agent_id, + before="msg_123", + after="msg_456", + limit=50, + order="asc" +) +messages = messages_page.items +``` + + +**Affected endpoints:** +- `agents.list()` - renamed `sort_by` → `order_by`, `ascending` → `order` +- `agents.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 + +Many methods were reorganized for better SDK structure: + + +```typescript TypeScript +// 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.summarize(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.export(agentId) + +- await client.agents.import_agent_serialized(file) ++ await client.agents.import(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 +``` +```python Python +# 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.summarize(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(agent_id) + +- client.agents.import_agent_serialized(file) ++ client.agents.import(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 + +Several endpoints and fields are now deprecated: + +**Deprecated endpoints:** +- `client.agents.search()` - use `client.agents.list()` with filters +- `client.messages.search()` - use `client.agents.messages.list()` with filters +- `client.runs.list_active()` - use `client.runs.list(active=True)` +- `client.jobs.list_active()` - use `client.jobs.list(active=True)` +- `client.folders.get_by_name()` - use `client.folders.list(name="...")` +- **MCP routes under `/tools/mcp/servers`** - replaced with new `/mcp-servers` endpoints + - All old MCP methods moved from `client.tools.mcp.servers` to `client.mcp_servers` + - Now use server IDs and tool IDs instead of names +- Sources-related routes - replaced with folders +- Passages routes - replaced with archives +- Legacy agent architecture routes +- All `/count` endpoints + +**Deprecated fields:** +- `agent.memory` - use `agent.blocks` +- `step.messages` - use `client.steps.messages.list(step_id)` +- `agent.identity_ids` - replaced with `agent.identities` (full objects) +- `agent.multi_agent_group` - renamed to `agent.managed_group` +- `use_assistant_message` parameter - no longer needed +- `tool_exec_environment_variables` - renamed to `secrets` + +**Deprecated on agent/block objects:** +- Template-related fields: `is_template`, `base_template_id`, `deployment_id` +- `entity_id`, `preserve_on_migration`, `hidden` +- `name` on blocks (use `label`) + +### 4. Property Names (camelCase → snake_case) + +All API properties now use `snake_case`: + + +```typescript TypeScript +// 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 + +- approvalRequestId: id ++ approval_request_id: id +``` +```python Python +# 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 only +``` + + +### 2. Agent Type Specification + + +```typescript TypeScript +// Old +- agentType: Letta.AgentType.LettaV1Agent + +// New ++ agent_type: "letta_v1_agent" as AgentType +``` +```python Python +# Old +- agent_type=AgentType.LETTA_V1_AGENT + +# New ++ agent_type="letta_v1_agent" +``` + + +### 3. Method Renames + + +```typescript TypeScript +// 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 }) ++ client.agents.messages.stream(agentId, { messages, stream_tokens }) +``` +```python Python +# 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.stream(agent_id, messages=messages) +``` + + +### 4. Message Roles and Stop Reasons + +Enums replaced with string literals: + + +```typescript TypeScript +// 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") +``` +```python Python +# 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 + +Tool calls changed from single object to array: + + +```typescript TypeScript +// 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; ++ } ++ } +``` +```python Python +# 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.name +``` + + +### 6. Pagination + +List methods now return page objects: + + +```typescript TypeScript +// Old +- const messages = await client.agents.messages.list(agentId); + +// New ++ const messagesPage = await client.agents.messages.list(agentId); ++ const messages = messagesPage.items; +``` +```python Python +# Old +- messages = client.agents.messages.list(agent_id=agent_id) + +# New ++ messages_page = client.agents.messages.list(agent_id=agent_id) ++ messages = messages_page.items +``` + + +### 7. Date Handling + + +```typescript TypeScript +// Old +- date: new Date() + +// New ++ date: new Date().toISOString() +``` +```python Python +# Python handles this automatically +from datetime import datetime +date = datetime.now() # Works in both versions +``` + + +### 8. Archive Management (New APIs) + +New endpoints for managing archival memory: + + +```typescript TypeScript +// Create archive +const archive = await client.archives.create({ + name: "my-archive", + description: "Project knowledge base" +}); + +// List archives +const archives = await client.archives.list(); + +// Get archive by ID +const archive = await client.archives.retrieve(archiveId); + +// Update archive +await client.archives.update(archiveId, { name: "updated-name" }); + +// Delete archive +await client.archives.delete(archiveId); + +// Attach archive to agent +await client.agents.archives.attach(agentId, archiveId); + +// Detach archive from agent +await client.agents.archives.detach(agentId, archiveId); + +// List agents using an archive +const agents = await client.archives.agents.list(archiveId); + +// Manage memories in archive +await client.archives.memories.create(archiveId, { text: "Important fact" }); +await client.archives.memories.update(archiveId, memoryId, { text: "Updated fact" }); +await client.archives.memories.delete(archiveId, memoryId); +``` +```python Python +# Create archive +archive = client.archives.create( + name="my-archive", + description="Project knowledge base" +) + +# Attach/detach +client.agents.archives.attach(agent_id, archive_id) +client.agents.archives.detach(agent_id, archive_id) + +# Manage memories +client.archives.memories.create(archive_id, text="Important fact") +``` + + +### 9. Identity and Block Management + +New attach/detach patterns for identities and blocks: + + +```typescript TypeScript +// Attach identity to agent +await client.agents.identities.attach(agentId, identityId); + +// Detach identity from agent +await client.agents.identities.detach(agentId, identityId); + +// Attach identity to block +await client.blocks.identities.attach(blockId, identityId); + +// Detach identity from block +await client.blocks.identities.detach(blockId, identityId); + +// Agent now returns full identity objects +const agent = await client.agents.retrieve(agentId); +// Old: agent.identity_ids = ["id1", "id2"] +// New: agent.identities = [{ id: "id1", name: "Alice", ... }, ...] +``` +```python Python +# Attach/detach identities +client.agents.identities.attach(agent_id, identity_id) +client.agents.identities.detach(agent_id, identity_id) + +# Full identity objects +agent = client.agents.retrieve(agent_id) +for identity in agent.identities: + print(identity.name) +``` + + +### 10. Agent Configuration Updates + +New parameters available for agent creation and updates: + + +```typescript TypeScript +// Temperature, top_p, reasoning_effort now available at top level +const 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 configuration +await client.agents.update(agentId, { + temperature: 0.5, + context_window_limit: 64000 +}); +``` +```python Python +# Create with configuration +agent = 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 configuration +client.agents.update( + agent_id, + temperature=0.5 +) +``` + + +### 11. Message Input Shorthand + +Simplified syntax for sending simple user messages: + + +```typescript TypeScript +// Old - verbose +const response = await client.agents.messages.create(agentId, { + messages: [{ + role: "user", + content: "Hello!" + }] +}); + +// New - shorthand available +const response = await client.agents.messages.create(agentId, { + input: "Hello!" // Automatically creates user message +}); + +// Both forms still supported +``` +```python Python +# Old +response = client.agents.messages.create( + agent_id, + messages=[{"role": "user", "content": "Hello!"}] +) + +# New shorthand +response = client.agents.messages.create( + agent_id, + input="Hello!" +) +``` + + +### 12. Attach/Detach Return Values + +All attach/detach endpoints now return `None` instead of agent/object state: + + +```typescript TypeScript +// Old - returned updated agent +const updatedAgent = await client.agents.tools.attach(agentId, toolId); + +// New - returns void/None +await client.agents.tools.attach(agentId, toolId); +// Fetch agent separately if needed +const agent = await client.agents.retrieve(agentId); +``` +```python Python +# Old +updated_agent = client.agents.tools.attach(agent_id, tool_id) + +# New +client.agents.tools.attach(agent_id, tool_id) +agent = client.agents.retrieve(agent_id) +``` + + +**Affected methods:** +- `agents.tools.attach/detach` +- `agents.blocks.attach/detach` +- `agents.folders.attach/detach` +- `agents.archives.attach/detach` +- `agents.identities.attach/detach` +- `blocks.identities.attach/detach` + +### 13. Agent Import/Export Changes + +Import endpoint now supports name overriding: + + +```typescript TypeScript +// Old - append_copy_suffix parameter +const agent = await client.agents.import(file, { + append_copy_suffix: true // Deprecated +}); + +// New - override_name parameter +const agent = await client.agents.import(file, { + override_name: "my-imported-agent" // Optional, exact name to use +}); +``` +```python Python +# Old +agent = client.agents.import_agent( + file, + append_copy_suffix=True +) + +# New +agent = client.agents.import_agent( + file, + override_name="my-imported-agent" +) +``` + + +### 14. Query Parameter to Request Body Changes + +Several endpoints moved from query parameters to request body: + + +```typescript TypeScript +// 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"] +}); +``` +```python Python +# Tool approval +client.agents.tools.update_approval( + agent_id, + tool_name, + require_approval=True +) + +# Reset messages +client.agents.messages.reset( + agent_id, + add_default_initial_messages=False +) +``` + + +### 15. Tags Endpoint + +Tags list endpoint now uses `name` parameter instead of `query_text`: + + +```typescript TypeScript +// Old +const tags = await client.tags.list({ query_text: "important" }); + +// New +const tags = await client.tags.list({ name: "important" }); +``` +```python Python +# Old +tags = client.tags.list(query_text="important") + +# New +tags = client.tags.list(name="important") +``` + + +### 16. Project ID Handling + +Project ID is now passed in the client constructor or headers: + + +```typescript TypeScript +// Pass in constructor +const 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) +``` +```python Python +# Pass in constructor +client = Letta( + api_key=os.environ["LETTA_API_KEY"], + project_id="proj_123" +) +``` + + +### 17. MCP (Model Context Protocol) Server Management + +MCP routes have been completely restructured with new endpoints under `/mcp-servers`: + + +```typescript TypeScript +// 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); +``` +```python Python +# 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.servers` to `client.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"`) +- **Tool Management**: Tools are now automatically synced when creating a server + - No longer need to manually "add" individual tools + - Use `refresh()` to resync tools (replaces `resync()`) +- **Tool Execution**: Method renamed from `execute()` to `run()` +- **Server Retrieval**: New `retrieve()` method to get individual server by ID +- **Tool Retrieval**: New `retrieve()` method to get individual tool by ID + +**Migration Example:** + + +```typescript TypeScript +// Before: Using names +const 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 IDs +const servers = await client.mcp_servers.list(); +const myServer = servers.find(s => s.name === "my-server"); +const serverId = myServer.id; // Get ID from server object +const 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 object +await client.mcp_servers.tools.run(serverId, toolId, { + args: { query: "hello" } +}); +``` +```python Python +# Before +servers = 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"} +) + +# After +servers = client.mcp_servers.list() +my_server = next(s for s in servers if s.name == "my-server") +server_id = my_server.id +tools = client.mcp_servers.tools.list(server_id) +my_tool = next(t for t in tools if t.name == "my-tool") +tool_id = my_tool.id +client.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/servers` are deprecated and will be removed + +## Migration Examples + +### Complete Agent Creation + + +```typescript TypeScript +// Before +const 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: [], +}); + +// After +const 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: [], +}); +``` +```python Python +# Before +agent = 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=[], +) + +# After +agent = 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 + + +```typescript TypeScript +// Before +const stream = await client.agents.messages.createStream(agentId, { + messages: [{ + role: Letta.MessageCreateRole.User, + content: "Hello" + }], + streamTokens: true, +}); + +// After +const stream = await client.agents.messages.stream(agentId, { + messages: [{ + role: "user", + content: "Hello" + }], + stream_tokens: true, +}); +``` +```python Python +# Before +stream = client.agents.messages.create_stream( + agent_id=agent_id, + messages=[{"role": "user", "content": "Hello"}], + stream_tokens=True, +) + +# After +stream = client.agents.messages.stream( + agent_id=agent_id, + messages=[{"role": "user", "content": "Hello"}], + stream_tokens=True, +) +``` + + +### Handling Approvals + + +```typescript TypeScript +// Before +if (message.messageType === "approval_request_message") { + const toolCall = message.toolCall; + await client.agents.messages.create(agentId, { + messages: [{ + type: "approval", + approvalRequestId: toolCall.toolCallId, + approve: true, + }], + }); +} + +// After +if (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, + }], + }); + } +} +``` +```python Python +# Before +if 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, + }], + ) + +# After +if 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 + + +```typescript TypeScript +// Before +await client.agents.modify(agentId, { + model: "openai/gpt-4", + llmConfig: { temperature: 0.7 } +}); +const agent = await client.agents.retrieve(agentId); +const config = agent.llmConfig; + +// After +await 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; +``` +```python Python +# Before +client.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 + +# After +client.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_config +``` + + +## Migration Checklist + +Use this checklist to ensure a complete migration: + +**Core SDK Changes:** +- [ ] Update package version to `1.0.0-alpha.10` or later +- [ ] Update all imports (client and types) +- [ ] Replace `LettaClient` with `Letta` +- [ ] Update client constructor params: `token` → `apiKey`, `baseUrl` → `baseURL` +- [ ] Add `projectId` to client constructor if using multi-project setup +- [ ] Convert all property names from `camelCase` to `snake_case` +- [ ] Replace enum references with string literals +- [ ] Convert `Date` objects to ISO strings where required +- [ ] Update type annotations to use new import paths + +**Method Renames:** +- [ ] Update `modify()` calls to `update()` +- [ ] Update `createStream()` calls to `stream()` +- [ ] Rename `summarize_agent_conversation()` → `messages.summarize()` +- [ ] Rename `cancel_agent_run()` → `messages.cancel()` +- [ ] Rename `preview_raw_payload()` → `messages.preview()` +- [ ] Rename `list_agent_files()` → `files.list()` +- [ ] Rename `export_agent_serialized()` → `export()` +- [ ] Rename `import_agent_serialized()` → `import()` +- [ ] Rename folder/provider method names (see section 2) +- [ ] Update telemetry routes to use `steps.trace()` + +**Pagination:** +- [ ] Update all list methods to access `.items` property +- [ ] Replace `sort_by` with `order_by` in `agents.list()` +- [ ] Replace `ascending` with `order` parameter +- [ ] Update pagination parameters: `before`, `after`, `limit`, `order` +- [ ] Handle cursor-based pagination for all list endpoints + +**Message Handling:** +- [ ] Handle `tool_calls` as an array instead of single object +- [ ] Update `identity_ids` references to use `identities` (full objects) +- [ ] Replace `agent.memory` with `agent.blocks` +- [ ] Update `step.messages` to use `steps.messages.list()` +- [ ] Consider using new `input` shorthand for simple messages + +**Deprecations:** +- [ ] Remove usage of deprecated search endpoints +- [ ] Replace `list_active()` with `list(active=True)` +- [ ] Remove `use_assistant_message` parameter +- [ ] Replace `tool_exec_environment_variables` with `secrets` +- [ ] 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_name` instead of `append_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.servers` to `client.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 with `refresh()` +- [ ] Replace `execute()` calls with `run()` +- [ ] 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 + +### Find and Replace Script + +Use this script to help automate common replacements: + + +```bash Shell (TypeScript projects) +# Install dependencies +npm 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(/.stream(/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 API +``` +```python Python (migration helper) +import re +from 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 (\w+)', + r'from letta import \1', + 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}") + +# Usage +for py_file in Path('src').rglob('*.py'): + migrate_file(py_file) +``` + + + +**Always review automated changes!** These scripts help with common patterns but cannot handle all edge cases. Test thoroughly after migration. + + +## Troubleshooting + +### "Property 'llmConfig' does not exist" (TypeScript) + +**Cause:** Property renamed to `llm_config` + +**Fix:** Update all references to use snake_case + +```typescript +- agent.llmConfig ++ agent.llm_config +``` + +### "Cannot read property 'toolCallId' of undefined" + +**Cause:** `tool_call` changed to `tool_calls` (array) + +**Fix:** Access the first element of the array + +```typescript +- const id = message.toolCall.toolCallId; ++ const toolCalls = message.tool_calls || []; ++ const id = toolCalls[0]?.tool_call_id; +``` + +### "items is not iterable" + +**Cause:** Trying to iterate over page object instead of items array + +**Fix:** Access the `.items` property first + +```typescript +- 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/...'" + +**Cause:** Types moved to subpath exports + +**Fix:** Update imports to use new subpaths + +```typescript +- import { Letta } from "@letta-ai/letta-client"; ++ import type { Block } from "@letta-ai/letta-client/resources/agents/agents"; +``` + +### "Method 'modify' does not exist" + +**Cause:** Method renamed to `update` + +**Fix:** Update all modify calls + +```typescript +- await client.agents.modify(agentId, updates) ++ await client.agents.update(agentId, updates) +``` + +### "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 + +```typescript +- const ids = agent.identity_ids; ++ const identities = agent.identities; ++ const ids = identities.map(i => i.id); +``` + +### "Pagination parameters 'sort_by' or 'ascending' not recognized" + +**Cause:** Pagination parameters standardized to `order_by` and `order` + +**Fix:** Update parameter names + +```typescript +- 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" + +**Cause:** These methods now return `None`/`void` instead of updated state + +**Fix:** Fetch the object separately if you need the updated state + +```typescript +await client.agents.tools.attach(agentId, toolId); +const agent = await client.agents.retrieve(agentId); // Get updated state +``` + +### "Cannot find method 'summarize_agent_conversation'" + +**Cause:** Method moved to messages subresource + +**Fix:** Use the new path + +```typescript +- await client.agents.summarize_agent_conversation(agentId) ++ await client.agents.messages.summarize(agentId) +``` + +### "Query parameter 'add_default_initial_messages' not working" + +**Cause:** Parameter moved from query to request body + +**Fix:** Pass as request body parameter + +```typescript +- 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'" + +**Cause:** MCP routes moved to new namespace + +**Fix:** Use new MCP server methods + +```typescript +- await client.tools.mcp.servers.list() ++ await client.mcp_servers.list() +``` + +### "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 name +const servers = await client.mcp_servers.list(); +const myServer = servers.find(s => s.name === "my-server"); +const serverId = myServer.id; + +// Use ID for subsequent operations +await client.mcp_servers.tools.list(serverId); +``` + +### "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.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" + +**Cause:** Method renamed from `execute()` to `run()` + +**Fix:** Use the new method name + +```typescript +- await client.mcp_servers.tools.execute(serverId, toolId, { args }) ++ await client.mcp_servers.tools.run(serverId, toolId, { args }) +``` + +## Additional Resources + +- [Architecture Migration Guide](/guides/legacy/migration_guide) - For migrating agent architectures +- [API Reference](/api-reference) - Complete SDK documentation +- [Changelog](/api-reference/changelog) - All SDK changes +- [GitHub](https://github.com/letta-ai/letta) - Source code and issues +- [Discord](https://discord.gg/letta) - Get help from the community + +## Getting Help + +If you encounter issues during migration: + +1. **Check the [Changelog](/api-reference/changelog)** for detailed release notes +2. **Search [GitHub Issues](https://github.com/letta-ai/letta/issues)** for known problems +3. **Ask in [Discord #dev-help](https://discord.gg/letta)** for community support +4. **Contact support@letta.com** for enterprise support