MCP Servers Explained: The New Standard Revolutionizing AI Agent Development
Aadarsh- •
- 08 MIN TO READ

Building Your First AI Agent with OpenAI's Agents SDK: A Step-by-Step Guide
The release of OpenAI's Agents SDK has fundamentally transformed how developers can build advanced AI systems. This powerful toolkit enables the creation of autonomous agents that can reason about complex problems, develop execution plans, and interact with external systems—all while maintaining context across multi-step workflows. In this comprehensive guide, we'll walk through the process of building your first AI agent using OpenAI's Agents SDK, from basic setup to advanced deployment.
The OpenAI Agents SDK provides a structured framework for building AI agents that can accomplish complex tasks through reasoning and tool use. Before diving into implementation, it's important to understand the core components:
This architecture enables agents to move beyond simple question-answering to become genuine assistants that can perform meaningful work in the real world.
Let's start by setting up a development environment for building agents:
1# Create a new project directory
2mkdir my-first-openai-agent
3cd my-first-openai-agent
4
5# Initialize a new Node.js project
6npm init -y
7
8# Install the OpenAI Agents SDK and dependencies
9npm install @openai/agents-sdk @openai/openai-api dotenv express
10
11# Create configuration files
12touch .env
13touch agent.js
14touch server.js
15
Configure your environment variables in the .env file:
1OPENAI_API_KEY=your_openai_api_key
2AGENT_PORT=3000
3
Let's start with a simple agent that can perform basic reasoning and answer questions:
1// agent.js
2require('dotenv').config();
3const { OpenAI } = require('@openai/openai-api');
4const { Agent, ToolRegistry } = require('@openai/agents-sdk');
5
6// Initialize OpenAI client
7const openai = new OpenAI({
8 apiKey: process.env.OPENAI_API_KEY
9});
10
11// Create a new tool registry
12const toolRegistry = new ToolRegistry();
13
14// Create and configure the agent
15const createAgent = async () => {
16 const agent = new Agent({
17 name: "ResearchAssistant",
18 description: "I help users find information and answer questions.",
19 model: "gpt-4o",
20 tools: toolRegistry,
21 openai: openai
22 });
23
24 // Initialize the agent
25 await agent.init();
26
27 return agent;
28};
29
30module.exports = { createAgent };
31
Now, let's create a simple server to interact with our agent:
1// server.js
2const express = require('express');
3const { createAgent } = require('./agent');
4
5const app = express();
6app.use(express.json());
7
8let agent;
9
10// Initialize the agent
11(async () => {
12 agent = await createAgent();
13 console.log("Agent initialized successfully!");
14})();
15
16// Endpoint to interact with the agent
17app.post('/chat', async (req, res) => {
18 try {
19 const { message, sessionId } = req.body;
20
21 if (!message) {
22 return res.status(400).json({ error: "Message is required" });
23 }
24
25 const response = await agent.chat({
26 message,
27 sessionId: sessionId || 'default-session'
28 });
29
30 res.json({ response });
31 } catch (error) {
32 console.error("Error:", error);
33 res.status(500).json({ error: "An error occurred while processing your request" });
34 }
35});
36
37const PORT = process.env.AGENT_PORT || 3000;
38app.listen(PORT, () => {
39 console.log(`Agent server running on port ${PORT}`);
40});
41
This gives us a basic agent accessible via an API endpoint, but it doesn't yet have special capabilities beyond conversation.
The real power of the Agents SDK comes from giving your agent the ability to use tools. Let's add some useful tools to our agent:
1// tools.js
2const axios = require('axios');
3const { Tool, Parameter, ParameterType } = require('@openai/agents-sdk');
4
5// Web Search Tool
6const webSearchTool = new Tool({
7 name: 'web_search',
8 description: 'Search the web for current information',
9 parameters: [
10 new Parameter({
11 name: 'query',
12 type: ParameterType.STRING,
13 description: 'The search query',
14 required: true
15 })
16 ],
17 execute: async ({ query }) => {
18 try {
19 // This would typically connect to a real search API
20 const response = await axios.get('https://api.searchprovider.com/search', {
21 params: { q: query, api_key: process.env.SEARCH_API_KEY }
22 });
23
24 return response.data.results.map(result => ({
25 title: result.title,
26 url: result.url,
27 snippet: result.snippet
28 }));
29 } catch (error) {
30 return { error: 'Failed to perform web search', details: error.message };
31 }
32 }
33});
34
35// Weather Information Tool
36const weatherTool = new Tool({
37 name: 'get_weather',
38 description: 'Get current weather information for a location',
39 parameters: [
40 new Parameter({
41 name: 'location',
42 type: ParameterType.STRING,
43 description: 'The city and country, e.g., "New York, USA"',
44 required: true
45 })
46 ],
47 execute: async ({ location }) => {
48 try {
49 const response = await axios.get('https://api.weatherprovider.com/current', {
50 params: { location, api_key: process.env.WEATHER_API_KEY }
51 });
52
53 return {
54 temperature: response.data.temperature,
55 conditions: response.data.conditions,
56 humidity: response.data.humidity,
57 wind: response.data.wind
58 };
59 } catch (error) {
60 return { error: 'Failed to retrieve weather information', details: error.message };
61 }
62 }
63});
64
65module.exports = { webSearchTool, weatherTool };
66
Now, update your agent.js file to register these tools:
1// agent.js (updated)
2require('dotenv').config();
3const { OpenAI } = require('@openai/openai-api');
4const { Agent, ToolRegistry } = require('@openai/agents-sdk');
5const { webSearchTool, weatherTool } = require('./tools');
6
7// Initialize OpenAI client
8const openai = new OpenAI({
9 apiKey: process.env.OPENAI_API_KEY
10});
11
12// Create a new tool registry and register tools
13const toolRegistry = new ToolRegistry();
14toolRegistry.register(webSearchTool);
15toolRegistry.register(weatherTool);
16
17// Create and configure the agent
18const createAgent = async () => {
19 const agent = new Agent({
20 name: "ResearchAssistant",
21 description: "I help users find information, answer questions, and check weather conditions.",
22 model: "gpt-4o",
23 tools: toolRegistry,
24 openai: openai
25 });
26
27 // Initialize the agent
28 await agent.init();
29
30 return agent;
31};
32
33module.exports = { createAgent };
34
To build truly useful agents, we need to implement proper memory and state management:
1// memory.js
2class AgentMemory {
3 constructor() {
4 this.sessions = new Map();
5 }
6
7 // Initialize a new session
8 createSession(sessionId) {
9 if (!this.sessions.has(sessionId)) {
10 this.sessions.set(sessionId, {
11 conversationHistory: [],
12 userData: {},
13 taskState: {}
14 });
15 }
16 return this.getSession(sessionId);
17 }
18
19 // Get an existing session
20 getSession(sessionId) {
21 if (!this.sessions.has(sessionId)) {
22 return this.createSession(sessionId);
23 }
24 return this.sessions.get(sessionId);
25 }
26
27 // Add a message to the conversation history
28 addMessage(sessionId, role, content) {
29 const session = this.getSession(sessionId);
30 session.conversationHistory.push({ role, content, timestamp: new Date() });
31
32 // Trim history if needed to manage context length
33 if (session.conversationHistory.length > 20) {
34 session.conversationHistory = session.conversationHistory.slice(-20);
35 }
36 }
37
38 // Store user-specific data
39 setUserData(sessionId, key, value) {
40 const session = this.getSession(sessionId);
41 session.userData[key] = value;
42 }
43
44 // Store task state for multi-step operations
45 setTaskState(sessionId, key, value) {
46 const session = this.getSession(sessionId);
47 session.taskState[key] = value;
48 }
49
50 // Get full context for agent
51 getContext(sessionId) {
52 const session = this.getSession(sessionId);
53 return {
54 conversationHistory: session.conversationHistory,
55 userData: session.userData,
56 taskState: session.taskState
57 };
58 }
59}
60
61module.exports = new AgentMemory();
62
Now, update your agent to use this memory system:
1// agent.js (with memory)
2const memory = require('./memory');
3
4// Inside the agent's chat method
5const response = await agent.chat({
6 message,
7 sessionId,
8 context: memory.getContext(sessionId)
9});
10
11// Save the interaction
12memory.addMessage(sessionId, 'user', message);
13memory.addMessage(sessionId, 'assistant', response.content);
14
15// Update any task state if needed
16if (response.taskState) {
17 Object.entries(response.taskState).forEach(([key, value]) => {
18 memory.setTaskState(sessionId, key, value);
19 });
20}
21
One of the most powerful capabilities of the Agents SDK is enabling agents to plan and execute multi-step tasks. Let's implement this functionality:
1// planning.js
2const { PlanningModule } = require('@openai/agents-sdk');
3
4const createPlanningModule = (agent) => {
5 return new PlanningModule({
6 agent: agent,
7 planningStrategy: 'recursive', // can be 'sequential', 'recursive', or 'adaptive'
8 maxPlanningSteps: 5,
9 maxExecutionSteps: 10,
10 planningPrompt: `
11 You are an AI assistant that creates plans to solve user tasks efficiently.
12 Based on the user's request, create a detailed plan with specific steps.
13 Each step should be concrete and actionable.
14 If a step requires using a tool, specify which tool to use and what parameters to provide.
15 `
16 });
17};
18
19module.exports = { createPlanningModule };
20
Update your agent to use planning:
1// agent.js (with planning)
2const { createPlanningModule } = require('./planning');
3
4// Inside createAgent function
5const agent = new Agent({
6 // ... existing configuration
7});
8
9await agent.init();
10
11// Add planning capability
12const planningModule = createPlanningModule(agent);
13agent.usePlanning(planningModule);
14
15return agent;
16
Now let's put everything together to build a practical research assistant agent that can:
First, let's add more specialized tools:
1// research-tools.js
2const fs = require('fs').promises;
3const path = require('path');
4const { Tool, Parameter, ParameterType } = require('@openai/agents-sdk');
5
6// Tool to save research findings
7const saveResearchTool = new Tool({
8 name: 'save_research_finding',
9 description: 'Save an important research finding to the research database',
10 parameters: [
11 new Parameter({
12 name: 'topic',
13 type: ParameterType.STRING,
14 description: 'The research topic',
15 required: true
16 }),
17 new Parameter({
18 name: 'finding',
19 type: ParameterType.STRING,
20 description: 'The research finding to save',
21 required: true
22 }),
23 new Parameter({
24 name: 'source',
25 type: ParameterType.STRING,
26 description: 'The source of the information (URL, book, etc.)',
27 required: true
28 })
29 ],
30 execute: async ({ topic, finding, source }) => {
31 try {
32 // Ensure research directory exists
33 const researchDir = path.join(__dirname, 'research');
34 await fs.mkdir(researchDir, { recursive: true });
35
36 // Create or append to topic file
37 const topicFile = path.join(researchDir, `${topic.replace(/[^a-z0-9]/gi, '_')}.json`);
38
39 let topicData = [];
40 try {
41 const existingData = await fs.readFile(topicFile, 'utf8');
42 topicData = JSON.parse(existingData);
43 } catch (err) {
44 // File doesn't exist yet, will create new one
45 }
46
47 // Add new finding
48 topicData.push({
49 finding,
50 source,
51 timestamp: new Date().toISOString()
52 });
53
54 // Save updated data
55 await fs.writeFile(topicFile, JSON.stringify(topicData, null, 2));
56
57 return { success: true, message: `Finding saved to topic "${topic}"` };
58 } catch (error) {
59 return { error: 'Failed to save research finding', details: error.message };
60 }
61 }
62});
63
64// Tool to generate a research report
65const generateReportTool = new Tool({
66 name: 'generate_research_report',
67 description: 'Generate a formatted research report from saved findings on a topic',
68 parameters: [
69 new Parameter({
70 name: 'topic',
71 type: ParameterType.STRING,
72 description: 'The research topic to generate a report for',
73 required: true
74 }),
75 new Parameter({
76 name: 'format',
77 type: ParameterType.STRING,
78 description: 'The desired format of the report (markdown, text, html)',
79 required: true
80 })
81 ],
82 execute: async ({ topic, format }) => {
83 try {
84 // Read findings for the topic
85 const topicFile = path.join(__dirname, 'research', `${topic.replace(/[^a-z0-9]/gi, '_')}.json`);
86 const topicData = JSON.parse(await fs.readFile(topicFile, 'utf8'));
87
88 // Generate the report
89 let report = '';
90
91 if (format === 'markdown') {
92 report = `# Research Report: ${topic}\n\n`;
93 report += `*Generated on ${new Date().toLocaleDateString()}*\n\n`;
94 report += `## Key Findings\n\n`;
95
96 topicData.forEach((item, index) => {
97 report += `### Finding ${index + 1}\n\n`;
98 report += `${item.finding}\n\n`;
99 report += `**Source:** ${item.source}\n\n`;
100 });
101 } else if (format === 'html') {
102 // HTML format implementation
103 report = `<h1>Research Report: ${topic}</h1>`;
104 // Add more HTML formatting
105 } else {
106 // Plain text format
107 report = `RESEARCH REPORT: ${topic}\n\n`;
108 // Add more text formatting
109 }
110
111 // Save the report
112 const reportFile = path.join(__dirname, 'research', `${topic.replace(/[^a-z0-9]/gi, '_')}_report.${format === 'html' ? 'html' : 'md'}`);
113 await fs.writeFile(reportFile, report);
114
115 return {
116 success: true,
117 message: `Report generated for topic "${topic}"`,
118 reportPath: reportFile,
119 reportPreview: report.substring(0, 500) + (report.length > 500 ? '...' : '')
120 };
121 } catch (error) {
122 return { error: 'Failed to generate research report', details: error.message };
123 }
124 }
125});
126
127module.exports = { saveResearchTool, generateReportTool };
128
Now, let's update our agent to use these research-specific tools:
1// research-agent.js
2require('dotenv').config();
3const { OpenAI } = require('@openai/openai-api');
4const { Agent, ToolRegistry } = require('@openai/agents-sdk');
5const { webSearchTool, weatherTool } = require('./tools');
6const { saveResearchTool, generateReportTool } = require('./research-tools');
7const memory = require('./memory');
8const { createPlanningModule } = require('./planning');
9
10// Initialize OpenAI client
11const openai = new OpenAI({
12 apiKey: process.env.OPENAI_API_KEY
13});
14
15// Create a new tool registry and register all tools
16const toolRegistry = new ToolRegistry();
17toolRegistry.register(webSearchTool);
18toolRegistry.register(weatherTool);
19toolRegistry.register(saveResearchTool);
20toolRegistry.register(generateReportTool);
21
22// Create the research agent
23const createResearchAgent = async () => {
24 const agent = new Agent({
25 name: "ResearchAssistant",
26 description: `I am a research assistant that can help with information gathering,
27 fact-checking, and report generation. I can search the web for current
28 information, save important findings, and compile research reports.`,
29 model: "gpt-4o",
30 tools: toolRegistry,
31 openai: openai,
32 systemPrompt: `As a research assistant, your primary goal is to help users gather
33 and organize information on topics they're interested in. When asked
34 about a topic, follow these steps:
35 1. Understand the user's research needs and goals
36 2. Search for relevant and up-to-date information
37 3. Extract key facts and findings
38 4. Save important information for later reference
39 5. Summarize what you've learned
40 6. Generate reports when requested
41
42 Always cite sources for information you provide, and prioritize
43 credible sources. Be transparent about the limitations of your
44 knowledge and the information you find.`
45 });
46
47 // Initialize the agent
48 await agent.init();
49
50 // Add planning capability
51 const planningModule = createPlanningModule(agent);
52 agent.usePlanning(planningModule);
53
54 return agent;
55};
56
57// Handle agent interaction with memory management
58const interactWithAgent = async (message, sessionId = 'default-session') => {
59 const agent = await createResearchAgent();
60
61 // Get session context
62 const context = memory.getContext(sessionId);
63
64 // Process the message
65 const response = await agent.chat({
66 message,
67 sessionId,
68 context
69 });
70
71 // Update memory
72 memory.addMessage(sessionId, 'user', message);
73 memory.addMessage(sessionId, 'assistant', response.content);
74
75 // Update task state if needed
76 if (response.taskState) {
77 Object.entries(response.taskState).forEach(([key, value]) => {
78 memory.setTaskState(sessionId, key, value);
79 });
80 }
81
82 return response;
83};
84
85module.exports = { createResearchAgent, interactWithAgent };
86
To deploy your agent in a production environment, you'll need proper monitoring and logging:
1// monitoring.js
2const winston = require('winston');
3
4// Create logger
5const logger = winston.createLogger({
6 level: 'info',
7 format: winston.format.combine(
8 winston.format.timestamp(),
9 winston.format.json()
10 ),
11 defaultMeta: { service: 'research-agent' },
12 transports: [
13 new winston.transports.File({ filename: 'error.log', level: 'error' }),
14 new winston.transports.File({ filename: 'combined.log' })
15 ]
16});
17
18// Add console logging in development
19if (process.env.NODE_ENV !== 'production') {
20 logger.add(new winston.transports.Console({
21 format: winston.format.simple()
22 }));
23}
24
25// Agent activity monitoring
26const monitorAgent = (agent) => {
27 // Log all agent actions
28 agent.on('action', (action) => {
29 logger.info('Agent action', { action });
30 });
31
32 // Log all tool executions
33 agent.on('toolExecution', (tool, params, result) => {
34 logger.info('Tool execution', {
35 tool: tool.name,
36 params,
37 success: !result.error,
38 resultSummary: result.error ? result.error : 'Success'
39 });
40 });
41
42 // Log errors
43 agent.on('error', (error) => {
44 logger.error('Agent error', { error: error.message, stack: error.stack });
45 });
46
47 return agent;
48};
49
50module.exports = { logger, monitorAgent };
51
Update your server to include monitoring:
1// server.js (with monitoring)
2const express = require('express');
3const { createResearchAgent, interactWithAgent } = require('./research-agent');
4const { logger, monitorAgent } = require('./monitoring');
5
6const app = express();
7app.use(express.json());
8
9let agent;
10
11// Initialize the agent with monitoring
12(async () => {
13 try {
14 agent = await createResearchAgent();
15 monitorAgent(agent);
16 logger.info("Agent initialized successfully!");
17 } catch (error) {
18 logger.error("Failed to initialize agent", { error: error.message });
19 }
20})();
21
22// Add request logging middleware
23app.use((req, res, next) => {
24 const start = Date.now();
25 res.on('finish', () => {
26 const duration = Date.now() - start;
27 logger.info('API request', {
28 method: req.method,
29 path: req.path,
30 statusCode: res.statusCode,
31 duration: `${duration}ms`
32 });
33 });
34 next();
35});
36
37// Endpoint to interact with the agent
38app.post('/chat', async (req, res) => {
39 try {
40 const { message, sessionId = 'default-session' } = req.body;
41
42 if (!message) {
43 return res.status(400).json({ error: "Message is required" });
44 }
45
46 logger.info('Chat request received', { sessionId, messagePreview: message.substring(0, 50) });
47
48 const response = await interactWithAgent(message, sessionId);
49 res.json({ response });
50 } catch (error) {
51 logger.error("Error in chat endpoint", { error: error.message });
52 res.status(500).json({ error: "An error occurred while processing your request" });
53 }
54});
55
56// Health check endpoint
57app.get('/health', (req, res) => {
58 if (agent) {
59 res.json({ status: 'healthy', agent: agent.name });
60 } else {
61 res.status(503).json({ status: 'initializing' });
62 }
63});
64
65const PORT = process.env.AGENT_PORT || 3000;
66app.listen(PORT, () => {
67 logger.info(`Agent server running on port ${PORT}`);
68});
69
For production deployment, consider these optimization strategies:
1// optimization.js
2const cacheManager = require('cache-manager');
3const redisStore = require('cache-manager-redis-store');
4
5// Create a Redis cache
6const redisCache = cacheManager.caching({
7 store: redisStore,
8 host: process.env.REDIS_HOST || 'localhost',
9 port: process.env.REDIS_PORT || 6379,
10 ttl: 600 // Time to live in seconds
11});
12
13// Optimize web search with caching
14const optimizeWebSearch = (searchTool) => {
15 const originalExecute = searchTool.execute;
16
17 searchTool.execute = async (params) => {
18 const cacheKey = `web_search:${JSON.stringify(params)}`;
19
20 try {
21 // Try to get from cache first
22 const cachedResult = await redisCache.get(cacheKey);
23 if (cachedResult) {
24 return cachedResult;
25 }
26
27 // If not in cache, execute original function
28 const result = await originalExecute(params);
29
30 // Store in cache
31 await redisCache.set(cacheKey, result);
32
33 return result;
34 } catch (error) {
35 // If cache fails, just execute original function
36 return originalExecute(params);
37 }
38 };
39
40 return searchTool;
41};
42
43module.exports = { optimizeWebSearch };
44
In this comprehensive guide, we've covered the essential steps for building AI agents with OpenAI's Agents SDK:
This foundation gives you the skills to build powerful AI agents that can tackle complex tasks across a wide range of domains. As you continue developing with the Agents SDK, consider these advanced techniques:
The Agents SDK opens up a new frontier in AI development, enabling applications that go beyond simple chat interfaces to become truly useful digital assistants that can understand, plan, and execute tasks in the real world.
Ready to take your AI agent development to the next level? Check out our advanced Agents SDK tutorials or join our developer community to share your experiences and learn from others.