Developer Guides

Deploy Claude-Powered AI Agents on Vercel Edge: Serverless Guide

Deploy ultra-low latency AI agents powered by Claude on Vercel Edge Functions. This serverless guide shows you how to build, tool-enable, and scale globally distributed agents.

J

Jennifer Yu

Workflow Automation Specialist

December 18, 2025 min read
Share:

Why Vercel Edge for Claude-Powered AI Agents?

Vercel Edge Functions combine the speed of edge computing with serverless simplicity, making them ideal for AI agents that demand low-latency responses. By integrating the Claude API—Anthropic's powerhouse models like Claude 3.5 Sonnet—you can create intelligent agents that process user queries, invoke tools, and respond in milliseconds from the nearest edge location.

Key benefits:

  • Global distribution: Responses served from 30+ edge locations, reducing latency to <50ms for cold starts.
  • Scalability: Auto-scales to millions of requests without infrastructure management.
  • Claude synergy: Claude's tool-use capabilities shine in stateless Edge Functions, enabling ReAct-style agents (Reason + Act).
  • Cost-effective: Pay-per-invocation, with Edge Functions at $0.60/million invocations.

This tutorial builds a customer support agent that answers queries, calculates refunds (tool), and fetches mock order status—deployable in minutes.

Prerequisites

Before starting:

  • Node.js 20+ installed.
  • Vercel account (CLI optional, but recommended).
  • Anthropic API key (free tier: 100k input tokens/day).
  • Basic TypeScript knowledge.
  • Git for version control.

Step 1: Project Setup

Create a Next.js app optimized for Vercel Edge:

git clone https://github.com/vercel/next.js/examples/edge-runtime-api-route.git claude-agent-vercel
cd claude-agent-vercel
npm install

Or from scratch:

npx create-next-app@latest claude-agent-vercel --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd claude-agent-vercel
npm install

Add Anthropic SDK for easier integration (optional, but we use fetch for Edge compatibility):

npm install @anthropic-ai/sdk

Note: @anthropic-ai/sdk works in Edge Runtime via Web APIs. Create .env.local:

ANTHROPIC_API_KEY=your-key-here

Step 2: Define Agent Tools

Claude excels at tool use with JSON schemas. Our agent uses two tools:

  • calculate_refund: Computes refunds based on order amount and reason.
  • get_order_status: Fetches mock order details.

These simulate real-world actions like database queries or external APIs.

Create src/lib/tools.ts:

export const tools = [
  {
    name: 'calculate_refund',
    description: 'Calculate refund amount for an order based on percentage and original amount. Use for customer refund requests.',
    input_schema: {
      type: 'object',
      properties: {
        order_amount: { type: 'number', description: 'Original order amount in USD' },
        refund_percentage: { type: 'number', description: 'Refund percentage (0-100)' },
      },
      required: ['order_amount', 'refund_percentage'],
    },
  },
  {
    name: 'get_order_status',
    description: 'Get the current status and details of a customer order.',
    input_schema: {
      type: 'object',
      properties: {
        order_id: { type: 'string', description: 'Unique order ID' },
      },
      required: ['order_id'],
    },
  },
] as const;

export async function executeTool(
  toolCall: any,
): Promise<{ tool_use_id: string; content: string }> {
  const { name, input } = toolCall;

  switch (name) {
    case 'calculate_refund': {
      const { order_amount, refund_percentage } = input;
      const refund = (order_amount * refund_percentage) / 100;
      return {
        tool_use_id: toolCall.id,
        content: JSON.stringify({ refund_amount: refund.toFixed(2) }),
      };
    }
    case 'get_order_status': {
      const { order_id } = input;
      // Mock DB lookup
      const mockOrders: Record<string, any> = {
        'ORD-123': { status: 'shipped', items: ['Widget A'], total: 99.99 },
        'ORD-456': { status: 'delivered', items: ['Widget B'], total: 49.99 },
      };
      const order = mockOrders[order_id] || { status: 'not_found' };
      return {
        tool_use_id: toolCall.id,
        content: JSON.stringify(order),
      };
    }
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
}

Step 3: Build the Edge Function

Create src/app/api/agent/route.ts:

import { NextRequest, NextResponse } from 'next/server';
import { tools, executeTool } from '@/lib/tools';

export const runtime = 'edge';

export async function POST(req: NextRequest) {
  try {
    const { messages } = await req.json();
    if (!messages || !Array.isArray(messages)) {
      return NextResponse.json({ error: 'Invalid messages' }, { status: 400 });
    }

    const apiKey = process.env.ANTHROPIC_API_KEY;
    if (!apiKey) {
      return NextResponse.json({ error: 'API key missing' }, { status: 500 });
    }

    const system = 'You are a helpful customer support agent. Use tools to check orders or calculate refunds accurately. Respond conversationally.';

    let currentMessages = [
      { role: 'system' as const, content: system },
      ...messages,
    ];

    const maxIterations = 5;
    let iterations = 0;

    while (iterations < maxIterations) {
      const response = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': apiKey,
          'anthropic-version': '2023-06-01',
          'content-type': 'application/json',
        },
        body: JSON.stringify({
          model: 'claude-3-5-sonnet-20241022',
          max_tokens: 1024,
          temperature: 0.7,
          system: system,
          messages: currentMessages,
          tools,
          tool_choice: 'auto',
        }),
      });

      if (!response.ok) {
        throw new Error(`Claude API error: ${response.statusText}`);
      }

      const data = await response.json();
      const assistantMessage = data.content[data.content.length - 1];

      currentMessages.push({ role: 'assistant' as const, content: assistantMessage });

      // Check for tool uses
      const toolUses = assistantMessage.type === 'tool_use' 
        ? [assistantMessage]
        : assistantMessage.content?.filter((c: any) => c.type === 'tool_use') || [];

      if (toolUses.length === 0) {
        // No more tools, return final response
        return NextResponse.json({
          response: assistantMessage.text || '',
          usage: data.usage,
        });
      }

      // Execute tools and append results
      const toolResults = [];
      for (const toolUse of toolUses) {
        const result = await executeTool(toolUse);
        toolResults.push({ type: 'tool_result' as const, tool_use_id: toolUse.id, content: result.content });
      }

      currentMessages.push({
        role: 'user' as const,
        content: toolResults,
      });

      iterations++;
    }

    return NextResponse.json({ error: 'Max iterations reached' }, { status: 500 });
  } catch (error) {
    console.error(error);
    return NextResponse.json({ error: 'Internal error' }, { status: 500 });
  }
}

