Skip to main content

Quickstart

Get your first AI agent commerce interaction running in under 5 minutes.

1 min read · updated

Quickstart

@codespar/sdkv0.9.0

This guide walks you through installing the SDK, creating a session, executing a tool call, and wiring it into a full agent loop -- all in under 5 minutes. By the end, you will have a working commerce agent that can process payments in Brazil.

Prerequisites

  • Node.js 18+ installed
  • A CodeSpar API key — mint one at Dashboard → API Keys. New orgs get a test-environment project plus a csk_test_* key auto-created at signup, and the mint modal defaults each new key's prefix to match the active project's environment.
  • An LLM provider account (Anthropic, OpenAI, or any Vercel AI SDK provider)

On Python? See Quickstart (Python) for the same flow with pip install codespar — sync and async clients are published on PyPI.

Install the SDK and an adapter

Install the core SDK and the adapter for your preferred framework:

npm install @codespar/sdk @codespar/claude @anthropic-ai/sdk
npm install @codespar/sdk @codespar/openai openai
npm install @codespar/sdk @codespar/vercel ai @ai-sdk/anthropic

Using pnpm or yarn? Replace npm install with pnpm add or yarn add. All packages are published on the public npm registry.

Set your API keys

You need two environment variables -- one for CodeSpar, one for your LLM provider:

export CODESPAR_API_KEY="csk_test_your_key_here"
export ANTHROPIC_API_KEY="sk-ant-your_key_here"
export CODESPAR_API_KEY="csk_test_your_key_here"
export OPENAI_API_KEY="sk-your_key_here"
export CODESPAR_API_KEY="csk_test_your_key_here"
export ANTHROPIC_API_KEY="sk-ant-your_key_here"

A csk_test_* key authorizes against a test-environment project. Tool calls route to the catalog's base_url_test provider sandbox where one is defined, and to the real production endpoint otherwise. For deterministic responses without any upstream call, declare per-session fixtures via cs.create({ mocks: {...} }) — see Test Mode for the full reference.

Run a session against an inline mock

The fastest way to confirm the SDK is wired correctly — and the pattern most tests use — is to declare a mock on session create. The runtime substitutes the fixture for the upstream call; policy, audit, and commerce-memory still fire on every invocation.

test-mode.ts
// Requires @codespar/sdk@0.10.0+
import { CodeSpar } from "@codespar/sdk";

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

const session = await cs.create("user_test", {
  servers: ["asaas"],
  mocks: {
    "asaas/create_payment": { id: "pay_test_42", status: "PENDING" },
  },
});

const result = await session.execute("asaas/create_payment", { value: 100 });
console.log(result.id); // "pay_test_42"

A non-empty mocks field puts the session into strict mode for its lifetime: any tool call whose canonical name is not declared returns a tool_not_mocked envelope rather than falling through to the real upstream. See Test Mode for the strict-mode contract, the five tool_result envelopes, and how to assert on them in TypeScript and Python.

Create a session

A session is a scoped connection to one or more MCP servers. It manages authentication, tool routing, and usage tracking. Think of it as a database connection pool -- you create one, use it for the duration of your interaction, and close it when done.

index.ts
import { CodeSpar } from "@codespar/sdk";

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

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

console.log("Session ID:", session.id);
console.log("Status:", session.status);
console.log("Servers:", session.servers);
Output
{
  "id": "ses_a1b2c3d4e5f6",
  "status": "active",
  "servers": ["stripe", "mercadopago"],
  "created_at": "2026-04-15T14:30:00Z",
  "expires_at": "2026-04-15T15:30:00Z"
}

Sessions expire after 30 minutes of inactivity. For long-running agents, implement session renewal or create a new session per interaction. See Sessions for lifecycle details.

List available tools

Once your session is active, retrieve the tools available from the connected servers. The session.tools() method is async and returns an array of tool definitions:

index.ts
const tools = await session.tools();

