Building AI-Powered Integrations with MCP Servers: A Complete Tutorial Gupta Abhishek Premkumar published a developer guide to building and deploying Model Context Protocol (MCP) servers, an open JSON-RPC standard for connecting LLM-based AI assistants to external tools, APIs, and data sources. The guide covers MCP's client-server architecture, tool, resource, and prompt definitions, and walks through constructing a practical MCP server that exposes database query capabilities. Model Context Protocol MCP Servers: A Complete Guide to Building AI-Powered Integrations Author: Gupta Abhishek Premkumar Published: September 2026 Reading Time: 13 minutes Tags: AI, MCP, Model Context Protocol, LLM, Integration, Developer Tools As Large Language Models LLMs become integral to modern software development, the need for standardized communication protocols between AI assistants and external tools has never been greater. The Model Context Protocol MCP emerges as a groundbreaking open standard that enables seamless, secure, and scalable integrations between AI models and external data sources, APIs, and services. This article provides a comprehensive guide to understanding, building, and deploying MCP servers, empowering developers to extend AI capabilities beyond their inherent limitations. The evolution of AI assistants has reached an inflection point. While Large Language Models possess remarkable reasoning and generation capabilities, they remain fundamentally limited by their training data cutoff and inability to interact with real-time systems. Enter the Model Context Protocol MCP — an open standard designed to bridge this gap by providing a universal interface for AI models to communicate with external tools, databases, and services. Think of MCP as the "USB standard" for AI integrations. Just as USB standardized how peripherals connect to computers, MCP standardizes how AI assistants connect to the digital world. The Model Context Protocol is an open, JSON-RPC-based protocol that defines how AI applications clients communicate with external services servers to access tools, resources, and contextual information. Developed with the goal of creating a universal standard for AI integrations, MCP enables: | Benefit | Description | |---|---| | Interoperability | Works across different AI platforms and providers | | Security | Built-in authentication and authorization mechanisms | | Scalability | Designed for enterprise-grade deployments | | Extensibility | Easy to add new tools and capabilities | | Developer Experience | Simple APIs with comprehensive documentation | The MCP architecture follows a client-server model with clear separation of concerns: ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ │ │ AI Client │◄───────►│ MCP Server │◄───────►│ External │ │ LLM Host │ JSON │ Your Code │ │ Services │ │ │ RPC │ │ │ APIs, DBs │ └─────────────────┘ └─────────────────┘ └─────────────────┘ Tools are the primary mechanism for AI models to perform actions. Each tool has: // Example Tool Definition { name: "get weather", description: "Retrieves current weather information for a specified city", inputSchema: { type: "object", properties: { city: { type: "string", description: "The city name to get weather for" }, units: { type: "string", enum: "celsius", "fahrenheit" , default: "celsius" } }, required: "city" } } Resources provide read-only access to data sources. They are ideal for: // Example Resource Definition { uri: "file:///config/settings.json", name: "Application Settings", description: "Current application configuration", mimeType: "application/json" } Prompts are reusable templates that help AI models understand how to interact with specific domains or workflows. // Example Prompt Definition { name: "code review", description: "Template for performing code reviews", arguments: { name: "language", description: "Programming language of the code", required: true } } Let's build a practical MCP server that provides database query capabilities. We'll use TypeScript with the official MCP SDK. Create project directory mkdir mcp-database-server cd mcp-database-server Initialize Node.js project npm init -y Install dependencies npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node ts-node Create tsconfig.json : { "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": "src/ / " , "exclude": "node modules", "dist" } Create src/index.ts : js import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; // Define tool input schemas using Zod const QueryDatabaseSchema = z.object { query: z.string .describe "SQL query to execute" , database: z.string .optional .describe "Target database name" , } ; const GetTableSchemaInput = z.object { tableName: z.string .describe "Name of the table to describe" , } ; // Simulated database replace with actual database connection const mockDatabase = { users: { id: 1, name: "Alice Johnson", email: "alice@example.com", role: "admin" }, { id: 2, name: "Bob Smith", email: "bob@example.com", role: "user" }, { id: 3, name: "Carol White", email: "carol@example.com", role: "user" }, , products: { id: 1, name: "Laptop", price: 999.99, stock: 50 }, { id: 2, name: "Mouse", price: 29.99, stock: 200 }, { id: 3, name: "Keyboard", price: 79.99, stock: 150 }, , }; // Create the MCP server const server = new Server { name: "database-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, resources: {}, }, } ; // Handle tool listing requests server.setRequestHandler ListToolsRequestSchema, async = { return { tools: { name: "query database", description: "Execute a SQL-like query against the database. " + "Supports SELECT statements with WHERE clauses.", inputSchema: { type: "object", properties: { query: { type: "string", description: "SQL query to execute SELECT only ", }, database: { type: "string", description: "Target database name optional ", }, }, required: "query" , }, }, { name: "get table schema", description: "Retrieve the schema information for a specific table, " + "including column names and data types.", inputSchema: { type: "object", properties: { tableName: { type: "string", description: "Name of the table to describe", }, }, required: "tableName" , }, }, { name: "list tables", description: "List all available tables in the database", inputSchema: { type: "object", properties: {}, required: , }, }, , }; } ; // Handle tool execution requests server.setRequestHandler CallToolRequestSchema, async request = { const { name, arguments: args } = request.params; switch name { case "query database": { const { query } = QueryDatabaseSchema.parse args ; // Simple query parser production should use proper SQL parser const tableMatch = query.toLowerCase .match /from\s+ \w+ / ; if tableMatch { return { content: { type: "text", text: "Error: Could not parse table name from query", }, , }; } const tableName = tableMatch 1 as keyof typeof mockDatabase; const data = mockDatabase tableName ; if data { return { content: { type: "text", text: Error: Table '${tableName}' not found , }, , }; } return { content: { type: "text", text: JSON.stringify data, null, 2 , }, , }; } case "get table schema": { const { tableName } = GetTableSchemaInput.parse args ; const data = mockDatabase tableName as keyof typeof mockDatabase ; if data || data.length === 0 { return { content: { type: "text", text: Error: Table '${tableName}' not found or empty , }, , }; } const schema = Object.keys data 0 .map key = { column: key, type: typeof data 0 key as keyof typeof data 0 , } ; return { content: { type: "text", text: JSON.stringify schema, null, 2 , }, , }; } case "list tables": { const tables = Object.keys mockDatabase ; return { content: { type: "text", text: JSON.stringify { tables, count: tables.length, }, null, 2 , }, , }; } default: throw new Error Unknown tool: ${name} ; } } ; // Handle resource listing server.setRequestHandler ListResourcesRequestSchema, async = { return { resources: { uri: "db://schema/overview", name: "Database Schema Overview", description: "Complete overview of all tables and their schemas", mimeType: "application/json", }, , }; } ; // Handle resource reading server.setRequestHandler ReadResourceRequestSchema, async request = { const { uri } = request.params; if uri === "db://schema/overview" { const overview = Object.entries mockDatabase .map table, data = { table, rowCount: data.length, columns: data.length 0 ? Object.keys data 0 : , } ; return { contents: { uri, mimeType: "application/json", text: JSON.stringify overview, null, 2 , }, , }; } throw new Error Resource not found: ${uri} ; } ; // Start the server async function main { const transport = new StdioServerTransport ; await server.connect transport ; console.error "Database MCP Server running on stdio" ; } main .catch console.error ; Create mcp-config.json for client configuration: { "mcpServers": { "database": { "command": "node", "args": "dist/index.js" , "cwd": "/path/to/mcp-database-server" } } } Compile TypeScript npx tsc The server is now ready to be connected to an MCP client js // Implement authentication for sensitive operations const authenticateRequest = async token: string : Promise