AI Agents, Skills, MCPs
& Multi-Agent Systems
A complete, practical guide with real working Node.js code using the Gemini API. You'll go from zero to building production-ready AI systems.
š¤ What is an AI Agent?
An AI Agent is an AI program that can think, decide, and act to achieve a goal ā not just answer questions. Unlike a regular chatbot that only talks, an agent can use tools, run code, browse the web, send emails, and take real actions in the world.
Regular Chatbot (No Agent)
You: "What's the weather in Lahore?"
Bot: "I cannot access real-time data."
Stuck. Can't do anything.
AI Agent (With Tools)
You: "What's the weather in Lahore?"
Agent: calls weather API tool
Agent: "It's 28°C and sunny right now!"
Acts. Gets real data.
The 3 Parts of Every Agent
Brain (LLM)
The AI model (like Gemini, GPT-4, Claude) that reads your request, reasons about it, and decides what to do next.
Tools
Functions the agent can call ā like searching Google, running code, reading files, calling APIs, or sending emails.
Memory / Context
The conversation history and any stored information the agent remembers from past steps or past conversations.
š§ How Agents Think ā The ReAct Loop
Agents use a loop called ReAct (Reason + Act). They don't just do one thing ā they keep looping: think ā act ā observe the result ā think again ā act again... until the task is done.
Real Example: "Book me a restaurant in Lahore tonight"
Think
Agent reasons: "I need to find restaurants. I'll use the search tool."
Act
Calls: search_restaurants({city: "Lahore", cuisine: "any", date: "tonight"})
Observe
Gets back: List of 5 restaurants with availability
Think Again
"I have options. I need to book one. I'll use the booking tool."
Act Again
Calls: book_restaurant({id: "xyz", time: "7pm", party: 2})
Done!
Returns: "Booked Haveli Restaurant at 7pm for 2 people. Confirmation: #HR2024"
š§ Tools ā Giving Agents Superpowers
A tool is just a JavaScript function that the agent can call. You describe what it does, and the AI decides when and how to call it. This is the most fundamental concept in agents.
Example: A Calculator Agent
Let's build a simple agent that can do math by giving it calculator tools.
// npm install @google/generative-ai
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// STEP 1: Define your actual JavaScript functions (tools)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const tools = {
add: ({ a, b }) => a + b,
subtract: ({ a, b }) => a - b,
multiply: ({ a, b }) => a * b,
divide: ({ a, b }) => {
if (b === 0) return "Error: Cannot divide by zero";
return a / b;
},
power: ({ base, exponent }) => Math.pow(base, exponent),
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// STEP 2: Describe your tools to the AI (function declarations)
// This is how the AI knows what tools exist and how to use them
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const toolDeclarations = [
{
functionDeclarations: [
{
name: "add",
description: "Add two numbers together",
parameters: {
type: "OBJECT",
properties: {
a: { type: "NUMBER", description: "First number" },
b: { type: "NUMBER", description: "Second number" },
},
required: ["a", "b"],
},
},
{
name: "multiply",
description: "Multiply two numbers",
parameters: {
type: "OBJECT",
properties: {
a: { type: "NUMBER", description: "First number" },
b: { type: "NUMBER", description: "Second number" },
},
required: ["a", "b"],
},
},
{
name: "divide",
description: "Divide number a by number b",
parameters: {
type: "OBJECT",
properties: {
a: { type: "NUMBER", description: "Dividend" },
b: { type: "NUMBER", description: "Divisor" },
},
required: ["a", "b"],
},
},
{
name: "power",
description: "Raise base to the power of exponent",
parameters: {
type: "OBJECT",
properties: {
base: { type: "NUMBER", description: "The base number" },
exponent: { type: "NUMBER", description: "The exponent" },
},
required: ["base", "exponent"],
},
},
],
},
];
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// STEP 3: The Agent Loop
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function runAgent(userMessage) {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: toolDeclarations,
});
const chat = model.startChat();
console.log(`\nš¤ User: ${userMessage}`);
let response = await chat.sendMessage(userMessage);
// The Agent Loop ā keeps running until no more tool calls
while (true) {
const candidate = response.response.candidates[0];
const parts = candidate.content.parts;
const functionCalls = parts.filter(p => p.functionCall);
// If no function calls, we're done ā return the text answer
if (functionCalls.length === 0) {
const text = parts.map(p => p.text || "").join("");
console.log(`\nš¤ Agent: ${text}`);
return text;
}
// Execute each tool call and collect results
const toolResults = [];
for (const part of functionCalls) {
const { name, args } = part.functionCall;
console.log(` š§ Calling tool: ${name}(${JSON.stringify(args)})`);
const toolFn = tools[name];
if (!toolFn) throw new Error(`Unknown tool: ${name}`);
const result = toolFn(args);
console.log(` ā
Result: ${result}`);
toolResults.push({
functionResponse: {
name: name,
response: { result },
},
});
}
// Send results back to the AI so it can continue thinking
response = await chat.sendMessage(toolResults);
}
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Test it!
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function main() {
await runAgent("What is (5 + 3) * 4, and then divide that by 2^3?");
// Agent will call: add(5,3)=8, multiply(8,4)=32, power(2,3)=8, divide(32,8)=4
}
main();
What Just Happened?
The AI saw your complex math question, broke it into steps, called 4 different tools in sequence, and combined the results. You wrote normal JS functions ā the AI figured out when and how to call them.
š Skills ā Teaching Agents What They Know
A skill is a group of related tools + the knowledge/instructions about how to use them. Think of it as a "specialization" you give to an agent. An agent can have many skills.
A Skill Has 3 Parts
System Prompt / Instructions
The "training manual" ā text that tells the agent how to behave, what it knows, what tone to use, rules to follow.
Tools
The functions the agent can call to actually do things ā like reading a database, calling an API, sending a message.
Knowledge
Context about the domain ā FAQs, product details, policies ā often passed in the system prompt or as retrieved documents.
Scope
What the agent should and shouldn't do ā "Only handle billing questions, escalate everything else."
Example: Building a Customer Support Agent with Skills
Let's build a real customer support agent with multiple skills: order tracking, refund processing, and product info.
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// FAKE DATABASE (in production, this hits a real DB)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const db = {
orders: {
"ORD-001": { status: "shipped", item: "iPhone Case", eta: "Feb 28", price: 25 },
"ORD-002": { status: "processing", item: "Laptop Stand", eta: "Mar 2", price: 45 },
"ORD-003": { status: "delivered", item: "USB Hub", eta: "delivered", price: 35 },
},
products: {
"iphone-case": { name: "iPhone Case", price: 25, stock: 150, description: "Premium leather case" },
"laptop-stand": { name: "Laptop Stand", price: 45, stock: 30, description: "Adjustable aluminum stand" },
},
refunds: [],
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// SKILL 1: Order Tracking
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const orderTrackingSkill = {
name: "Order Tracking",
instructions: `
You are an order tracking specialist. When customers ask about their orders,
use the track_order tool to get real-time status. Be empathetic and clear.
If an order is delayed, apologize and offer to help.
Always end with asking if there's anything else you can help with.
`,
tools: {
track_order: ({ order_id }) => {
const order = db.orders[order_id];
if (!order) return { error: `Order ${order_id} not found` };
return order;
},
list_customer_orders: ({ customer_email }) => {
// In real app, query by email
return Object.entries(db.orders).map(([id, o]) => ({ id, ...o }));
},
},
toolDeclarations: [
{
name: "track_order",
description: "Get the current status of an order by its ID",
parameters: {
type: "OBJECT",
properties: {
order_id: { type: "STRING", description: "The order ID like ORD-001" },
},
required: ["order_id"],
},
},
{
name: "list_customer_orders",
description: "List all orders for a customer by their email",
parameters: {
type: "OBJECT",
properties: {
customer_email: { type: "STRING", description: "Customer email address" },
},
required: ["customer_email"],
},
},
],
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// SKILL 2: Refund Processing
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const refundSkill = {
name: "Refund Processing",
instructions: `
You handle refund requests. Rules:
- Only refund delivered orders
- Maximum refund is the original price
- Always verify the order exists before refunding
- Be professional and apologetic for any inconvenience
- Refunds take 3-5 business days
`,
tools: {
process_refund: ({ order_id, reason }) => {
const order = db.orders[order_id];
if (!order) return { success: false, error: "Order not found" };
if (order.status !== "delivered") {
return { success: false, error: "Can only refund delivered orders" };
}
const refundId = `REF-${Date.now()}`;
db.refunds.push({ refundId, order_id, reason, amount: order.price, date: new Date() });
return { success: true, refundId, amount: order.price, eta: "3-5 business days" };
},
},
toolDeclarations: [
{
name: "process_refund",
description: "Process a refund for a delivered order",
parameters: {
type: "OBJECT",
properties: {
order_id: { type: "STRING", description: "The order ID to refund" },
reason: { type: "STRING", description: "Reason for refund" },
},
required: ["order_id", "reason"],
},
},
],
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// SKILL 3: Product Info
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const productSkill = {
name: "Product Information",
instructions: `
You are a product expert. Help customers find the right product.
Always mention price and stock availability.
Suggest related products when relevant.
Be enthusiastic about the products but honest.
`,
tools: {
get_product: ({ product_id }) => {
return db.products[product_id] || { error: "Product not found" };
},
search_products: ({ query }) => {
return Object.entries(db.products)
.filter(([id, p]) => p.name.toLowerCase().includes(query.toLowerCase()))
.map(([id, p]) => ({ id, ...p }));
},
},
toolDeclarations: [
{
name: "get_product",
description: "Get details about a specific product",
parameters: {
type: "OBJECT",
properties: {
product_id: { type: "STRING", description: "Product ID like iphone-case" },
},
required: ["product_id"],
},
},
{
name: "search_products",
description: "Search products by keyword",
parameters: {
type: "OBJECT",
properties: {
query: { type: "STRING", description: "Search query" },
},
required: ["query"],
},
},
],
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AGENT FACTORY: Combine skills into one agent
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
function createAgent(skills) {
// Merge all skills' instructions
const systemInstruction = `
You are a helpful customer support agent for TechStore.
You have the following capabilities:
${skills.map(s => `- ${s.name}: ${s.instructions}`).join('\n')}
Always be polite, empathetic, and efficient.
If a customer's issue is outside your capabilities, say so honestly.
`;
// Merge all skills' tool functions
const allToolFns = skills.reduce((acc, skill) => ({
...acc,
...skill.tools,
}), {});
// Merge all tool declarations
const allDeclarations = [{
functionDeclarations: skills.flatMap(s => s.toolDeclarations),
}];
return { systemInstruction, allToolFns, allDeclarations };
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Run the skilled agent
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function runSkilledAgent(userMessage) {
const { systemInstruction, allToolFns, allDeclarations } = createAgent([
orderTrackingSkill,
refundSkill,
productSkill,
]);
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction,
tools: allDeclarations,
});
const chat = model.startChat();
console.log(`\nš¤ Customer: ${userMessage}`);
let response = await chat.sendMessage(userMessage);
while (true) {
const parts = response.response.candidates[0].content.parts;
const functionCalls = parts.filter(p => p.functionCall);
if (functionCalls.length === 0) {
console.log(`\nš¤ Support Agent: ${parts.map(p => p.text || "").join("")}`);
return;
}
const toolResults = [];
for (const part of functionCalls) {
const { name, args } = part.functionCall;
console.log(` š§ Using skill: ${name}(${JSON.stringify(args)})`);
const result = allToolFns[name](args);
toolResults.push({ functionResponse: { name, response: result } });
}
response = await chat.sendMessage(toolResults);
}
}
// Test all 3 skills!
async function main() {
await runSkilledAgent("Where is my order ORD-001?");
await runSkilledAgent("I want a refund for ORD-003, it was broken");
await runSkilledAgent("Do you have any laptop accessories?");
}
main();
š MCP ā Model Context Protocol
MCP (Model Context Protocol) is a standard way for AI agents to connect to external tools and data sources. Think of it as USB for AI ā just like USB lets any device connect to any computer, MCP lets any AI agent connect to any tool server.
MCP Architecture
MCP Has 3 Things It Can Expose
| Type | What It Is | Example |
|---|---|---|
| Tools | Functions the AI can call | send_email(), query_db() |
| Resources | Data/files the AI can read | company_docs.md, user_data.json |
| Prompts | Pre-built prompt templates | "Summarize this document as..." |
Build Your Own MCP Server from Scratch
Let's build an MCP server that exposes a file system and a "todo" database to any AI that connects to it.
// npm install @modelcontextprotocol/sdk
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} = require("@modelcontextprotocol/sdk/types.js");
const fs = require("fs");
const path = require("path");
// In-memory Todo Database
let todos = [
{ id: 1, text: "Buy groceries", done: false, priority: "high" },
{ id: 2, text: "Write project report", done: false, priority: "medium" },
{ id: 3, text: "Call dentist", done: true, priority: "low" },
];
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Create the MCP Server
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const server = new Server(
{ name: "todo-server", version: "1.0.0" },
{ capabilities: { tools: {}, resources: {} } }
);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// LIST TOOLS: Tell any connected AI what tools are available
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "add_todo",
description: "Add a new todo item to the list",
inputSchema: {
type: "object",
properties: {
text: { type: "string", description: "The todo task text" },
priority: {
type: "string",
enum: ["low", "medium", "high"],
description: "Priority level",
},
},
required: ["text"],
},
},
{
name: "list_todos",
description: "List all todos, optionally filtered by status or priority",
inputSchema: {
type: "object",
properties: {
filter: {
type: "string",
enum: ["all", "done", "pending"],
description: "Filter by completion status",
},
},
},
},
{
name: "complete_todo",
description: "Mark a todo as completed",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "The todo ID to complete" },
},
required: ["id"],
},
},
{
name: "delete_todo",
description: "Delete a todo item",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "The todo ID to delete" },
},
required: ["id"],
},
},
{
name: "read_file",
description: "Read the contents of a file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string", description: "Path to the file" },
},
required: ["filepath"],
},
},
{
name: "write_file",
description: "Write content to a file",
inputSchema: {
type: "object",
properties: {
filepath: { type: "string", description: "Path to write the file" },
content: { type: "string", description: "Content to write" },
},
required: ["filepath", "content"],
},
},
],
}));
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// CALL TOOL: Handle when AI calls a tool
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "add_todo": {
const newTodo = {
id: todos.length + 1,
text: args.text,
done: false,
priority: args.priority || "medium",
};
todos.push(newTodo);
return { content: [{ type: "text", text: `Added todo: ${JSON.stringify(newTodo)}` }] };
}
case "list_todos": {
let filtered = todos;
if (args.filter === "done") filtered = todos.filter(t => t.done);
if (args.filter === "pending") filtered = todos.filter(t => !t.done);
return { content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }] };
}
case "complete_todo": {
const todo = todos.find(t => t.id === args.id);
if (!todo) return { content: [{ type: "text", text: `Todo ${args.id} not found` }] };
todo.done = true;
return { content: [{ type: "text", text: `Completed: "${todo.text}"` }] };
}
case "delete_todo": {
const idx = todos.findIndex(t => t.id === args.id);
if (idx === -1) return { content: [{ type: "text", text: `Todo ${args.id} not found` }] };
const [removed] = todos.splice(idx, 1);
return { content: [{ type: "text", text: `Deleted: "${removed.text}"` }] };
}
case "read_file": {
try {
const content = fs.readFileSync(args.filepath, "utf-8");
return { content: [{ type: "text", text: content }] };
} catch (e) {
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
}
}
case "write_file": {
try {
fs.writeFileSync(args.filepath, args.content, "utf-8");
return { content: [{ type: "text", text: `Written to ${args.filepath}` }] };
} catch (e) {
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
}
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// LIST RESOURCES: Expose data files as readable resources
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
{
uri: "todo://database",
name: "Current Todos",
description: "The complete list of todos in JSON format",
mimeType: "application/json",
},
{
uri: "file://docs/readme.md",
name: "App Documentation",
description: "README file for this application",
mimeType: "text/markdown",
},
],
}));
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
if (uri === "todo://database") {
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(todos, null, 2),
}],
};
}
throw new Error(`Resource not found: ${uri}`);
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Start the server (communicates via stdin/stdout)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Todo MCP Server running on stdio");
}
main();
MCP Client ā Connect Your Agent to the MCP Server
const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
const { StdioClientTransport } = require("@modelcontextprotocol/sdk/client/stdio.js");
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
async function main() {
// Step 1: Connect to the MCP server
const mcpClient = new Client({ name: "gemini-agent", version: "1.0.0" }, {});
const transport = new StdioClientTransport({
command: "node",
args: ["./03_mcp_server.js"],
});
await mcpClient.connect(transport);
// Step 2: Discover available tools from the MCP server
const { tools: mcpTools } = await mcpClient.listTools();
console.log("Available MCP tools:", mcpTools.map(t => t.name));
// Step 3: Convert MCP tool format to Gemini format
const geminiTools = [{
functionDeclarations: mcpTools.map(tool => ({
name: tool.name,
description: tool.description,
parameters: tool.inputSchema,
})),
}];
// Step 4: Create Gemini model with MCP tools
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: geminiTools,
systemInstruction: "You are a helpful assistant that manages todos and files. Use the available tools.",
});
// Step 5: Run agent loop, dispatching tool calls to MCP server
async function chat(message) {
console.log(`\nš¤ User: ${message}`);
const chatSession = model.startChat();
let response = await chatSession.sendMessage(message);
while (true) {
const parts = response.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) {
console.log(`š¤ Agent: ${parts.map(p => p.text || "").join("")}`);
return;
}
const results = [];
for (const p of calls) {
const { name, args } = p.functionCall;
console.log(` š” MCP call: ${name}(${JSON.stringify(args)})`);
// Execute tool ON THE MCP SERVER (not locally!)
const mcpResult = await mcpClient.callTool({ name, arguments: args });
const resultText = mcpResult.content[0].text;
console.log(` ā
MCP result: ${resultText}`);
results.push({ functionResponse: { name, response: { result: resultText } } });
}
response = await chatSession.sendMessage(results);
}
}
await chat("Show me all my pending todos");
await chat("Add a high priority todo: Deploy the new website");
await chat("Mark todo #1 as done and then show all todos");
await mcpClient.close();
}
main();
āļø Build Your First Full Agent
Let's build a real, complete agent ā a Research Assistant that can search the web (simulated), read URLs, summarize content, and save notes to files. This is a production-quality pattern.
const { GoogleGenerativeAI } = require("@google/generative-ai");
const fs = require("fs");
const https = require("https");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// TOOLS for the Research Agent
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Tool 1: Web search (using DuckDuckGo instant answers API)
async function web_search({ query }) {
return new Promise((resolve) => {
const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1`;
https.get(url, (res) => {
let data = "";
res.on("data", d => data += d);
res.on("end", () => {
try {
const json = JSON.parse(data);
const results = {
abstract: json.AbstractText || "No abstract available",
source: json.AbstractSource || "",
topics: (json.RelatedTopics || []).slice(0, 5).map(t => t.Text || "").filter(Boolean),
answer: json.Answer || "",
};
resolve(results);
} catch {
resolve({ error: "Search failed", query });
}
});
}).on("error", () => resolve({ error: "Network error", query }));
});
}
// Tool 2: Save notes to a file
function save_note({ filename, content }) {
const filepath = `./research_notes/${filename}`;
if (!fs.existsSync("./research_notes")) {
fs.mkdirSync("./research_notes");
}
fs.writeFileSync(filepath, content, "utf-8");
return { success: true, filepath, size: content.length };
}
// Tool 3: Read saved notes
function read_note({ filename }) {
const filepath = `./research_notes/${filename}`;
if (!fs.existsSync(filepath)) return { error: `File ${filename} not found` };
return { content: fs.readFileSync(filepath, "utf-8") };
}
// Tool 4: List saved notes
function list_notes() {
if (!fs.existsSync("./research_notes")) return { files: [] };
return { files: fs.readdirSync("./research_notes") };
}
// Tool 5: Get current datetime
function get_datetime() {
return { datetime: new Date().toISOString(), timestamp: Date.now() };
}
const toolMap = { web_search, save_note, read_note, list_notes, get_datetime };
const toolDeclarations = [{
functionDeclarations: [
{
name: "web_search",
description: "Search the web for information on any topic",
parameters: {
type: "OBJECT",
properties: { query: { type: "STRING", description: "Search query" } },
required: ["query"],
},
},
{
name: "save_note",
description: "Save research notes to a file for later reference",
parameters: {
type: "OBJECT",
properties: {
filename: { type: "STRING", description: "Filename like research.md" },
content: { type: "STRING", description: "Content to save" },
},
required: ["filename", "content"],
},
},
{
name: "read_note",
description: "Read a previously saved note file",
parameters: {
type: "OBJECT",
properties: {
filename: { type: "STRING", description: "Filename to read" },
},
required: ["filename"],
},
},
{
name: "list_notes",
description: "List all saved research note files",
parameters: { type: "OBJECT", properties: {} },
},
{
name: "get_datetime",
description: "Get the current date and time",
parameters: { type: "OBJECT", properties: {} },
},
],
}];
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// The Research Agent ā Full implementation
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
class ResearchAgent {
constructor() {
this.model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: toolDeclarations,
systemInstruction: `
You are a research assistant. Your job is to:
1. Search for information on topics the user asks about
2. Synthesize the information clearly
3. Save important findings to notes when asked
4. Be factual, cite sources when available
5. Always search BEFORE answering questions about current events or facts
When saving notes, use descriptive filenames like "ai-trends-2024.md"
`,
});
this.chat = this.model.startChat();
this.stepCount = 0;
}
async run(message) {
console.log(`\n${"=".repeat(60)}`);
console.log(`š¤ User: ${message}`);
console.log(`${"=".repeat(60)}`);
let response = await this.chat.sendMessage(message);
this.stepCount = 0;
while (this.stepCount < 10) { // Safety limit
this.stepCount++;
const parts = response.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) {
const text = parts.map(p => p.text || "").join("");
console.log(`\nš¤ Research Agent:\n${text}`);
return text;
}
const results = [];
for (const p of calls) {
const { name, args } = p.functionCall;
console.log(`\n š Step ${this.stepCount}: ${name}(${JSON.stringify(args)})`);
const fn = toolMap[name];
const result = await Promise.resolve(fn(args));
console.log(` ā
Done`);
results.push({ functionResponse: { name, response: result } });
}
response = await this.chat.sendMessage(results);
}
}
}
async function main() {
const agent = new ResearchAgent();
await agent.run("Search for information about Node.js and save a summary note called 'nodejs-research.md'");
await agent.run("What notes have I saved so far?");
await agent.run("Read my nodejs-research.md file");
}
main();
š Multi-Agent Systems
A multi-agent system is when you have multiple specialized agents working together ā each does what it's best at, and they coordinate to solve complex problems.
Multi-Agent Patterns
| Pattern | How It Works | Use Case |
|---|---|---|
| Orchestrator/Worker | One boss agent delegates tasks to worker agents | Complex research, content pipelines |
| Pipeline | Agents in sequence ā output of one is input to next | Data processing, document transformation |
| Debate | Two agents argue/review each other's work | Code review, fact checking, writing quality |
| Parallel | Multiple agents work simultaneously, results merged | Research from multiple sources |
| Supervisor | One agent monitors and corrects others | Quality assurance, safety checking |
Real Example: Content Creation Pipeline
3 agents working in sequence: Researcher ā Writer ā Editor
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Helper: Simple LLM call (no tools needed for some agents)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function llmCall(systemPrompt, userMessage) {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction: systemPrompt,
});
const result = await model.generateContent(userMessage);
return result.response.text();
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AGENT 1: Researcher ā Gathers key points on a topic
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function researcherAgent(topic) {
console.log(`\nš AGENT 1 (Researcher): Researching "${topic}"...`);
const researchNotes = await llmCall(
`You are a research specialist. Given a topic, produce:
1. Key facts (5-7 bullet points)
2. Recent trends
3. Important statistics
4. Common misconceptions
Be factual and specific. Format as structured notes.`,
`Research topic: ${topic}`
);
console.log(`ā
Researcher done. Notes: ${researchNotes.length} chars`);
return researchNotes;
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AGENT 2: Writer ā Turns research into an article
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function writerAgent(topic, researchNotes, style) {
console.log(`\nāļø AGENT 2 (Writer): Writing ${style} article...`);
const article = await llmCall(
`You are a professional content writer. Given research notes, write a compelling article.
Writing style: ${style}
Requirements:
- Engaging headline
- Strong opening paragraph
- 3-4 body sections with headers
- Clear conclusion
- Natural, flowing prose
Do NOT use bullet points in the final article.`,
`Topic: ${topic}\n\nResearch Notes:\n${researchNotes}`
);
console.log(`ā
Writer done. Article: ${article.length} chars`);
return article;
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AGENT 3: Editor ā Reviews and improves the article
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function editorAgent(article) {
console.log(`\nš AGENT 3 (Editor): Reviewing and improving...`);
const edited = await llmCall(
`You are a senior editor. Review the article and:
1. Fix any grammatical errors
2. Improve clarity and flow
3. Strengthen the opening and closing
4. Ensure consistent tone
5. Add a compelling meta description (1-2 sentences) at the top
Return the improved article only. No editorial commentary.`,
`Review and improve this article:\n\n${article}`
);
console.log(`ā
Editor done.`);
return edited;
}
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// ORCHESTRATOR: Coordinates the pipeline
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
async function contentPipeline({ topic, style = "informative blog post" }) {
console.log(`\nš Starting Content Pipeline`);
console.log(`š Topic: ${topic}`);
console.log(`šØ Style: ${style}`);
const startTime = Date.now();
// STEP 1: Research
const research = await researcherAgent(topic);
// STEP 2: Write (uses research from step 1)
const draft = await writerAgent(topic, research, style);
// STEP 3: Edit (uses draft from step 2)
const finalArticle = await editorAgent(draft);
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(`\nā±ļø Pipeline completed in ${elapsed}s`);
console.log(`\n${"ā".repeat(60)}`);
console.log(`š° FINAL ARTICLE:`);
console.log(`${"ā".repeat(60)}\n`);
console.log(finalArticle);
return { research, draft, finalArticle };
}
contentPipeline({
topic: "The impact of AI agents on software development in 2024",
style: "engaging tech blog post for developers",
});
Advanced: Orchestrator Agent with Dynamic Worker Routing
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// WORKER AGENTS (specialized mini-agents)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const workers = {
code_agent: async (task) => {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction: "You are an expert programmer. Write clean, working code with comments. Always include example usage.",
});
const result = await model.generateContent(task);
return result.response.text();
},
analysis_agent: async (task) => {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction: "You are a data analyst. Analyze information, find patterns, and provide clear insights with numbers.",
});
const result = await model.generateContent(task);
return result.response.text();
},
writing_agent: async (task) => {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction: "You are a professional writer. Write clear, engaging, well-structured content.",
});
const result = await model.generateContent(task);
return result.response.text();
},
math_agent: async (task) => {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
systemInstruction: "You are a mathematician. Solve problems step by step, showing all work clearly.",
});
const result = await model.generateContent(task);
return result.response.text();
},
};
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// ORCHESTRATOR: Analyzes task, delegates to right worker(s)
// Can run workers in parallel for speed!
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
class Orchestrator {
constructor() {
// Orchestrator has ONE special tool: call_worker
this.model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: [{
functionDeclarations: [{
name: "call_worker",
description: "Delegate a subtask to a specialized worker agent",
parameters: {
type: "OBJECT",
properties: {
worker: {
type: "STRING",
enum: ["code_agent", "analysis_agent", "writing_agent", "math_agent"],
description: "Which specialized agent to use",
},
task: {
type: "STRING",
description: "The specific task for this worker",
},
priority: {
type: "STRING",
enum: ["parallel", "sequential"],
description: "Run in parallel with other calls or wait for result first",
},
},
required: ["worker", "task"],
},
}],
}],
systemInstruction: `
You are an orchestrator AI. Break complex user requests into subtasks
and delegate each to the right specialist agent using call_worker.
Available workers:
- code_agent: Writing code, debugging, programming tasks
- analysis_agent: Data analysis, research synthesis, pattern finding
- writing_agent: Articles, emails, documentation, creative writing
- math_agent: Calculations, formulas, statistics
For independent subtasks, call multiple workers (they can run in parallel).
Combine all results into a comprehensive final answer.
`,
});
}
async run(userRequest) {
console.log(`\n${"ā".repeat(60)}`);
console.log(`šÆ Orchestrator received: "${userRequest}"`);
console.log(`${"ā".repeat(60)}`);
const chat = this.model.startChat();
let response = await chat.sendMessage(userRequest);
const workerResults = {};
while (true) {
const parts = response.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) {
const finalAnswer = parts.map(p => p.text || "").join("");
console.log(`\nš FINAL ANSWER:\n${finalAnswer}`);
return finalAnswer;
}
// Run all worker calls in parallel!
console.log(`\nš Dispatching ${calls.length} worker(s) in parallel...`);
const workerPromises = calls.map(async (p) => {
const { worker, task } = p.functionCall.args;
console.log(` š¤ ā ${worker}: "${task.substring(0, 60)}..."`);
const result = await workers[worker](task);
console.log(` ā
${worker} completed`);
return { name: "call_worker", result: { worker, result } };
});
const results = await Promise.all(workerPromises);
const toolResults = results.map(r => ({
functionResponse: { name: r.name, response: r.result },
}));
response = await chat.sendMessage(toolResults);
}
}
}
async function main() {
const orchestrator = new Orchestrator();
// Complex request requiring multiple agents
await orchestrator.run(`
I want to build a Node.js REST API for a todo app. Please:
1. Analyze what endpoints I'll need and why
2. Write the actual Express.js code for the API
3. Write developer documentation for the API
4. Calculate how many requests per second a single Node.js instance can handle
`);
}
main();
šļø Real-World Projects
Project 1: Personal Email Assistant Agent
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// Simulated email inbox (in real app, use nodemailer + IMAP)
const inbox = [
{
id: 1, from: "[email protected]", subject: "Q4 Report Review",
body: "Please review and send feedback on the Q4 report by Friday.",
date: "2024-02-26", read: false, priority: "high"
},
{
id: 2, from: "[email protected]", subject: "Weekly Tech Digest",
body: "This week in tech: AI advances, new frameworks, and more...",
date: "2024-02-25", read: false, priority: "low"
},
{
id: 3, from: "[email protected]", subject: "Project Update Needed",
body: "Hi, we need an update on the project status. What's the current progress?",
date: "2024-02-25", read: false, priority: "high"
},
];
const sentEmails = [];
const emailTools = {
list_emails: ({ filter }) => {
let emails = inbox;
if (filter === "unread") emails = inbox.filter(e => !e.read);
if (filter === "high_priority") emails = inbox.filter(e => e.priority === "high");
return emails.map(({ body, ...e }) => e); // Don't include full body in list
},
read_email: ({ id }) => {
const email = inbox.find(e => e.id === id);
if (!email) return { error: `Email ${id} not found` };
email.read = true;
return email;
},
compose_reply: ({ email_id, context }) => {
const email = inbox.find(e => e.id === email_id);
if (!email) return { error: "Email not found" };
return {
to: email.from,
subject: `Re: ${email.subject}`,
original_email: email.body,
suggestion_context: context,
};
},
send_email: ({ to, subject, body }) => {
const sent = { id: sentEmails.length + 1, to, subject, body, sentAt: new Date().toISOString() };
sentEmails.push(sent);
return { success: true, message: `Email sent to ${to}`, emailId: sent.id };
},
mark_as_read: ({ id }) => {
const email = inbox.find(e => e.id === id);
if (email) email.read = true;
return { success: true };
},
summarize_thread: ({ ids }) => {
const emails = inbox.filter(e => ids.includes(e.id));
return emails;// Agent will summarize these
},
};
const emailToolDeclarations = [{
functionDeclarations: [
{ name: "list_emails", description: "List emails in inbox, with optional filter",
parameters: { type: "OBJECT", properties: {
filter: { type: "STRING", enum: ["all", "unread", "high_priority"] }
}}},
{ name: "read_email", description: "Read the full content of an email",
parameters: { type: "OBJECT", properties: { id: { type: "NUMBER" } }, required: ["id"] }},
{ name: "send_email", description: "Send an email",
parameters: { type: "OBJECT", properties: {
to: { type: "STRING" }, subject: { type: "STRING" }, body: { type: "STRING" }
}, required: ["to", "subject", "body"] }},
{ name: "compose_reply", description: "Get context needed to compose a reply to an email",
parameters: { type: "OBJECT", properties: {
email_id: { type: "NUMBER" },
context: { type: "STRING", description: "What the reply should say" }
}, required: ["email_id", "context"] }},
],
}];
async function emailAgent(command) {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: emailToolDeclarations,
systemInstruction: `
You are a professional email assistant. You can:
- Read and manage emails
- Draft and send replies
- Summarize email threads
- Prioritize important emails
When replying to clients or bosses, be professional and concise.
When sending emails, draft thoughtful responses.
Always confirm before sending important emails.
`,
});
const chat = model.startChat();
console.log(`\nš§ Command: ${command}`);
let resp = await chat.sendMessage(command);
while (true) {
const parts = resp.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) { console.log(`š¤: ${parts.map(p=>p.text||"").join("")}`); return; }
const results = calls.map(p => {
const {name,args} = p.functionCall;
console.log(` š¬ ${name}(${JSON.stringify(args)})`);
return { functionResponse: { name, response: emailTools[name](args) }};
});
resp = await chat.sendMessage(results);
}
}
async function main() {
await emailAgent("Show me my high priority emails and summarize what I need to do");
await emailAgent("Reply to the client email saying we're 70% complete and on track for next week");
}
main();
Project 2: SQL Database Agent
// npm install @google/generative-ai better-sqlite3
const { GoogleGenerativeAI } = require("@google/generative-ai");
const Database = require("better-sqlite3");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// Set up a real SQLite database
const db = new Database(":memory:");
// Create tables and seed data
db.exec(`
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT, department TEXT, salary INTEGER, hire_date TEXT
);
CREATE TABLE sales (
id INTEGER PRIMARY KEY, employee_id INTEGER,
amount REAL, product TEXT, sale_date TEXT,
FOREIGN KEY (employee_id) REFERENCES employees(id)
);
INSERT INTO employees VALUES
(1,'Ali Hassan','Engineering',95000,'2021-03-15'),
(2,'Sara Khan','Marketing',75000,'2022-01-10'),
(3,'Ahmed Raza','Engineering',88000,'2020-06-20'),
(4,'Fatima Ali','Sales',65000,'2023-02-01'),
(5,'Usman Malik','Engineering',102000,'2019-11-05');
INSERT INTO sales VALUES
(1,4,15000,'Enterprise Plan','2024-01-15'),
(2,2,8500,'Pro Plan','2024-01-20'),
(3,4,22000,'Enterprise Plan','2024-02-01'),
(4,2,4200,'Starter Plan','2024-02-10'),
(5,4,18000,'Enterprise Plan','2024-02-20');
`);
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// SQL Tools
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
const sqlTools = {
get_schema: () => {
const tables = db.prepare("SELECT name, sql FROM sqlite_master WHERE type='table'").all();
return tables;
},
run_query: ({ sql }) => {
try {
// Safety: only allow SELECT queries
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
return { error: "Only SELECT queries are allowed" };
}
const results = db.prepare(sql).all();
return { rows: results, count: results.length };
} catch (e) {
return { error: e.message };
}
},
};
const sqlToolDeclarations = [{
functionDeclarations: [
{
name: "get_schema",
description: "Get the database schema (table names and column definitions)",
parameters: { type: "OBJECT", properties: {} },
},
{
name: "run_query",
description: "Execute a SELECT SQL query on the database",
parameters: {
type: "OBJECT",
properties: {
sql: { type: "STRING", description: "The SQL SELECT query to execute" },
},
required: ["sql"],
},
},
],
}];
async function sqlAgent(question) {
const model = genAI.getGenerativeModel({
model: "gemini-2.5-flash",
tools: sqlToolDeclarations,
systemInstruction: `
You are a SQL expert. When users ask business questions:
1. First, check the schema to understand the database structure
2. Write the appropriate SQL query
3. Run the query and interpret the results
4. Explain findings in plain English with numbers
Always show the SQL query you used.
`,
});
const chat = model.startChat();
console.log(`\nā Question: ${question}`);
let resp = await chat.sendMessage(question);
while (true) {
const parts = resp.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) {
console.log(`\nš Answer:\n${parts.map(p=>p.text||"").join("")}`);
return;
}
const results = calls.map(p => {
const {name,args} = p.functionCall;
console.log(` šļø ${name}(${JSON.stringify(args)})`);
return { functionResponse: { name, response: sqlTools[name](args) }};
});
resp = await chat.sendMessage(results);
}
}
async function main() {
await sqlAgent("Who are the top earners in Engineering?");
await sqlAgent("What's the total sales amount per employee? Who's the best salesperson?");
await sqlAgent("What's the average salary by department?");
}
main();
š Patterns & Project Structure
Complete Project Structure
Reusable Base Agent Class
const { GoogleGenerativeAI } = require("@google/generative-ai");
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
/**
* BaseAgent - Reusable agent class you can extend
* Usage:
* class MyAgent extends BaseAgent {
* constructor() {
* super({ name: 'MyAgent', systemPrompt: '...', tools: [...], toolFns: {...} });
* }
* }
*/
class BaseAgent {
constructor({ name, systemPrompt, tools, toolFns, model = "gemini-2.5-flash", maxSteps = 20 }) {
this.name = name;
this.toolFns = toolFns;
this.maxSteps = maxSteps;
this.conversationHistory = [];
this.model = genAI.getGenerativeModel({
model,
systemInstruction: systemPrompt,
tools: tools ? [{ functionDeclarations: tools }] : undefined,
});
this.chat = this.model.startChat({ history: this.conversationHistory });
this.verbose = true;
}
log(msg) {
if (this.verbose) console.log(`[${this.name}] ${msg}`);
}
async run(userMessage, context = {}) {
this.log(`š¤ "${userMessage}"`);
// Inject any extra context into the message
const fullMessage = context ? `${userMessage}\n\nContext: ${JSON.stringify(context)}` : userMessage;
let response = await this.chat.sendMessage(fullMessage);
let steps = 0;
while (steps++ < this.maxSteps) {
const parts = response.response.candidates[0].content.parts;
const calls = parts.filter(p => p.functionCall);
if (!calls.length) {
const text = parts.map(p => p.text || "").join("");
this.log(`š¤ Done in ${steps} steps`);
return { success: true, response: text, steps };
}
const toolResults = [];
for (const p of calls) {
const { name, args } = p.functionCall;
this.log(`š§ ${name}(${JSON.stringify(args)})`);
try {
const fn = this.toolFns[name];
if (!fn) throw new Error(`Unknown tool: ${name}`);
const result = await Promise.resolve(fn(args));
toolResults.push({ functionResponse: { name, response: result } });
} catch (err) {
this.log(`ā Error: ${err.message}`);
toolResults.push({ functionResponse: { name, response: { error: err.message } } });
}
}
response = await this.chat.sendMessage(toolResults);
}
return { success: false, error: "Max steps reached" };
}
// Reset conversation history
reset() {
this.chat = this.model.startChat();
}
}
module.exports = { BaseAgent };
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Example: Create a specialized agent using BaseAgent
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
class WeatherAgent extends BaseAgent {
constructor() {
super({
name: "WeatherAgent",
systemPrompt: "You are a weather assistant. Always check weather before answering.",
tools: [
{
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "OBJECT",
properties: {
city: { type: "STRING", description: "City name" },
},
required: ["city"],
},
},
],
toolFns: {
get_weather: ({ city }) => ({
city,
temp: 28,
condition: "Sunny",
humidity: "65%",
wind: "15 km/h",
}),
},
});
}
}
// Use it:
const weather = new WeatherAgent();
weather.run("What's the weather like in Lahore? Should I bring an umbrella?")
.then(result => console.log(result.response));
Setup Instructions
mkdir my-agent && cd my-agent
npm init -y
npm install @google/generative-ai @modelcontextprotocol/sdk better-sqlite3
export GEMINI_API_KEY=your_key_here
node 01_calculator_agent.js
šÆ Summary ā The Big Picture
| Concept | What It Is | Analogy | When to Use |
|---|---|---|---|
| Agent | AI that thinks + acts using tools | Personal assistant | Any task requiring action, not just answers |
| Tool | A JS function the agent can call | A skill or equipment | Every time agent needs to do something real |
| Skill | Group of related tools + instructions | Job training + tools | Organizing agent capabilities by domain |
| MCP | Standard protocol for AI tool servers | USB for AI | Sharing tools across multiple AI clients |
| Multi-Agent | Multiple specialized agents collaborating | A team of experts | Complex tasks, parallel work, quality checks |
| Orchestrator | Agent that manages other agents | Project manager | Coordinating complex workflows |