AgileSoftLabs Logo
EmachalanBy Emachalan
Published: July 2026|Updated: July 2026|Reading Time: 17 minutes

Share:

How to Build an AI Agent with MCP: Step-by-Step Guide 2026

 Published: July 22, 2026 | Reading Time: 18 minutes 


About the Author

Emachalan is a Full-Stack Developer specializing in MEAN & MERN Stack, focused on building scalable web and mobile applications with clean, user-centric code.

Key Takeaways

  • MCP (Model Context Protocol) is the emerging standard for connecting AI models to external tools, data sources, and services — developed by Anthropic and now adopted across the AI ecosystem, including OpenAI, Cursor, and Cline, it solves the n×m integration problem that previously required custom code for every model-tool pair.
  • One MCP server works with any MCP-compatible client — build your Postgres or Slack integration once, and it works with Claude Desktop, Claude Code, Cursor, and any custom agent without model-specific code changes.
  • Three transport options cover the full deployment spectrum: stdio for local tools (server runs as a subprocess), HTTP+SSE for remote multi-client deployments, and the emerging Streamable HTTP transport for modern production architectures.
  • MCP servers expose three capability types: Tools (functions the AI calls with defined inputs), Resources (data the AI reads — files, database tables, API responses), and Prompts (parameterized message templates that inject domain context into the conversation).
  • The agentic loop runs until stop_reason === 'end_turn' — while the model returns tool_use, your code executes the requested MCP tool, appends the result as a tool_result message, and loops back to inference, creating a self-directing agent that uses tools until the task is complete.
  • Security is non-negotiable from the first line of tool implementation: input validation with Zod, SELECT-only query enforcement, row limit enforcement, rate limiting per tool, and structured audit logging must be designed in — not retrofitted after an incident.
  • The MCP ecosystem already has 1,000+ open-source servers for Slack, GitHub, PostgreSQL, Google Drive, and dozens of other services — the correct starting point for a new integration is checking the registry before building from scratch.

Introduction

Before MCP, connecting an AI model to external tools meant building a custom integration for every model-tool pair. Claude + Notion required one codebase. GPT-4 + Notion required a different codebase. Neither was composable or reusable. The protocol mismatch created an n×m maintenance problem that compounded every time a new model or tool entered the picture.

MCP defines a standard so that one server implementation works with any MCP-compatible client, tools are composable across servers, and no model-specific code lives in your tool implementations. The ecosystem impact is already visible: 1,000+ open-source MCP servers exist for common services, and every major AI development environment now supports the protocol.

At AgileSoftLabs, we have built production MCP servers connecting AI agents to internal databases, Slack workspaces, Salesforce instances, and custom business tools. This guide is the practical implementation path — from first server through production deployment.

AI & Machine Learning Development Services builds the production MCP server infrastructure and agent orchestration layers described in this guide, including the enterprise deployments connecting AI agents to internal databases, CRM systems, and business intelligence tools.

What MCP Solves

Before MCP, connecting an AI model to external tools meant building a custom integration for every model-tool pair. Claude + Notion = custom code. GPT-4 + Notion = different custom code. Neither was composable.

MCP defines a standard protocol so:

  • One MCP server (for Notion, GitHub, Postgres, etc.) works with any MCP-compatible client (Claude, Claude Code, Cursor, custom agents)
  • Tools are composable — combine an MCP server for your database with one for Slack and one for GitHub
  • No model-specific code in your tool implementations
The impact:

Without MCPWith MCP
Custom integration per modelOne server, any MCP-compatible client
Non-composable toolsMix and match servers freely
Tightly coupled architecturePluggable, swappable components
Manual tool discovery by developerSelf-describing tools discovered at runtime

The architectural shift is from a two-dimensional integration matrix to a single standardized protocol layer. An MCP server for PostgreSQL works with Claude, Claude Code, Cursor, and any future MCP-compatible client without modification. An enterprise team building an MCP server for their internal CRM does that work once — the investment compounds across every AI workflow that needs CRM access.

MCP Architecture: Clients, Servers, Transport

MCP Client (Claude, agent application)
         ↕ (JSON-RPC via stdio / SSE / HTTP)
MCP Server (your tool implementations)
         ↕
External Systems (database, API, filesystem, etc.)

Three layers compose the system:

