Skip to main content

LangChain

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

1 min read · updated

LangChain Adapter

@codespar/langchainv0.4.0

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).

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.

Framework-specific notes

  • Zod schemas flow throughgetTools(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 constructorcreateToolCallingAgent, 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

npm install @codespar/sdk @codespar/langchain zod
pnpm add @codespar/sdk @codespar/langchain zod
yarn add @codespar/sdk @codespar/langchain zod

@codespar/langchain has peer dependencies on @codespar/sdk@^0.9.0 and zod@>=3.0.0. You also need a LangChain LLM package like @langchain/openai or @langchain/anthropic.

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.

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

getTools is async because it calls session.tools() under the hood. Always await it.

toLangChainTool(tool, session): CodeSparLangChainTool

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

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.

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:

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:

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:

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:

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

When using AgentExecutor, error handling is built in. The executor catches tool errors and feeds them back to the LLM automatically.

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.

Next steps

Edit on GitHub

Last updated on