Quickstart: Your First Agent in 15 Minutes
A hands-on tutorial that walks you through building a working AI agent. You will create a research agent that can search the web, read pages, and produce a structured report.
What you will build
A research agent that takes a company name, searches the web for information, reads their website, and produces a structured competitive brief. All in about 50 lines of code.
By the end of this tutorial, you will understand:
- How to define an agent with a system prompt
- How to give an agent tools
- How to run the agent and get results
- How the observe-think-act loop works in practice
Prerequisites
- Claude Code installed (or any Claude API access)
- An Anthropic API key (get one at console.anthropic.com)
- Node.js 18+ installed
If you prefer Cursor or Windsurf, the concepts are the same — only the execution environment differs. See our tool comparison for setup instructions specific to each.
Step 1: Set up your project
Create a new directory and initialize:
mkdir my-first-agent && cd my-first-agent
npm init -y
npm install @anthropic-ai/sdk
Set your API key:
export ANTHROPIC_API_KEY="your-key-here"
Step 2: Define the agent prompt
Create a file called agent.ts:
const SYSTEM_PROMPT = `You are a company research agent.
Your job: Given a company name, research it and produce a structured brief.
STEPS:
1. Search the web for the company
2. Read their main website
3. Find their pricing (if public)
4. Check for recent news
OUTPUT FORMAT:
## Company: [Name]
**Website:** [URL]
**What they do:** [1-2 sentences]
**Pricing:** [Summary or "Not public"]
**Recent news:** [Key headlines]
**Strengths:** [Bullet points]
**Weaknesses:** [Bullet points]
RULES:
- Only include facts you can verify from sources
- If you cannot find something, say "Not found" — never fabricate
- Keep the brief under 500 words`;
This prompt is the agent's identity. It defines the goal, the steps, the output format, and the rules. A good prompt is specific, structured, and honest about limitations.
Step 3: Define tools
Tools are what give your agent hands. Without tools, it can only think — it cannot act.
const tools = [
{
name: "search_web",
description: "Search the web for information. Returns top results with titles and snippets.",
input_schema: {
type: "object" as const,
properties: {
query: { type: "string", description: "The search query" },
},
required: ["query"],
},
},
{
name: "read_page",
description: "Read the content of a web page. Returns the text content.",
input_schema: {
type: "object" as const,
properties: {
url: { type: "string", description: "The URL to read" },
},
required: ["url"],
},
},
];
Each tool has:
- A name the LLM uses to call it
- A description so the LLM knows when to use it
- An input schema defining what parameters it accepts
The description matters more than you think. The LLM reads it to decide which tool to use. Write descriptions that explain when to use the tool, not just what it does.
Step 4: Implement tool execution
When the agent decides to use a tool, you need to actually execute it:
async function executeTool(name: string, input: Record<string, string>): Promise<string> {
switch (name) {
case "search_web":
// In production, use a real search API (Tavily, SerpAPI, etc.)
// For this demo, we will simulate it
return `Search results for "${input.query}":\n1. ${input.query} - Official Website\n2. ${input.query} pricing and plans\n3. ${input.query} recent news and updates`;
case "read_page":
// In production, use fetch + cheerio or a headless browser
return `Content from ${input.url}: [Page content would appear here]`;
default:
return `Unknown tool: ${name}`;
}
}
In a real agent, search_web calls a search API and read_page fetches and parses HTML. The agent does not know or care about the implementation — it just sees the results.
Step 5: Run the agent loop
This is the core of every agent — the loop that keeps running until the task is done:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function runAgent(companyName: string) {
console.log(`Researching: ${companyName}\n`);
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: `Research this company: ${companyName}` },
];
// Agent loop — keeps running until the agent stops calling tools
while (true) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
system: SYSTEM_PROMPT,
tools,
messages,
});
// Collect all text and tool calls from the response
const toolCalls = response.content.filter((b) => b.type === "tool_use");
const textBlocks = response.content.filter((b) => b.type === "text");
// Print any text the agent produces
for (const block of textBlocks) {
console.log(block.text);
}
// If no tool calls, the agent is done
if (toolCalls.length === 0) break;
// Add the assistant's response to the conversation
messages.push({ role: "assistant", content: response.content });
// Execute each tool call and add results
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const call of toolCalls) {
console.log(` [Tool] ${call.name}(${JSON.stringify(call.input)})`);
const result = await executeTool(call.name, call.input as Record<string, string>);
toolResults.push({
type: "tool_result",
tool_use_id: call.id,
content: result,
});
}
messages.push({ role: "user", content: toolResults });
}
}
// Run it
runAgent("Vercel").catch(console.error);
Step 6: Run your agent
npx tsx agent.ts
You will see the agent:
- Search the web for "Vercel"
- Read their website
- Look for pricing information
- Compile everything into a structured brief
That is the observe-think-act loop in action. The LLM decided which tools to call, in what order, and when to stop.
What just happened
You built an agent with:
- A system prompt defining its role and rules
- Two tools giving it the ability to search and read
- An execution loop that runs until the task is complete
The LLM handled all the decision-making. You did not write any if/else logic for "search first, then read, then summarize." The model figured that out from the prompt.
Making it production-ready
This demo is simplified. A production agent adds:
| Concern | Solution |
|---|---|
| Real search | Use Tavily, SerpAPI, or Brave Search API |
| Page reading | Use fetch + cheerio for HTML parsing |
| Error handling | Wrap tool calls in try/catch, return errors to the agent |
| Token limits | Track token usage, implement context windowing |
| Rate limiting | Add delays between API calls |
| Memory | Save results to a database for future reference |
| Guardrails | Validate tool inputs, restrict allowed domains |
| Scheduling | Run agents on a cron schedule with an orchestrator |
Each of these is covered in depth in the Production section.
Key takeaways
- An agent is a loop: prompt the LLM, execute tool calls, feed results back, repeat
- The system prompt defines what the agent does; tools define how it acts
- Tool descriptions are critical — the LLM reads them to decide which tool to use
- Start simple (2-3 tools), add complexity only when needed
- The same pattern works regardless of which tool you use (Claude Code, Cursor, Windsurf)