MCP Client is the AI model or application sending requests. Claude Desktop, Claude Code, and any application using Anthropic's SDK can act as an MCP client. The client discovers what tools, resources, and prompts a server offers at connection time through a standardized capability negotiation.

MCP Server is your code exposing tools, resources, and prompts. It runs as a separate process connected to the client via one of the transport mechanisms. It has no knowledge of which specific AI model is on the other side — it speaks the MCP protocol.

Transport options match deployment context.

  • stdio runs the server as a subprocess of the client, communicating via stdin/stdout — ideal for local tools and developer workflows in Claude Desktop.
  • HTTP+SSE (Server-Sent Events) runs the server independently, allowing multiple clients to connect — the correct architecture for remote servers and multi-user deployments.
  • Streamable HTTP is the emerging modern alternative to SSE for production cloud deployments.

Cloud Development Services provisions the remote MCP server infrastructure — containerized deployments, connection management, TLS termination, and the horizontal scaling that multi-client server deployments require when AI agents serve multiple concurrent users.

Building Your First MCP Server

Install the MCP SDK

npm install @modelcontextprotocol/sdk

Basic Server (TypeScript)

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

const server = new Server(
  {
    name: 'my-first-mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},      // Server provides tools
      resources: {},  // Server provides resources (data to read)
    },
  }
);

// Register tool definitions
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'get_weather',
      description: 'Get current weather for a city',
      inputSchema: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: 'The city name',
          },
          units: {
            type: 'string',
            enum: ['celsius', 'fahrenheit'],
            description: 'Temperature units',
            default: 'celsius',
          },
        },
        required: ['city'],
      },
    },
  ],
}));

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'get_weather') {
    const { city, units = 'celsius' } = request.params.arguments as {
      city: string;
      units?: string;
    };

    const weather = await fetchWeather(city, units);

    return {
      content: [
        {
          type: 'text',
          text: `Weather in ${city}: ${weather.temperature}°${units === 'celsius' ? 'C' : 'F'}, ${weather.description}`,
        },
      ],
    };
  }

  throw new Error(`Unknown tool: ${request.params.name}`);
});

// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP server running on stdio');

Defining Tools, Resources, and Prompts

MCP servers expose three distinct capability types, each serving a different role in the AI interaction.

Tools (Actions)

Tools are functions the AI can call. They have defined inputs and return text or structured data. The input schema is JSON Schema — the same format Anthropic's tool calling API uses, which is not coincidental:

{
  name: 'query_database',
  description: 'Execute a read-only SQL query against the production database',
  inputSchema: {
    type: 'object',
    properties: {
      query: {
        type: 'string',
        description: 'The SQL SELECT query to execute',
      },
      limit: {
        type: 'number',
        description: 'Maximum rows to return (default: 100)',
        default: 100,
      },
    },
    required: ['query'],
  },
}

The description field matters more than most developers initially appreciate. The AI reads it to decide when to use a tool, not just how to use it. Descriptions that say what the tool does, what data it works with, and when it is appropriate dramatically improve the quality of tool selection.

Resources (Data to Read)

Resources are data sources the AI can read — files, database tables, API responses — identified by URI:

server.setRequestHandler(ListResourcesRequestSchema, async () => ({
  resources: [
    {
      uri: 'postgres://mydb/users',
      name: 'Users Table',
      description: 'All user records',
      mimeType: 'application/json',
    },
    {
      uri: 'file:///data/config.json',
      name: 'App Configuration',
      mimeType: 'application/json',
    },
  ],
}));

Prompts (Reusable Templates)

Prompts are parameterized message templates that inject structured context or domain knowledge into the conversation:

server.setRequestHandler(ListPromptsRequestSchema, async () => ({
  prompts: [
    {
      name: 'analyze_user_churn',
      description: 'Analyze user churn for a specific date range',
      arguments: [
        { name: 'start_date', description: 'Start date (YYYY-MM-DD)', required: true },
        { name: 'end_date', description: 'End date (YYYY-MM-DD)', required: true },
      ],
    },
  ],
}));

AI Meeting Assistant Slack workspace integrations use the Prompts capability to inject company-specific context — meeting templates, decision frameworks, and agenda structures — into the AI's conversation context before any tool calls execute. The prompt acts as a domain briefing that shapes how the agent interprets results it retrieves through tools.

