Skip to main content

Letta

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

1 min read · updated

Letta Adapter

@codespar/lettav0.4.0

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.

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.

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

npm install @codespar/sdk @codespar/letta
pnpm add @codespar/sdk @codespar/letta
yarn add @codespar/sdk @codespar/letta

@codespar/letta has a peer dependency on @codespar/sdk@^0.9.0. Make sure it is installed.

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.

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.

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:

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:

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:

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:

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);
  }
}

Letta agents have persistent memory. When a tool call fails, the agent remembers the failure context and can adapt its approach in future interactions.

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.

Next steps

Edit on GitHub

Last updated on