console.log(`${tools.length} tools available:`);
tools.forEach((t) => {
  console.log(`  - ${t.name}: ${t.description}`);
});
Output
[
  {
    "name": "codespar_discover",
    "description": "Find available commerce tools by domain",
    "input_schema": {
      "type": "object",
      "properties": {
        "domain": {
          "type": "string",
          "enum": ["payments", "fiscal", "logistics", "messaging", "banking", "erp", "crypto"]
        }
      },
      "required": ["domain"]
    }
  },
  {
    "name": "codespar_checkout",
    "description": "Create a checkout session for a product or service",
    "input_schema": { "..." : "..." }
  },
  {
    "name": "codespar_pay",
    "description": "Process a payment via Pix, boleto, or card",
    "input_schema": { "..." : "..." }
  }
]

The tools returned depend on which servers you connected in the session. Connecting ["stripe", "mercadopago"] gives you payment tools. Add "correios" to also get shipping tools.

Execute a tool call

Call a tool directly against the session to verify everything is wired correctly:

index.ts
const result = await session.execute("codespar_discover", {
  domain: "payments",
});

console.log(JSON.stringify(result, null, 2));
Response
{
  "domain": "payments",
  "servers": ["stripe", "mercadopago"],
  "tools": [
    {
      "name": "codespar_checkout",
      "description": "Create a checkout session for a product or service"
    },
    {
      "name": "codespar_pay",
      "description": "Process a payment via Pix, boleto, or card"
    }
  ],
  "capabilities": ["pix", "boleto", "credit_card", "checkout_link", "recurring"]
}

Now try creating a checkout link:

index.ts
const checkout = await session.execute("codespar_checkout", {
  provider: "stripe",
  amount: 4990,
  currency: "BRL",
  description: "Pro Plan - Monthly",
  payment_methods: ["pix", "card"],
});

console.log(JSON.stringify(checkout, null, 2));
Response
{
  "checkout_id": "chk_7f8g9h0i1j2k",
  "url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3...",
  "amount": 4990,
  "currency": "BRL",
  "description": "Pro Plan - Monthly",
  "payment_methods": ["pix", "card"],
  "status": "open",
  "expires_at": "2026-04-16T14:30:00Z"
}

Wire it into an agent

Now connect everything to a real LLM. Choose your framework:

agent-claude.ts
import Anthropic from "@anthropic-ai/sdk";
import { CodeSpar } from "@codespar/sdk";
import { getTools, handleToolUse, toToolResultBlock } from "@codespar/claude";

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

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

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

  // 3. Start the conversation
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMessage },
  ];

  let response = await anthropic.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    system:
      "You are a commerce assistant for a Brazilian e-commerce store. " +
      "Use the available tools to help with payments, invoicing, and shipping. " +
      "Always confirm amounts and details before processing payments.",
    tools,
    messages,
  });

  // 4. Tool-use loop: keep going until Claude stops calling tools
  while (response.stop_reason === "tool_use") {
    const toolUseBlocks = response.content.filter(
      (b) => b.type === "tool_use"
    );

    const toolResults = await Promise.all(
      toolUseBlocks.map(async (block) => {
        try {
          const result = await handleToolUse(session, block);
          return toToolResultBlock(block.id, result);
        } catch (error) {
          return toToolResultBlock(block.id, {
            error: error instanceof Error ? error.message : "Tool call failed",
          });
        }
      })
    );

    messages.push({ role: "assistant", content: response.content });
    messages.push({ role: "user", content: toolResults });

    response = await anthropic.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 4096,
      tools,
      messages,
    });
  }

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

  // 6. Return the final text
  const text = response.content.find((b) => b.type === "text");
  return text?.type === "text" ? text.text : "";
}

// Run it
const reply = await run("Create a R$49.90 checkout link for 'Pro Plan' using Stripe");
console.log(reply);

See the full Claude adapter guide for streaming, error handling, and advanced patterns.