Connecting to Claude

Claude Desktop Configuration

Configure Claude Desktop to use your MCP server by editing the config file at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the equivalent Windows path:

{
  "mcpServers": {
    "my-weather-server": {
      "command": "node",
      "args": ["/path/to/my-server/build/index.js"],
      "env": {
        "WEATHER_API_KEY": "your-api-key"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost/mydb"
      }
    }
  }
}

Restart Claude Desktop. Your tools appear in the Claude interface automatically — no additional configuration required.

Connect via Anthropic SDK in Your Application

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const tools = [
  {
    name: 'get_weather',
    description: 'Get current weather for a city',
    input_schema: {
      type: 'object' as const,
      properties: {
        city: { type: 'string', description: 'City name' },
      },
      required: ['city'],
    },
  },
];

const response = await client.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 4096,
  tools,
  messages: [
    { role: 'user', content: 'What is the weather in Tokyo?' }
  ],
});

Business AI OS enterprise knowledge management deployments use exactly this SDK integration pattern — multiple MCP servers registered for different data domains (CRM, analytics, documents, communications) with the agent selecting the appropriate server based on the query context.

Building an Agent with MCP Tools

The agentic loop runs until the model signals it is done. While it has work to do, it requests tool use; your code executes the tools and returns results; the loop continues:

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

async function runAgent(userQuery: string, mcpTools: any[]) {
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: userQuery }
  ];

  while (true) {
    const response = await client.messages.create({
      model: 'claude-opus-4-7',
      max_tokens: 4096,
      tools: mcpTools,
      messages,
    });

    console.log(`Stop reason: ${response.stop_reason}`);

    if (response.stop_reason === 'end_turn') {
      // Agent is done — extract final text response
      const finalText = response.content
        .filter(block => block.type === 'text')
        .map(block => (block as any).text)
        .join('');
      return finalText;
    }

    if (response.stop_reason === 'tool_use') {
      // Agent wants to use tools
      const toolUseBlocks = response.content.filter(b => b.type === 'tool_use');

      // Add assistant's message (with tool calls) to history
      messages.push({ role: 'assistant', content: response.content });

      // Execute all tool calls, collect results
      const toolResults: Anthropic.MessageParam = {
        role: 'user',
        content: await Promise.all(
          toolUseBlocks.map(async (toolUse: any) => {
            const result = await executeMCPTool(toolUse.name, toolUse.input);
            return {
              type: 'tool_result' as const,
              tool_use_id: toolUse.id,
              content: JSON.stringify(result),
            };
          })
        ),
      };

      messages.push(toolResults);
      // Loop continues — agent processes tool results and decides next action
    }
  }
}

async function executeMCPTool(toolName: string, input: any) {
  switch (toolName) {
    case 'get_weather':
      return await fetchWeather(input.city);
    case 'query_database':
      return await queryDB(input.query);
    default:
      throw new Error(`Unknown tool: ${toolName}`);
  }
}

The key architectural point: the agent drives its own execution path. It decides which tools to call, in what order, how many times, and when it has enough information to produce a final answer — without any hardcoded orchestration logic specifying that sequence.

AI Sales Agent and AI Document Processing enterprise deployments both run variations of this agentic loop — the sales agent calling CRM tools, email tools, and calendar tools in whatever sequence the prospect research task requires; the document agent calling extraction tools, validation tools, and database write tools in sequence driven by document content rather than a fixed pipeline.

Real-World MCP Server: Database Query Agent

A production-grade MCP server for safe analytics database querying demonstrates the full pattern — tools, security enforcement, structured error handling, and resource listing all together:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import postgres from 'postgres';

const sql = postgres(process.env.DATABASE_URL!);

