Files
letta-server/fern/pages/api/sdk_migration_guide.mdx
jnjpng 3de712fce0 fix: compact and import export file naming in migration guide (#6265)
compact and import export file

Co-authored-by: Letta Bot <noreply@letta.com>
2025-11-24 19:09:54 -08:00

1520 lines
40 KiB
Plaintext

---
title: SDK v1.0 Migration Guide
subtitle: Upgrading from v0.x to v1.0
slug: api-reference/sdk-migration-guide
---
<Info>
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).
</Info>
<Warning>
**Letta Cloud Only (for now)**
This SDK v1.0 migration guide applies **only to Letta Cloud**.
The current self-hosted Letta release (v0.13.x) does **not** support the v1.0 SDK. If you are self-hosting Letta, continue using SDK v0.x for now.
**Coming soon:** We will be releasing a new open-source version of Letta that includes SDK v1.0 support for self-hosted deployments.
To use the v1.0 SDK today, you must connect to Letta Cloud at `https://api.letta.com`.
</Warning>
## 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:
<CodeGroup>
```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"
```
</CodeGroup>
### Import Changes
<CodeGroup>
```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
```
</CodeGroup>
### Client Instantiation
<CodeGroup>
```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"
+ )
```
</CodeGroup>
## Breaking Changes by Category
### 1. Pagination
All list endpoints now use cursor-based pagination with consistent parameters:
<CodeGroup>
```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
```
</CodeGroup>
**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:
<CodeGroup>
```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.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
```
```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.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)
```
</CodeGroup>
### 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`:
<CodeGroup>
```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
```
</CodeGroup>
### 2. Agent Type Specification
<CodeGroup>
```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"
```
</CodeGroup>
### 3. Method Renames
<CodeGroup>
```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)
```
</CodeGroup>
### 4. Message Roles and Stop Reasons
Enums replaced with string literals:
<CodeGroup>
```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":
```
</CodeGroup>
### 5. Tool Calls Structure
Tool calls changed from single object to array:
<CodeGroup>
```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
```
</CodeGroup>
### 6. Pagination
List methods now return page objects:
<CodeGroup>
```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
```
</CodeGroup>
### 7. Date Handling
<CodeGroup>
```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
```
</CodeGroup>
### 8. Archive Management (New APIs)
New endpoints for managing archival memory:
<CodeGroup>
```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")
```
</CodeGroup>
### 9. Identity and Block Management
New attach/detach patterns for identities and blocks:
<CodeGroup>
```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)
```
</CodeGroup>
### 10. Agent Configuration Updates
New parameters available for agent creation and updates:
<CodeGroup>
```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
)
```
</CodeGroup>
### 11. Message Input Shorthand
Simplified syntax for sending simple user messages:
<CodeGroup>
```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!"
)
```
</CodeGroup>
### 12. Attach/Detach Return Values
All attach/detach endpoints now return `None` instead of agent/object state:
<CodeGroup>
```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)
```
</CodeGroup>
**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:
<CodeGroup>
```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.importFile(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_file(
file,
override_name="my-imported-agent"
)
```
</CodeGroup>
### 14. Query Parameter to Request Body Changes
Several endpoints moved from query parameters to request body:
<CodeGroup>
```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
)
```
</CodeGroup>
### 15. Tags Endpoint
Tags list endpoint now uses `name` parameter instead of `query_text`:
<CodeGroup>
```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")
```
</CodeGroup>
### 16. Project ID Handling
Project ID is now passed in the client constructor or headers:
<CodeGroup>
```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"
)
```
</CodeGroup>
### 17. MCP (Model Context Protocol) Server Management
MCP routes have been completely restructured with new endpoints under `/mcp-servers`:
<CodeGroup>
```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)
```
</CodeGroup>
**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:**
<CodeGroup>
```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"}
)
```
</CodeGroup>
**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
<CodeGroup>
```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=[],
)
```
</CodeGroup>
### Streaming Messages
<CodeGroup>
```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,
)
```
</CodeGroup>
### Handling Approvals
<CodeGroup>
```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,
}],
)
```
</CodeGroup>
### Updating Agent Configuration
<CodeGroup>
```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
```
</CodeGroup>
## 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.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 `.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:
<CodeGroup>
```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)
```
</CodeGroup>
<Warning>
**Always review automated changes!** These scripts help with common patterns but cannot handle all edge cases. Test thoroughly after migration.
</Warning>
## 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.compact(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