AZMX AI

Guide · 2026-05-29 · 7 min read

How to Build an MCP Server

A technical guide to implementing the Model Context Protocol for local and remote AI tool integration.

The Model Context Protocol (MCP) standardizes how AI agents interact with external data and tools. Instead of writing custom glue code for every agent, you build a single MCP server that exposes resources, prompts, and tools. This allows any MCP-compliant client to interact with your system via a unified interface, decoupling the LLM's reasoning from the specific API implementation.

The MCP Architecture

MCP operates on a client-server model. The MCP Client (such as AZMX AI, Claude Desktop, or IDE extensions) maintains the session and handles the LLM interaction. The MCP Server provides the actual capabilities. Communication typically happens over stdio for local processes or HTTP/SSE for remote services.

Core Primitives

  • Resources: Read-only data sources (e.g., a local log file, a database schema, or a documentation page).
  • Tools: Executable functions that can change state or fetch real-time data (e.g., create_jira_ticket or run_sql_query).
  • Prompts: Pre-defined templates that help the LLM use the server's capabilities effectively.

Step-by-Step Implementation

While you can implement MCP in any language, TypeScript and Python have the most mature SDKs. This example uses the TypeScript SDK.

1. Environment Setup

Initialize your project and install the official SDK:

npm init -y
npm install @modelcontextprotocol/sdk

2. Defining the Server

Create a server instance and define your capabilities. A basic server requires a name and version.

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

const server = new Server({
  name: "my-custom-tool",
  version: "1.0.0",
}, {
  capabilities: {
    resources: {},
    tools: {},
  },
});

3. Implementing a Tool

Tools are the most common use case. You must define the tool's input schema using JSON Schema so the LLM knows how to call it.

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "get_system_load",
    description: "Returns current CPU and memory usage",
    inputSchema: {
      type: "object",
      properties: {},
    },
  }],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_system_load") {
    return {
      content: [{ type: "text", text: "CPU: 12%, MEM: 4.2GB" }],
    };
  }
  throw new Error("Tool not found");
});

4. Transport and Execution

For local agents, use stdio. This allows the client to spawn the server as a child process.

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

Connecting to AI Agents

Once your server is compiled to JS, you must register it in your client's configuration file. Most clients use a JSON config to map the command and arguments required to start the server.

If you are using AZMX AI, the platform supports MCP over both stdio and HTTP. Because AZMX AI is a native Rust app rather than an Electron wrapper, it handles the child process lifecycle with lower overhead, which is critical when running multiple heavy MCP servers (like those indexing large local databases) alongside your IDE.

Comparison with Other Integration Methods

Before MCP, developers relied on proprietary plugin systems or custom function-calling wrappers. Here is how MCP compares to existing patterns:

  • Custom API Wrappers: Requires writing unique integration code for every agent (e.g., different code for Aider vs. Cline). MCP provides a single interface for all.
  • IDE Plugins: Tools like GitHub Copilot or Tabnine often lock you into their ecosystem. MCP servers are portable across any client that implements the protocol.
  • Agent-Specific Toolkits: Frameworks like LangChain provide vast libraries, but MCP moves the logic to the server side, making the agent lighter and the tool more maintainable.

Security Considerations

MCP servers have direct access to your system. This creates a significant attack vector if the LLM is tricked into calling a destructive tool.

Implementation Safeguards

  1. Approval Gates: Never run an MCP server in a client that executes tools automatically. Use a client with explicit approval gates for every shell or edit operation.
  2. Input Validation: Treat all LLM-provided arguments as untrusted. Use libraries like Zod to validate inputs before passing them to system commands.
  3. Deny-lists: Ensure your server cannot access sensitive directories. For instance, AZMX AI implements a default deny-list for .env and .ssh folders to prevent accidental credential leakage via MCP resources.

Optimizing for Performance

To keep your AI agent responsive, follow these guidelines:

  • Pagination: For resources returning large datasets, implement pagination. Do not dump 10MB of logs into the LLM context.
  • Caching: Use a local cache for expensive API calls within your MCP server to reduce latency.
  • Granular Tools: Instead of one manage_database tool, create read_table, update_row, and list_schemas. This reduces LLM hallucination and improves tool-selection accuracy.

Building an MCP server transforms an AI agent from a chat interface into a functional operator capable of interacting with your specific professional environment. By decoupling the tool logic from the model, you ensure your infrastructure remains sovereign and compatible with future LLM advancements.

One window. The whole loop.