Tekko

Language

Get in Touch

Usually respond within 24 hours

Back to BlogAI & ML

Building Custom MCP Servers: Giving AI Agents Direct Tool Access

7 min read
MCPAI AgentsTypeScriptAnthropicSoftware Architecture
Building Custom MCP Servers: Giving AI Agents Direct Tool Access

The limitation of Large Language Models (LLMs) has never been their reasoning capability; it has been their isolation. For years, we have operated in a paradigm where we copy-paste logs, code snippets, and database schemas into a chat interface, acting as a manual bridge between our development environment and the AI.

Anthropic’s Model Context Protocol (MCP) changes this dynamic. It provides an open-standard specification for how AI agents—whether they are running in your IDE, a desktop app, or a CLI—interact with external data and tools. By building a custom MCP server, you effectively give the AI a pair of hands and eyes within your specific ecosystem.

The Architecture of MCP

To build an effective server, you must first understand the three-tier architecture of the protocol:

  1. The Host (Client): This is the application the user interacts with, such as Claude Desktop or an MCP-compatible IDE. The host is responsible for managing permissions and the user interface.
  2. The MCP Server: This is the bridge you build. It exposes specific capabilities (tools, resources, and prompts) via a standardized JSON-RPC interface.
  3. The Local/Remote Resources: These are your databases, APIs, local file systems, or internal microservices that the server interacts with.

The communication happens over a transport layer—typically Standard Input/Output (stdio) for local processes or HTTP with Server-Sent Events (SSE) for remote connections.

Core Concepts: Resources, Tools, and Prompts

When designing your server, you need to categorize your functionality into three buckets:

Resources

Resources are essentially the "read-only" data sources. Think of them as GET endpoints. They allow the AI to explore information like documentation, log files, or database schemas. Resources are identified by URIs (e.g., db://main/tables/users).

Tools

Tools are the "action" layer. These are executable functions that allow the AI to change the state of the world or perform complex computations. A tool could be "Run SQL Query," "Create Jira Ticket," or "Trigger GitHub Action." Tools require explicit user approval before execution in most host environments.

Prompts

Prompts are reusable templates that help the AI understand how to interact with your specific tools. They act as the "UI components" of the AI world, providing context and instructions for specific tasks.

Implementation: Building a Custom SQLite Inspector

Let’s walk through building a practical MCP server using the official TypeScript SDK. Our goal is to create a server that allows an AI agent to inspect a local SQLite database to help with debugging or data analysis.

1. Project Initialization

First, set up a new Node.js project:

mkdir mcp-sqlite-server cd mcp-sqlite-server npm init -y npm install @modelcontextprotocol/sdk sqlite3 npm install -D @types/node @types/sqlite3 typescript npx tsc --init

2. Creating the Server

Create an index.ts file. We will initialize the MCP server and define our tools. For this example, we’ll create a tool called query_database.

import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import sqlite3 from "sqlite3"; import { promisify } from "util"; // Initialize the database connection const db = new sqlite3.Database("./dev_database.db"); const dbQuery = promisify(db.all.bind(db)); const server = new Server( { name: "sqlite-inspector", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // 1. List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "query_database", description: "Execute a read-only SQL query on the local database", inputSchema: { type: "object", properties: { sql: { type: "string", description: "The SQL query to run" }, }, required: ["sql"], }, }, ], }; }); // 2. Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "query_database") { const sql = request.params.arguments?.sql as string; // Security check: Only allow SELECT statements if (!sql.trim().toLowerCase().startsWith("select")) { return { content: [{ type: "text", text: "Error: Only SELECT queries are allowed for security reasons." }], isError: true, }; } try { const results = await dbQuery(sql); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }], }; } catch (error: any) { return { content: [{ type: "text", text: `Database error: ${error.message}` }], isError: true, }; } } throw new Error("Tool not found"); }); // 3. Start the server using stdio transport async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("SQLite MCP Server running on stdio"); } main().catch(console.error);

Connecting the Server to a Host

Once the server is built and compiled (npx tsc), you need to register it with an MCP host. If you are using the Claude Desktop app, you would modify your claude_desktop_config.json file:

{ "mcpServers": { "my-sqlite-inspector": { "command": "node", "args": ["/path/to/your/project/dist/index.js"] } } }

Upon restarting Claude, the AI will now see the query_database tool. You can ask, "Look at my local database and tell me how many users signed up last week," and the agent will autonomously generate the SQL, call your server, and interpret the results.

Security Considerations for Custom Servers

Giving an AI agent access to your development tools is powerful, but it introduces a new attack surface. As a senior engineer, you must implement layers of defense:

  1. Read-Only by Default: Unless the specific purpose of the tool is to modify data, enforce read-only access at the application or database user level.
  2. Input Validation: Never pass raw strings directly to a shell or a database without strict validation. Use parameterized queries or allow-lists for commands.
  3. Human-in-the-Loop: MCP hosts generally require a user to click "Allow" before a tool is executed. Do not try to bypass this mechanism.
  4. Local Scoping: If your server interacts with the file system, ensure it is scoped to a specific project directory rather than the entire root disk.

Why MCP Matters for Engineering Teams

Beyond simple database queries, the real power of MCP lies in internal tooling. Most engineering organizations have "tribal knowledge" locked away in internal APIs, custom deployment scripts, and obscure log aggregators.

By building a suite of internal MCP servers, you can:

  • Accelerate Onboarding: A new engineer can ask the AI agent, "How do I spin up the staging environment for the billing service?" and the agent can use the internal MCP tool to find the docs and run the necessary setup scripts.
  • Streamline Incident Response: During a production incident, an MCP server can give the agent access to real-time metrics and logs, allowing it to correlate events faster than a human manually jumping between browser tabs.
  • Standardize Workflows: You can encode your team's best practices into MCP Prompts, ensuring that when the AI helps write code, it follows your specific architectural patterns.

Conclusion and Actionable Steps

The Model Context Protocol is shifting the role of the AI from a remote consultant to an integrated team member. For developers, the effort spent building a custom MCP server pays dividends in reduced context-switching and increased agency for the LLMs we use daily.

To get started:

  1. Identify a repetitive task: Find a workflow where you currently copy-paste data between a tool and an AI.
  2. Build a minimal MCP server: Use the TypeScript or Python SDK to expose one read-only resource or tool.
  3. Test with Claude Desktop: Use the local configuration to verify the AI can interact with your tool correctly.
  4. Iterate on Security: Add validation and scoping before sharing the server with your broader team.

The bridge is built; it's time to start crossing it.