const server = new Server(
  { name: 'postgres-analytics', version: '1.0.0' },
  { capabilities: { tools: {}, resources: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'query_analytics',
      description: `Query the analytics database. Only SELECT queries are allowed.
        Available tables: users, orders, products, events.
        Always include a LIMIT clause (max 1000 rows).`,
      inputSchema: {
        type: 'object',
        properties: {
          query: { type: 'string', description: 'SQL SELECT query' },
          explain: {
            type: 'boolean',
            description: 'Show query execution plan',
            default: false,
          },
        },
        required: ['query'],
      },
    },
    {
      name: 'list_tables',
      description: 'List all available tables and their schemas',
      inputSchema: { type: 'object', properties: {} },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'query_analytics') {
    const { query, explain } = request.params.arguments as {
      query: string;
      explain?: boolean;
    };

    // Security: only allow SELECT and WITH (CTE) queries
    const normalized = query.trim().toUpperCase();
    if (!normalized.startsWith('SELECT') && !normalized.startsWith('WITH')) {
      return {
        content: [{ type: 'text', text: 'Error: Only SELECT queries are permitted.' }],
        isError: true,
      };
    }

    // Security: enforce row limit
    const limitedQuery = query.includes('LIMIT') ? query : `${query} LIMIT 100`;

    try {
      const finalQuery = explain ? `EXPLAIN ANALYZE ${limitedQuery}` : limitedQuery;
      const result = await sql.unsafe(finalQuery);

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              rowCount: result.length,
              columns: result.length > 0 ? Object.keys(result[0]) : [],
              rows: result,
            }, null, 2),
          },
        ],
      };
    } catch (error: any) {
      return {
        content: [{ type: 'text', text: `Query error: ${error.message}` }],
        isError: true,
      };
    }
  }

  if (request.params.name === 'list_tables') {
    const tables = await sql`
      SELECT table_name,
             (SELECT count(*)
              FROM information_schema.columns
              WHERE table_name = t.table_name
              AND table_schema = 'public') AS column_count
      FROM information_schema.tables t
      WHERE table_schema = 'public'
      ORDER BY table_name
    `;
    return {
      content: [{ type: 'text', text: JSON.stringify(tables, null, 2) }],
    };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

Security Considerations

MCP servers run with your application's permissions. An agent with access to a poorly secured MCP server has access to everything the server can access. Security must be designed in from the first tool implementation.

1. Input Validation with Zod

import { z } from 'zod';

const QuerySchema = z.object({
  query: z.string().max(10000).refine(
    q => q.trim().toUpperCase().startsWith('SELECT'),
    'Only SELECT queries allowed'
  ),
});

// In your tool handler:
const validated = QuerySchema.parse(request.params.arguments);

2. Principle of Least Privilege

Each MCP server should run with the minimum permissions required for its specific function. A read-only analytics server uses a read-only database user. A file-access server exposes only a specific directory. An API integration uses an API key scoped to the specific endpoints the agent needs. Never give an MCP server admin credentials because it is convenient.

3. Rate Limiting Per Tool

const rateLimiter = new Map<string, number[]>();

function checkRateLimit(toolName: string, maxCalls = 100, windowMs = 60000) {
  const now = Date.now();
  const calls = rateLimiter.get(toolName) || [];
  const recentCalls = calls.filter(t => now - t < windowMs);
  if (recentCalls.length >= maxCalls) {
    throw new Error(`Rate limit exceeded for ${toolName}`);
  }
  rateLimiter.set(toolName, [...recentCalls, now]);
}

4. Audit Logging

Every tool call should be logged with timestamp, tool name, and input before execution — not after, so you have a record even if the call fails or times out:

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  console.error(JSON.stringify({
    timestamp: new Date().toISOString(),
    tool: request.params.name,
    input: request.params.arguments,
  }));
  // ... execute tool
});

Deploying MCP Servers

Remote Deployment with HTTP+SSE

  • Local (stdio transport): Runs as a subprocess on the user's machine. Good for developer tools, Claude Desktop integration.
  • Remote (HTTP+SSE transport): Runs as a persistent service. Multiple clients can connect. 

Required for multi-user or cloud deployments, the server runs independently, and clients connect via HTTP:

import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';

const app = express();

app.get('/sse', async (req, res) => {
  const transport = new SSEServerTransport('/messages', res);
  await server.connect(transport);
});

app.post('/messages', async (req, res) => {
  // Handle incoming client messages
});

app.listen(3000);

Docker Deployment

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY build/ ./build/
EXPOSE 3000
CMD ["node", "build/index.js"]

MCP Inspector for Development and Testing

Before connecting your server to Claude, validate it works correctly using the MCP Inspector:

npx @modelcontextprotocol/inspector node build/index.js

