---
title: Letta
description: Use @codespar/letta to give Letta (MemGPT) agents commerce capabilities in Latin America.
---

import { Callout } from "fumadocs-ui/components/callout";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";

# Letta Adapter

<VersionBadge pkg="@codespar/letta" />

The `@codespar/letta` adapter converts CodeSpar session tools into Letta's (formerly MemGPT) tool format. Each tool includes a `callable` that routes execution through the CodeSpar session for billing and audit. Use it to give your Letta agents with persistent memory access to commerce operations in Latin America.

<Callout type="info">
**Pick this adapter when** your agent needs to remember context across many conversations and days (a long-running account manager, a customer concierge, a returning support agent) — Letta's core-memory and external-memory architecture outlasts session timeouts.
</Callout>

## Framework-specific notes

- **Memory outlasts CodeSpar sessions** — CodeSpar sessions expire after 30 min; Letta's core memory persists indefinitely per agent. Pattern: one Letta agent per end-customer, reopen a CodeSpar session per interaction, let the Letta agent carry the relational context (preferences, past complaints, typical order size).
- **Tool registry is per-agent** — register the CodeSpar tools once at agent creation; they are available on every subsequent message without reconnecting.
- **External-memory for audit** — Letta's archival memory is the right place to persist tool-call summaries for the agent's own reference (separate from CodeSpar's own immutable audit ledger, which lives at the platform level).
- **Concierge pattern** — a single Letta agent handling payments, refunds, and invoicing over months with full recall of the customer's history is the canonical fit.
- **Runtime is its own service** — Letta runs as a separate process / service; plan the deployment (Docker, fly.io, self-hosted) separately from your Next.js / FastAPI app.

## Installation

<Tabs items={["npm", "pnpm", "yarn"]}>
<Tab value="npm">
```bash
npm install @codespar/sdk @codespar/letta
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add @codespar/sdk @codespar/letta
```
</Tab>
<Tab value="yarn">
```bash
yarn add @codespar/sdk @codespar/letta
```
</Tab>
</Tabs>

<Callout type="info">
`@codespar/letta` has a peer dependency on `@codespar/sdk@^0.10.0`. Make sure it is installed.
</Callout>
## API Reference

### `getTools(session): Promise<LettaTool[]>`

Fetches all tools from the session and converts them to Letta tool format. Each tool has `name`, `description`, `parameters` (JSON Schema), and a `callable`.

```typescript
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/letta";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });
const session = await codespar.create("user_123", {
  servers: ["stripe", "mercadopago"],
});

const tools = await getTools(session);
console.log(tools[0].name);       // "codespar_charge"
console.log(tools[0].parameters); // { type: "object", properties: { ... } }
```

### `toLettaTool(tool, session): LettaTool`

Converts a single CodeSpar tool to Letta format with a bound `callable`.

```typescript
import { toLettaTool } from "@codespar/letta";

const allTools = await session.tools();
const paymentTools = allTools
  .filter((t) => t.name.includes("pay"))
  .map((t) => toLettaTool(t, session));
```

### `handleToolCall(session, toolName, args): Promise<ToolResult>`

Convenience executor that routes a tool call through the CodeSpar session.

## Full agent loop

This is a complete example of a Letta agent with persistent memory using CodeSpar tools:

```typescript title="letta-agent.ts"
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/letta";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });

async function run(userMessage: string) {
  // 1. Create a session
  const session = await codespar.create("user_123", {
    servers: ["stripe", "asaas"],
  });

  // 2. Get tools in Letta format
  const tools = await getTools(session);

  // 3. Register tools with your Letta agent
  // const agent = await client.createAgent({
  //   name: "commerce-agent",
  //   tools: tools.map((t) => ({
  //     name: t.name,
  //     description: t.description,
  //     parameters: t.parameters,
  //   })),
  // });

  // 4. Execute tools manually
  const chargeTool = tools.find((t) => t.name === "codespar_charge");
  if (chargeTool) {
    const result = await chargeTool.callable({
      provider: "stripe",
      amount: 25000,
      currency: "BRL",
    });
    console.log("Result:", result);
  }

  // 5. Clean up
  await session.close();
}

await run("Process a Pix payment for R$250");
```

## Handling parallel tool calls

Execute multiple tool callables in parallel:

```typescript
const results = await Promise.all(
  tools
    .filter((t) => ["codespar_charge", "codespar_notify"].includes(t.name))
    .map((t) => t.callable({ amount: 5000, currency: "BRL" }))
);
```

## Streaming

Letta agents support streaming responses. Use the `callable` in your tool execution handler for real-time results:

```typescript title="letta-streaming.ts"
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/letta";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });
const session = await codespar.create("user_123", { servers: ["stripe"] });
const tools = await getTools(session);

// Register as Letta tool executor
async function executeToolCall(name: string, args: Record<string, unknown>) {
  const tool = tools.find((t) => t.name === name);
  if (!tool) return JSON.stringify({ error: `Unknown tool: ${name}` });
  return tool.callable(args);
}
```

## Error handling

Wrap `callable` invocations in try-catch:

```typescript
for (const tool of tools) {
  try {
    const result = await tool.callable(args);
    console.log(`${tool.name}:`, result);
  } catch (error) {
    console.error(`${tool.name} failed:`, error instanceof Error ? error.message : error);
  }
}
```

<Callout type="info">
Letta agents have persistent memory. When a tool call fails, the agent remembers the failure context and can adapt its approach in future interactions.
</Callout>
## Best practices

1. **Always close sessions.** Use `try/finally` to ensure `session.close()` runs.

2. **Scope servers narrowly.** Only connect the MCP servers your agent needs.

3. **Leverage persistent memory.** Letta agents remember past interactions — tool results become part of the agent's memory.

4. **Use descriptive tool names.** Letta's memory system benefits from clear, descriptive tool names for recall.

5. **Return errors as strings.** Let the agent reason about failures and store the context in memory.

6. **Filter tools per task.** Use `session.findTools()` to load only relevant tools for each agent instance.

## Newer SDK wrappers

`getTools(session)` is the agent-facing path. From any tool handler you can also call typed wrappers on the session — same routing, no LLM hop:

- `session.discover(query)` / `session.charge(args)` / `session.pay(args)` / `session.ship(args)` — typed shortcuts for the meta-tools.
- `session.connectionWizard(serverId)` — open a hosted auth flow for a missing connection.
- `session.paymentStatus(toolCallId)` and `session.paymentStatusStream(toolCallId)` — async settlement correlation (poll or SSE).
- `session.verificationStatus(toolCallId)` and `session.verificationStatusStream(toolCallId)` — KYC outcome polling / SSE.

Full reference at [/docs/api/sdk](/docs/api/sdk).

## Next steps

<NextStepsGrid items={[
  { label: "CONCEPT", title: "Sessions", description: "Session lifecycle — Letta's memory outlasts these.", href: "/docs/concepts/sessions" },
  { label: "CONCEPT", title: "Tools & Meta-Tools", description: "Meta-tools and how routing works.", href: "/docs/concepts/tools" },
  { label: "PROVIDER", title: "OpenAI Adapter", description: "Letta uses OpenAI-compatible formats.", href: "/docs/providers/openai" },
  { label: "PROVIDER", title: "AutoGen Adapter", description: "Similar callable pattern for multi-agent flows.", href: "/docs/providers/autogen" },
  { label: "QUICKSTART", title: "Quickstart", description: "End-to-end setup in under 5 minutes.", href: "/docs/quickstart" },
]} />