agent-openai.ts
import OpenAI from "openai";
import { CodeSpar } from "@codespar/sdk";
import { getTools, handleToolCall } from "@codespar/openai";

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

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

  const tools = await getTools(session);

  const messages: OpenAI.ChatCompletionMessageParam[] = [
    {
      role: "system",
      content:
        "You are a commerce assistant for a Brazilian store. " +
        "Use the available tools for payments, invoicing, and shipping.",
    },
    { role: "user", content: userMessage },
  ];

  let response = await openai.chat.completions.create({
    model: "gpt-4o",
    tools,
    messages,
  });

  let message = response.choices[0].message;

  while (message.tool_calls && message.tool_calls.length > 0) {
    messages.push(message);

    for (const toolCall of message.tool_calls) {
      let content: string;
      try {
        content = await handleToolCall(session, toolCall);
      } catch (error) {
        content = JSON.stringify({
          error: error instanceof Error ? error.message : "Tool call failed",
        });
      }
      messages.push({
        role: "tool",
        tool_call_id: toolCall.id,
        content,
      });
    }

    response = await openai.chat.completions.create({
      model: "gpt-4o",
      tools,
      messages,
    });
    message = response.choices[0].message;
  }

  await session.close();
  return message.content ?? "";
}

const reply = await run("Generate a Pix QR code for R$250");
console.log(reply);

See the full OpenAI adapter guide for streaming and advanced patterns.

agent-vercel.ts
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { CodeSpar } from "@codespar/sdk";
import { getTools } from "@codespar/vercel";

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

const { text } = await generateText({
  model: anthropic("claude-sonnet-4-20250514"),
  tools,
  maxSteps: 5,
  system:
    "You are a commerce assistant for a Brazilian e-commerce store. " +
    "Use the available tools for payments, invoicing, and shipping.",
  prompt: "Create a R$49.90 checkout link for 'Pro Plan' via Stripe",
});

console.log(text);
await session.close();

See the full Vercel AI SDK guide for streamText, Next.js API routes, and the useChat hook.

The Complete Loop in action

Here is what happens when you run the Claude example above. The agent performs the Complete Loop automatically:

  1. Claude receives the prompt and sees the available tools from getTools.
  2. Claude calls codespar_discover (optional) to understand available payment providers.
  3. Claude calls codespar_checkout with the amount, currency, and provider.
  4. CodeSpar routes the request to the Stripe MCP server, which creates a real checkout session.
  5. Claude receives the result (checkout URL, status, expiry) and formats a response for the user.
User: "Create a R$49.90 checkout link for 'Pro Plan' using Stripe"

Claude thinks: I need to create a checkout link. I have the codespar_checkout tool.

Claude calls: codespar_checkout({
  provider: "stripe",
  amount: 4990,
  currency: "BRL",
  description: "Pro Plan",
  payment_methods: ["pix", "card"]
})

CodeSpar returns: {
  checkout_id: "chk_7f8g9h0i1j2k",
  url: "https://checkout.stripe.com/c/pay/cs_live_...",
  status: "open"
}

Claude responds: "I've created your checkout link for the Pro Plan at R$49.90.
Here it is: https://checkout.stripe.com/c/pay/cs_live_...
The link accepts both Pix and card payments and expires in 24 hours."

Two places to explore without leaving the loop

These are different surfaces — pick the one that matches what you're doing:

  • In-dashboard sandbox at /dashboard/sandbox — a hosted chat interface where you can pick tools and watch them dispatch, without writing any code. Use it to scan what a server exposes before wiring it into your agent.
  • SDK-driven test mode — the mocks field on cs.create shown above. Use it inside your test suite to assert deterministic responses for the tools your agent actually calls. See Test Mode for the canonical reference.

Common errors

ErrorCauseFix
INVALID_API_KEYMissing or malformed API keyCheck that CODESPAR_API_KEY is set and starts with csk_test_ (test-environment project) or csk_live_ (live-environment project)
SESSION_EXPIREDSession timed out after 30 minutes of inactivityCreate a new session
SERVER_NOT_FOUNDRequested a server that does not existCheck available servers with codespar_discover
TOOL_NOT_FOUNDCalled a tool not available in the current sessionVerify the tool exists with session.tools()
RATE_LIMITEDToo many requests in a short periodImplement exponential backoff or reduce request frequency

Next steps

Edit on GitHub

Last updated on