Most "AI agent" tutorials stop at calling a single API. But real agents need real tools — DNS lookups, screenshots, crypto prices, code execution, file storage, and more.
In this tutorial, I'll show you how to build a JavaScript AI agent that has access to 40 production tools through a single API key. The whole thing is under 50 lines.
What We're Building
An AI agent that can:
- Take screenshots of any website
- Look up DNS records and IP geolocation
- Check live crypto prices
- Execute code in a sandbox
- Generate QR codes
- Scrape web pages
- ...and 34 more tools
All through one REST API endpoint.
Prerequisites
- Node.js 18+
- An OpenAI API key (or Anthropic — I'll show both)
- A free API key from Agent Gateway (200 free credits, no credit card)
Step 1: Get Your API Key
curl https://agent-gateway-kappa.vercel.app/api/keys/create
Response:
{
"apiKey": "ag_xxxxxxxxxxxx",
"credits": 200,
"rateLimit": "100 requests/hour"
}
Save that API key. Each tool call costs 1 credit.
Step 2: Define Your Tools
The gateway exposes tools at predictable URLs. Here are the ones we'll wire up:
const TOOLS = [
{
name: "take_screenshot",
description: "Take a screenshot of any website. Returns a PNG image URL.",
params: { url: "string (required) - The website URL to screenshot" },
endpoint: "/v1/agent-screenshot/api/screenshot",
method: "GET",
buildQuery: (args) => `?url=${encodeURIComponent(args.url)}`
},
{
name: "dns_lookup",
description: "Look up DNS records for a domain (A, AAAA, MX, TXT, NS, CNAME).",
params: { domain: "string (required)", type: "string (optional, default: A)" },
endpoint: "/api/resolve",
method: "GET",
buildQuery: (args) => `/${args.domain}${args.type ? '/' + args.type : ''}`
},
{
name: "crypto_price",
description: "Get real-time crypto prices. Returns price, 24h change, volume.",
params: { symbol: "string (required) - e.g. BTC, ETH, SOL" },
endpoint: "/v1/defi-trading/prices",
method: "GET",
buildQuery: () => ""
},
{
name: "ip_geolocation",
description: "Get geolocation data for any IP address (country, city, ISP, coordinates).",
params: { ip: "string (required)" },
endpoint: "/api/geo",
method: "GET",
buildQuery: (args) => `/${args.ip}`
},
{
name: "execute_code",
description: "Execute code in a sandboxed environment. Supports JavaScript, Python, Bash.",
params: { language: "string (required)", code: "string (required)" },
endpoint: "/v1/agent-coderunner/api/execute",
method: "POST",
buildBody: (args) => ({ language: args.language, code: args.code })
},
{
name: "scrape_webpage",
description: "Scrape a webpage and return its text content, links, and metadata.",
params: { url: "string (required)" },
endpoint: "/v1/agent-scraper/api/scrape",
method: "GET",
buildQuery: (args) => `?url=${encodeURIComponent(args.url)}`
}
];
You can see the full list of 40 services at the API catalog.
Step 3: The Agent Loop (46 Lines)
Here's the complete agent. It uses OpenAI's function calling to decide which tools to use:
import OpenAI from "openai";
const GATEWAY = "https://agent-gateway-kappa.vercel.app";
const GATEWAY_KEY = process.env.GATEWAY_KEY; // your ag_xxx key
const openai = new OpenAI();
// Convert our tools to OpenAI function format
const functions = TOOLS.map(t => ({
type: "function",
function: {
name: t.name,
description: t.description,
parameters: {
type: "object",
properties: Object.fromEntries(
Object.entries(t.params).map(([k, v]) => [k, { type: "string", description: v }])
),
required: Object.keys(t.params).filter(k => t.params[k].includes("required"))
}
}
}));
async function callTool(name, args) {
const tool = TOOLS.find(t => t.name === name);
const url = GATEWAY + tool.endpoint + (tool.buildQuery?.(args) || "");
const opts = {
method: tool.method,
headers: { "X-API-Key": GATEWAY_KEY, "Content-Type": "application/json" }
};
if (tool.method === "POST") opts.body = JSON.stringify(tool.buildBody(args));
const res = await fetch(url, opts);
return await res.text();
}
async function agent(userMessage) {
const messages = [
{ role: "system", content: "You are a helpful assistant with access to real tools. Use them to answer questions accurately." },
{ role: "user", content: userMessage }
];
while (true) {
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages,
tools: functions
});
const choice = response.choices[0];
messages.push(choice.message);
if (choice.finish_reason === "stop") return choice.message.content;
for (const call of choice.message.tool_calls || []) {
const args = JSON.parse(call.function.arguments);
console.log(` [tool] ${call.function.name}(${JSON.stringify(args)})`);
const result = await callTool(call.function.name, args);
messages.push({ role: "tool", tool_call_id: call.id, content: result });
}
}
}
Step 4: Try It
// Run the agent
const answer = await agent("What's the current price of Bitcoin and Ethereum?");
console.log(answer);
Output:
[tool] crypto_price({})
As of right now:
- **Bitcoin (BTC)**: $94,231.45 (+2.3% in 24h)
- **Ethereum (ETH)**: $3,412.18 (+1.7% in 24h)
Try something more complex:
const answer = await agent(
"Take a screenshot of news.ycombinator.com, then tell me what the top story is about"
);
[tool] take_screenshot({"url":"https://news.ycombinator.com"})
[tool] scrape_webpage({"url":"https://news.ycombinator.com"})
The top story on Hacker News right now is about...
The agent automatically chains tools when it needs multiple sources of information.
Using Anthropic Instead
Swap the OpenAI client for Anthropic's:
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
async function agent(userMessage) {
const messages = [{ role: "user", content: userMessage }];
const tools = TOOLS.map(t => ({
name: t.name,
description: t.description,
input_schema: {
type: "object",
properties: Object.fromEntries(
Object.entries(t.params).map(([k, v]) => [k, { type: "string", description: v }])
),
required: Object.keys(t.params).filter(k => t.params[k].includes("required"))
}
}));
while (true) {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages,
tools
});
if (response.stop_reason === "end_turn") {
return response.content.find(b => b.type === "text")?.text;
}
messages.push({ role: "assistant", content: response.content });
const toolResults = [];
for (const block of response.content.filter(b => b.type === "tool_use")) {
console.log(` [tool] ${block.name}(${JSON.stringify(block.input)})`);
const result = await callTool(block.name, block.input);
toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result });
}
messages.push({ role: "user", content: toolResults });
}
}
All 40 Available Tools
These are all accessible through the same gateway with the same API key:
| Category | Tools |
|---|---|
| Crypto & DeFi | Wallet management, DEX trading, price feeds, on-chain analytics, token deployment |
| Web | Screenshots, web scraping, DNS lookup, URL shortener, domain checker |
| Data | IP geolocation, email validation, web search, data transformation |
| Dev Tools | Code execution (JS/Python/Bash), file storage, PDF generation, QR codes |
| Infrastructure | Webhooks, cron jobs, uptime monitoring, event bus, task queue |
Full API docs with Swagger UI: api-catalog-three.vercel.app/docs
How Much Does It Cost?
200 free credits on signup. Each tool call costs 1 credit. After that, top up with USDC on Base chain or get an API key with higher limits.
For most prototyping and small projects, the free tier is more than enough.
Why This Matters
The AI agent ecosystem is full of "demo" tools — calculators, weather lookups, random number generators. But real-world agents need real-world capabilities.
By pointing your agent at a single gateway with 40+ services, you get:
- One API key instead of managing 10+ service credentials
- Consistent interface across all tools
- No infrastructure to maintain
- Machine-readable discovery via MCP, A2A agent cards, and OpenAPI specs
Full source code and more examples at the API catalog. Questions? Open an issue on GitHub.
Top comments (0)