This provides an interactive UI to test tools, resources, and prompts against your server — surfacing tool definition errors, input schema problems, and response format issues before they create confusing agent behavior in production.

Web Application Development Services builds the web-facing layer for remote MCP server deployments — the Express/Fastify application server, authentication middleware, and SSE endpoint configuration that connects Claude and custom agent applications to backend MCP servers in cloud environments.

Explore AgileSoftLabs case studies for production MCP server deployments connecting enterprise AI agents to databases, CRM systems, and business intelligence tools across regulated industries including healthcare and financial services.

Building an Enterprise AI Agent with MCP?

The Model Context Protocol makes AI agents genuinely composable — one server per data source, mixed and matched by any agent that needs that data, with zero model-specific code in your tool implementations. The architectural investment compounds across every AI workflow you build.

AgileSoftLabs has deployed production MCP servers connecting AI agents to internal databases, CRM platforms, business intelligence tools, and communication workflows. Explore the full AI products and services portfolio or contact our AI team to discuss your enterprise AI agent architecture.

Frequently Asked Questions

1. What is the difference between MCP tools and regular Anthropic API tool calling? 

Anthropic's native tool calling API requires the developer to define tools inline in each API call and handle execution manually. MCP is a protocol layer on top of that model — MCP servers self-describe their tools, the MCP client (Claude Desktop, your agent app) discovers them at connection time rather than requiring them to be hardcoded, and any MCP-compatible client can use any MCP server. The underlying mechanism when Claude uses an MCP tool is still Anthropic's tool calling API — MCP adds the discovery, composition, and reuse layer that the raw API does not provide.

2. Can I use MCP with GPT-4 or models other than Claude? 

Yes. MCP is an open protocol, not Anthropic-specific. OpenAI has announced MCP support. Cursor, Cline, Continue, and other AI coding tools already support MCP. The ecosystem is moving toward MCP as a universal standard in the same way REST became the standard for web APIs. Writing your tool implementations as MCP servers means they work across the current and future landscape of MCP-compatible AI applications without any modification.

3. How do I debug MCP server issues before connecting to Claude?

Use the MCP Inspector: npx @modelcontextprotocol/inspector node build/index.js. It provides an interactive UI to test your server's tools, resources, and prompts — you can send test tool calls with specific inputs and inspect the exact responses before any AI model is involved. For stdio transport issues specifically, check that your server writes logs to stderr (not stdout — stdout is reserved for the MCP protocol), and that your server exits cleanly when its stdin closes.

4. What is the performance overhead of MCP compared to direct API calls?

For stdio transport, the overhead is negligible — the server runs as a local subprocess and communication is via in-process pipes. For HTTP+SSE transport, network latency adds 5–50ms per tool call depending on server location. In practice, AI model inference time (1–10 seconds per turn) dwarfs any MCP communication overhead by 1–3 orders of magnitude. The bottleneck is never MCP transport — it is model inference and whatever the tool itself does (database query, API call, file I/O).

5. How should I handle errors in MCP tool implementations — throw or return an error response?

Return an error response using isError: true in the content block rather than throwing JavaScript exceptions. Returning a structured error allows the agent to read the error message and decide how to respond — it might retry with different parameters, use a different tool, or explain the failure to the user. Throwing an uncaught exception in a tool handler crashes the MCP server entirely (or returns an unstructured error), leaving the agent without useful information about what went wrong. Treat every tool handler like an API endpoint: always return a structured response, and only propagate exceptions for truly unrecoverable server errors.

6. What MCP servers already exist that I should use rather than building from scratch?

The MCP ecosystem has 1,000+ open-source servers covering the most common integrations. Official Anthropic-maintained servers include filesystem, PostgreSQL, SQLite, Google Drive, Slack, GitHub, Google Maps, and Brave Search. Community servers cover dozens more — Notion, Salesforce, HubSpot, Jira, Linear, various database clients, and monitoring tools. Always check the MCP server registry before building a custom implementation — an existing server that covers 90% of your use case is almost always preferable to a custom build that you own and maintain indefinitely.

Building an AI agent? Get a free architecture review.

A senior AI engineer will review your use case and recommend the right framework, model mix, and infra — in 30 minutes, no pitch.

How to Build an AI Agent with MCP: Step-by-Step Guide 2026 - AgileSoftLabs Blog