---
title: LangChain
description: Use @codespar/langchain to give LangChain.js agents commerce capabilities in Latin America.
---

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

# LangChain Adapter

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

The `@codespar/langchain` adapter converts CodeSpar session tools into LangChain-compatible tool objects with Zod schemas and execution methods. It works with any LangChain.js agent — `createToolCallingAgent`, `createReactAgent`, or custom loops — using any LLM provider (`@langchain/openai`, `@langchain/anthropic`, `@langchain/google-genai`).

<Callout type="info">
**Pick this adapter when** you already have LangChain chains or retrievers you want to compose with commerce tools (RAG over order histories, document-aware agents), you use LangSmith for tracing, or you want the freedom to swap LLM backends without rewriting the agent loop.
</Callout>

## Framework-specific notes

- **Zod schemas flow through** — `getTools(session)` returns `DynamicStructuredTool` instances with the meta-tool `input_schema` converted to Zod. LangChain validates inputs at call time; bad args surface as `ToolException` before hitting the wire.
- **Works with any agent constructor** — `createToolCallingAgent`, `createReactAgent`, or your own `AgentExecutor` loop. The tools are framework-standard, not LangChain-CodeSpar specific.
- **LLM backend is independent** — `@langchain/openai`, `@langchain/anthropic`, `@langchain/google-genai` all work. Swap the model, the tool wiring does not change.
- **LangSmith tracing captures CodeSpar calls** — each tool invocation shows up as a span with input / output / duration. Combined with CodeSpar's own audit ledger, you get traces at two layers: the agent's decision path and the commerce execution path.
- **RAG + commerce fits naturally** — retrieve the customer's past orders with a LangChain retriever, hand them to the agent as context, then call `codespar_pay` / `codespar_refund` on the resolved entity.

## Installation

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

<Callout type="info">
`@codespar/langchain` has peer dependencies on `@codespar/sdk@^0.10.0` and `zod@>=3.0.0`. You also need a LangChain LLM package like `@langchain/openai` or `@langchain/anthropic`.
</Callout>
## API Reference

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

Fetches all tools from the session and converts them to LangChain-compatible tool objects. Each tool has a `name`, `description`, Zod `schema`, and an `invoke` method that routes through the CodeSpar session.

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

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].schema);     // ZodObject
```

<Callout type="warn">
`getTools` is async because it calls `session.tools()` under the hood. Always `await` it.
</Callout>
### `toLangChainTool(tool, session): CodeSparLangChainTool`

Converts a single CodeSpar tool to LangChain format. Use this when you want to filter or transform tools individually.

```typescript
import { toLangChainTool } from "@codespar/langchain";

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

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

Convenience executor that routes a tool call through the CodeSpar session. Returns the raw `ToolResult` object.

```typescript
import { handleToolCall } from "@codespar/langchain";

const result = await handleToolCall(session, "codespar_charge", {
  provider: "stripe",
  amount: 4990,
  currency: "BRL",
});
```

### `jsonSchemaToZod(schema): z.ZodObject`

Utility that converts a JSON Schema object to a Zod object schema. Handles `string`, `number`, `integer`, `boolean`, `array`, and `object` types, plus `required` fields.

## Full agent loop

This is a complete example using LangChain's tool-calling agent with OpenAI:

```typescript title="langchain-agent.ts"
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/langchain";

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

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

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

  // 3. Create the LLM and prompt
  const llm = new ChatOpenAI({ model: "gpt-4o" });

  const prompt = ChatPromptTemplate.fromMessages([
    [
      "system",
      "You are a commerce assistant for a Brazilian e-commerce store. " +
        "Use the available tools to handle payments, invoicing, and shipping. " +
        "Respond in the same language the user writes in.",
    ],
    ["human", "{input}"],
    ["placeholder", "{agent_scratchpad}"],
  ]);

  // 4. Create and run the agent
  const agent = await createToolCallingAgent({ llm, tools, prompt });
  const executor = new AgentExecutor({ agent, tools, maxIterations: 10 });

  const result = await executor.invoke({ input: userMessage });

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

  return result.output;
}

const reply = await run("Generate a boleto for R$250 due in 7 days");
console.log(reply);
```

## Handling parallel tool calls

LangChain's `AgentExecutor` handles parallel tool calls automatically when the LLM returns multiple tool invocations. If you're building a custom loop, use `Promise.all`:

```typescript
const toolCalls = response.tool_calls ?? [];

const results = await Promise.all(
  toolCalls.map(async (tc) => {
    const tool = tools.find((t) => t.name === tc.name);
    if (!tool) throw new Error(`Unknown tool: ${tc.name}`);
    return tool.invoke(tc.args);
  })
);
```

## Streaming

LangChain supports streaming via `.stream()` on the agent executor:

```typescript title="langchain-streaming.ts"
import { ChatOpenAI } from "@langchain/openai";
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/langchain";

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

async function runStreaming(userMessage: string) {
  const session = await codespar.create("user_123", {
    servers: ["stripe", "mercadopago"],
  });

  const tools = await getTools(session);
  const llm = new ChatOpenAI({ model: "gpt-4o", streaming: true });

  const prompt = ChatPromptTemplate.fromMessages([
    ["system", "You are a commerce assistant for a Brazilian store."],
    ["human", "{input}"],
    ["placeholder", "{agent_scratchpad}"],
  ]);

  const agent = await createToolCallingAgent({ llm, tools, prompt });
  const executor = new AgentExecutor({ agent, tools });

  const stream = await executor.stream({ input: userMessage });

  for await (const event of stream) {
    if (event.output) {
      process.stdout.write(event.output);
    }
  }

  await session.close();
}

await runStreaming("Create a Pix payment for R$150");
```

## Error handling

Wrap tool invocations in try-catch and return errors as structured data so the LLM can reason about failures:

```typescript
for (const tc of toolCalls) {
  try {
    const result = await handleToolCall(session, tc.name, tc.args);
    // Feed result back to the agent
  } catch (error) {
    const errorResult = JSON.stringify({
      error: error instanceof Error ? error.message : "Tool call failed",
      tool_name: tc.name,
    });
    // Feed error back to the agent as tool output
  }
}
```

<Callout type="info">
When using `AgentExecutor`, error handling is built in. The executor catches tool errors and feeds them back to the LLM automatically.
</Callout>
## Best practices

1. **Always close sessions.** Use `try/finally` to ensure `session.close()` runs even if the agent throws an exception.

2. **Scope servers narrowly.** Only connect the MCP servers your agent actually needs. Fewer servers means fewer tools, which improves tool selection accuracy.

3. **Set `maxIterations`.** Pass `maxIterations: 10` to `AgentExecutor` to prevent infinite tool-call loops.

4. **Use a descriptive system prompt.** Tell the LLM what domain it operates in and what tools to prefer.

5. **Pick the right LLM.** GPT-4o and Claude have the best tool-calling accuracy. Smaller models may struggle with complex tool schemas.

6. **Filter tools when possible.** If your agent only needs payment tools, filter with `session.findTools("payments")` before converting.

## Newer SDK wrappers

The adapter wraps `session.tools()` for LangChain's tool-calling agent. For higher-level flows you can 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 and configuration.", 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: "Direct OpenAI SDK integration.", href: "/docs/providers/openai" },
  { label: "PROVIDER", title: "Vercel AI SDK", description: "Framework-agnostic with automatic tool execution.", href: "/docs/providers/vercel" },
  { label: "QUICKSTART", title: "Quickstart", description: "End-to-end setup in under 5 minutes.", href: "/docs/quickstart" },
]} />