Key notes:

  • ReAct loop: Handles up to 5 tool rounds to prevent infinite loops.
  • Edge-safe: Uses fetch (no Node modules), async execution.
  • Error handling: Graceful failures with logging.
  • Rate limits: Claude: 50 RPM (Sonnet), Vercel: 1000 invocations/min.

Step 4: Add a Frontend Chat Interface

Update src/app/page.tsx for testing:

import { useState } from 'react';

export default function Home() {
  const [messages, setMessages] = useState<{ role: string; content: string }[]>([]);
  const [input, setInput] = useState('');
  const [loading, setLoading] = useState(false);

  const sendMessage = async () => {
    if (!input.trim()) return;
    const userMsg = { role: 'user', content: input };
    setMessages((prev) => [...prev, userMsg]);
    setLoading(true);
    setInput('');

    const res = await fetch('/api/agent', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ messages: [userMsg] }),
    });
    const data = await res.json();
    setMessages((prev) => [...prev, { role: 'assistant', content: data.response }]);
    setLoading(false);
  };

  return (
    <div className="max-w-2xl mx-auto p-8">
      <h1 className="text-3xl font-bold mb-8">Claude Agent on Vercel Edge</h1>
      <div className="space-y-4 mb-8 h-96 overflow-y-auto border p-4 rounded-lg">
        {messages.map((msg, i) => (
          <div key={i} className={`p-2 ${msg.role === 'user' ? 'text-right' : 'text-left'}`}>
            <span className={`inline-block p-2 rounded ${msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>
              {msg.content}
            </span>
          </div>
        ))}
        {loading && <div>Loading...</div>}
      </div>
      <div className="flex gap-2">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          className="flex-1 p-2 border rounded"
          placeholder="Ask about order ORD-123 or request a 20% refund on $100..."
        />
        <button onClick={sendMessage} disabled={loading} className="px-4 py-2 bg-black text-white rounded">
          Send
        </button>
      </div>
    </div>
  );
}

Test locally: npm run dev. Try: "What's the status of order ORD-123? Calculate 20% refund on $100."

Step 5: Deploy to Vercel

npm install -g vercel
vercel env add ANTHROPIC_API_KEY
vercel --prod

Your agent lives at https://your-project.vercel.app/api/agent or full app at root.

Optimization and Best Practices

  • Streaming: Modify to stream: true, parse SSE for real-time responses (Claude supports).

    // Add to body: stream: true
    // Use ReadableStream for NextResponse
    
  • Caching: Use Vercel KV or Edge Config for agent state.

  • Monitoring: Integrate Vercel Analytics, Log Drains; track Claude usage.

  • Costs: Claude ~$3/million input tokens; Vercel Edge free tier generous.

  • Limits: Edge CPU ~100ms (fine for API calls); Claude context 200k tokens.

  • Advanced: Multi-agent (orchestrator), RAG with Pinecone, auth with NextAuth.

  • SEO/Production: Add CORS, rate limiting (upstash/ratelimit).

Troubleshooting

IssueSolution
fetch failsCheck API key env propagation (vercel env pull).
Tool loop hangsReduce maxIterations.
High latencyUse claude-3-haiku for speed.
Edge incompatibilityAvoid Node APIs; use Web Crypto if needed.

Conclusion

You've deployed a production-ready Claude agent on Vercel Edge—scalable, low-latency, and tool-enabled. Extend with real DBs (Vercel Postgres), more tools, or agents in n8n/Zapier. Fork the GitHub repo and share your builds!

Word count: ~1450. Questions? Comment below.

The #1 Newsletter in AI

Stay ahead of the AI curve

The most important updates, news, and content — delivered in one weekly newsletter.

No spam. Unsubscribe anytime. Privacy policy

Claude API
AI Agents
Vercel
Serverless
Edge Computing
ai-agents
J

About Jennifer Yu

Workflow Automation Specialist

Jennifer covers workflow strategy, no-code platforms, and clear implementation guidance for teams adopting automation.

Comments (0)