Quick Start: Build Your First Agent in 10 Minutes¶
This tutorial walks you through building a working AI agent with a tool, an HTTP endpoint, and a React chat UI. By the end, you will have a complete full-stack agent application.
Prerequisites
Make sure you have completed the Installation steps and have a valid GOOGLE_AI_STUDIO_KEY environment variable set.
Step 1 -- Install packages¶
Create a new project and install the required dependencies:
mkdir my-first-agent && cd my-first-agent
npm init -y
npm install @modernpath/agent-framework @google/genai yaml zod
npm install -D typescript tsx @types/node
Create a minimal tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"declaration": true,
"experimentalDecorators": true
},
"include": ["src"]
}
Step 2 -- Configure GeminiClient¶
The GeminiClient wraps the @google/genai SDK with retry logic, document validation, and streaming support.
import { GeminiClient } from "@modernpath/agent-framework";
export const gemini = new GeminiClient({
apiKey: process.env.GOOGLE_AI_STUDIO_KEY!, // (1)!
model: "gemini-2.5-flash", // (2)!
maxOutputTokens: 4096,
temperature: 0.7,
});
- The API key is loaded from an environment variable. Never hard-code keys in source.
- See Environment Setup for available models.
Step 3 -- Create a ToolRegistry and register a tool¶
Tools extend what your agent can do. The @Tool decorator attaches metadata (name, description, parameter schema) that the framework uses for validation and LLM function calling.
import { Tool, ITool, ToolMetadata } from "@modernpath/agent-framework";
@Tool({
name: "get_weather",
description: "Get the current weather for a city",
category: "utilities",
parameters: {
city: {
type: "string",
description: "City name, e.g. 'Helsinki'",
required: true,
},
},
})
export class WeatherTool implements ITool {
async execute(params: Record<string, any>): Promise<any> {
const city = params.city as string;
// In production, call a real weather API here
return {
city,
temperature: 22,
condition: "Partly cloudy",
humidity: 65,
};
}
}
Register the tool in a ToolRegistry:
import { ToolRegistry } from "@modernpath/agent-framework";
import { WeatherTool } from "./tools/weather";
export function createRegistry(): ToolRegistry {
const registry = new ToolRegistry();
registry.register("get_weather", new WeatherTool());
return registry;
}
Dynamic tools
You can also register tools without decorators using createTool():
import { createTool, ToolRegistry } from "@modernpath/agent-framework";
const greetTool = createTool(
{ name: "greet", description: "Say hello", parameters: {
name: { type: "string", description: "Name to greet", required: true },
}},
async (params) => `Hello, ${params.name}!`,
);
const registry = new ToolRegistry();
registry.register("greet", greetTool);
Step 4 -- Create an agent¶
For this tutorial, we will build a simple custom agent that uses GeminiClient directly. The framework also ships built-in agents like QAChatAgent for RAG workloads (see Built-in Agents).
import { BaseAgent, AgentContext, ToolRegistry } from "@modernpath/agent-framework";
import { GeminiClient } from "@modernpath/agent-framework";
export class MyFirstAgent extends BaseAgent {
public description = "A simple agent that answers questions and can check the weather.";
constructor(
toolRegistry: ToolRegistry,
private readonly gemini: GeminiClient,
) {
super("MyFirstAgent", "1.0.0", toolRegistry);
}
protected async executeInternal(context: AgentContext): Promise<any> {
// Check if the user is asking about weather
const prompt = context.prompt.toLowerCase();
if (prompt.includes("weather")) {
// Extract city (simple heuristic for demo)
const cityMatch = context.prompt.match(/weather (?:in|for|at) (\w+)/i);
const city = cityMatch?.[1] || "Helsinki";
const weatherData = await this.useTool("get_weather", { city });
const answer = await this.gemini.generateContent(
`The user asked: "${context.prompt}"\n\n` +
`Weather data: ${JSON.stringify(weatherData)}\n\n` +
`Write a helpful response about the weather.`,
{ temperature: 0.5 },
);
return { answer: answer.text, weatherData };
}
// General question -- just use Gemini directly
const answer = await this.gemini.generateContent(context.prompt, {
systemPrompt: "You are a helpful assistant. Be concise and accurate.",
temperature: 0.7,
});
return { answer: answer.text };
}
clone(toolRegistry: ToolRegistry): BaseAgent {
return new MyFirstAgent(toolRegistry, this.gemini);
}
}
Step 5 -- Create an HTTP endpoint¶
The framework provides handler factories that produce platform-agnostic request handlers. The gcpHttp adapter wraps them for GCP Cloud Functions (Express-compatible).
import {
createAgentExecuteHandler,
gcpHttp,
} from "@modernpath/agent-framework";
import { gemini } from "./gemini";
import { createRegistry } from "./registry";
import { MyFirstAgent } from "./agents/my-agent";
const registry = createRegistry();
const agent = new MyFirstAgent(registry, gemini);
// Create the handler
const handler = createAgentExecuteHandler({
resolveAgent: (agentType: string) => {
if (agentType === "my-first-agent") return agent;
throw new Error(`Unknown agentType: ${agentType}`);
},
getUserId: () => 1, // (1)!
});
// Wrap for GCP Cloud Functions / Express
export const agentExecute = gcpHttp(handler);
- In production, extract the user ID from an authentication token in the request headers.
You can test this locally with a lightweight Express server:
import express from "express";
import { agentExecute } from "./server";
const app = express();
app.use(express.json());
// Mount the agent endpoint
app.post("/api/agents/:auditingId/execute", agentExecute as any);
app.listen(3001, () => {
console.log("Agent server running at http://localhost:3001");
});
Test it with curl:
curl -X POST http://localhost:3001/api/agents/1/execute \
-H "Content-Type: application/json" \
-d '{"agentType": "my-first-agent", "prompt": "What is the weather in Helsinki?"}'
Step 6 -- Add a React UI with AgentChat¶
Now let's connect a React frontend. In a separate directory (or workspace), set up a React app:
npm create vite@latest my-agent-ui -- --template react-ts
cd my-agent-ui
npm install @modernpath/agent-ui-react
Wire up the AgentChat component with a backend factory:
import { AgentChat, createSseAgentBackend } from "@modernpath/agent-ui-react";
// For non-streaming, use createHttpAgentBackend instead
const backend = createSseAgentBackend({
baseUrl: "http://localhost:3001",
buildPath: (auditingId) => `/api/agents/${auditingId}/execute/stream`,
});
function App() {
return (
<div style={{ height: "100vh", padding: 24 }}>
<AgentChat
backend={backend}
auditingId={1}
agentType="my-first-agent"
title="My First Agent"
placeholder="Ask me anything..."
stream={true}
/>
</div>
);
}
export default App;
Streaming requires an SSE endpoint
The createSseAgentBackend expects your server to expose a streaming SSE endpoint. Use createAgentExecuteStreamHandler on the backend to provide this. For the simplest setup, use createHttpAgentBackend with stream={false} instead:
import { AgentChat, createHttpAgentBackend } from "@modernpath/agent-ui-react";
const backend = createHttpAgentBackend({
baseUrl: "http://localhost:3001",
buildPath: (auditingId) => `/api/agents/${auditingId}/execute`,
});
<AgentChat
backend={backend}
auditingId={1}
agentType="my-first-agent"
title="My First Agent"
/>
Start the UI:
Open http://localhost:5173 in your browser. Type a question and the agent will respond through the chat interface.
What you have built¶
You now have a working full-stack agent application:
graph LR
Browser["React UI<br/><code>AgentChat</code>"] -- "POST /api/agents/1/execute" --> Server["Express Server<br/><code>gcpHttp(handler)</code>"]
Server --> Agent["MyFirstAgent<br/><code>executeInternal()</code>"]
Agent --> Gemini["GeminiClient<br/><code>gemini-2.5-flash</code>"]
Agent --> Tool["WeatherTool<br/><code>get_weather</code>"] Next steps¶
- Environment Setup -- Configure API keys, models, and feature flags.
- Project Structure -- Organize for production with workspaces.
- Built-in Agents -- Use
QAChatAgentfor RAG,OrchestratorAgentfor multi-step tasks. - Tracing -- Add observability with
setTraceStore(). - Deployment -- Deploy to GCP Cloud Functions, Azure, or Cloud Run.