Model Context Protocol (MCP)

Mastering MCP Servers: Connecting Large Language Models to the Real World

In the rapidly evolving landscape of Artificial Intelligence, Large Language Models (LLMs) are powerful but often isolated. They rely on pre-trained knowledge or context injected via prompts, which can be limited, static, or token-expensive. The Model Context Protocol (MCP) has emerged as the standard for solving this problem. Developed by Anthropic, MCP is an open standard that allows LLM applications to securely connect to external data sources, tools, and environments.

Think of MCP as the "USB-C for AI." Just as USB-C provides a universal interface for charging and data transfer across different devices, MCP provides a universal interface for LLMs to interact with the digital world. In this guide, we will explore the architecture of MCP and demonstrate how to build a robust MCP server from scratch.

Understanding the MCP Architecture

MCP operates on a client-server architecture. The MCP Host (such as Claude Desktop or a custom application) initiates the connection. It contains an MCP Client that communicates with one or more MCP Servers. These servers act as bridges, exposing specific capabilities (Tools) or data (Resources) to the LLM in a structured, JSON-RPC compliant format.

The key benefit of this decoupling is security and modularity. The LLM itself does not execute arbitrary code or make direct API calls to third-party services. Instead, it requests specific actions defined by the MCP Server, which then executes the action in a sandboxed environment and returns the result.

Defining Capabilities: Tools vs. Resources

When building an MCP server, you must decide what you are exposing. Generally, these fall into two categories:

  • Tools: Functions that the LLM can execute. For example, "get_current_weather" or "create_github_issue." These are stateful actions that change the world or fetch fresh data.
  • Resources: Read-only data structures that the LLM can retrieve. For example, a file system path or a specific database table schema. These are typically used to provide context without requiring a function call.

Building a Simple MCP Server in TypeScript

For this example, we will use the official MCP TypeScript SDK to build a server that exposes a simple "Calculator" tool. This server allows the LLM to perform basic arithmetic operations.

First, install the necessary dependencies:


npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript ts-node

Now, let's create our server file, server.ts. We will define the tool using zod for schema validation, ensuring that the input parameters are strictly typed.


import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Initialize the MCP Server
const server = new McpServer({
  name: "calculator-server",
  version: "1.0.0",
});

// Define a Tool: Calculate
server.tool(
  "calculate",
  {
    expression: z
      .string()
      .describe("A mathematical expression to evaluate, e.g., '2 + 2'"),
  },
  async ({ expression }) => {
    try {
      // Simple safety check: Only allow basic operators
      if (!/^[\d+\-*/(). ]+$/.test(expression)) {
        return {
          content: [{ type: "text", text: "Invalid characters in expression." }],
        };
      }

      // Note: In a production environment, use a safe math parser library.
      // Do NOT use eval() directly on user input.
      const result = new Function(`return ${expression}`)();
      
      return {
        content: [{ type: "text", text: `Result: ${result}` }],
      };
    } catch (error) {
      return {
        content: [{ type: "text", text: `Error: ${error.message}` }],
        isError: true,
      };
    }
  }
);

// Define a Resource: Current Timestamp
server.resource(
  "current-time",
  "time://now",
  { title: "Current Server Time" },
  async (uri) => {
    return {
      contents: [
        {
          uri: uri.toString(),
          text: new Date().toISOString(),
        },
      ],
    };
  }
);

// Run the Server
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);

Key Implementation Details

Notice the use of zod in the tool definition. MCP relies heavily on JSON Schema to describe what a tool expects. The describe method is crucial because it provides metadata to the LLM. If your description is vague, the LLM may hallucinate invalid inputs or fail to understand when to use the tool. Precision in description writing is a critical skill for MCP developers.

Additionally, error handling is explicit. By setting isError: true, you inform the MCP Client (and thus the LLM) that the tool execution failed. This allows the LLM to potentially retry with different arguments or inform the user of the failure gracefully.

Connecting to Claude Desktop

To test your server, you need to configure your MCP Host. For Claude Desktop, you edit the claude_desktop_config.json file (located in ~/Library/Application Support/Claude/ on macOS or %APPDATA%\Claude on Windows).


{
  "mcpServers": {
    "calculator": {
      "command": "npx",
      "args": ["ts-node", "/path/to/your/server.ts"]
    }
  }
}

Restart Claude Desktop. You should now see the "Calculator" tool available in the developer menu or directly usable in the chat. Ask Claude to "calculate 15 * 3" and watch it invoke your server in the background.

Best Practices and Security Considerations

  1. Least Privilege: Your MCP server should only expose the minimum necessary capabilities. If you are building a database connector, don't expose raw SQL execution if you can avoid it. Provide specific, safe functions like getUserById.
  2. Input Validation: Always validate inputs. Even though the LLM generates the input, it can make mistakes. Use schema validation (like Zod) to reject malformed data before it hits your backend logic.
  3. Idempotency: Where possible, design tools to be idempotent. If a tool fails midway, the LLM might retry. Ensure that retries don't cause side effects like duplicate charges or data corruption.
  4. Logging: Implement detailed logging within your server. When the LLM struggles to use a tool, the logs will provide insight into what parameters were passed and why the call might have failed.

Future Directions

MCP is still a young protocol. As it matures, expect to see:

  • Authentication Standards: More robust OAuth2 flows integrated directly into the protocol spec for secure access to protected APIs.
  • Streamable Resources: Better support for real-time data streams, allowing LLMs to monitor live data sources like stock tickers or server logs.
  • Multi-Server Orchestration: Enhanced capabilities for hosts to manage dozens of MCP servers simultaneously, with smart routing to avoid context window bloat.

Conclusion

Building MCP servers is no longer just a theoretical exercise; it is becoming the standard way to extend the capabilities of AI applications. By decoupling the logic from the model, you create systems that are more secure, maintainable, and versatile. Whether you are building a simple file reader or a complex enterprise data connector, MCP provides the foundation you need.

Start small. Build a single tool that solves a specific problem in your workflow. Test it thoroughly. Then, expand. The ecosystem is growing rapidly, and early adopters are already seeing significant improvements in their LLM applications' ability to reason and act within their specific environments.

Share: