TutorialIntermediate

Building Custom MCP Servers: Connect Any API to Your AI

Create Model Context Protocol servers to give AI models access to your custom tools and data.

AIcloud2026-01-2514 min read

Introduction

Model Context Protocol (MCP) servers allow you to expose any API, database, or service to AI models through a standardized interface. In this tutorial, we will build a practical MCP server that connects to a project management tool, giving your AI assistant the ability to create tasks, query project status, and manage workflows.

Prerequisites

  • Node.js 18+ or Python 3.10+
  • An MCP-compatible client (Claude Desktop, Claude Code)
  • Basic TypeScript or Python knowledge

Step 1: Project Setup

bash
mkdir mcp-project-manager
cd mcp-project-manager
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init

Step 2: Define Your Tools

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

const server = new McpServer({
  name: "project-manager",
  version: "1.0.0",
});

// Tool: Create a new task
server.tool(
  "create-task",
  "Create a new task in the project",
  {
    title: z.string().describe("Task title"),
    description: z.string().describe("Task description"),
    priority: z.enum(["low", "medium", "high"]).default("medium"),
    assignee: z.string().optional().describe("Person to assign"),
  },
  async ({ title, description, priority, assignee }) => {
    // Call your project management API here
    const task = await createTask({ title, description, priority, assignee });
    return {
      content: [{
        type: "text",
        text: `Created task #${task.id}: "${title}" (Priority: ${priority})`,
      }],
    };
  }
);

// Tool: List tasks
server.tool(
  "list-tasks",
  "List all tasks with optional filtering",
  {
    status: z.enum(["open", "in-progress", "done", "all"]).default("all"),
    assignee: z.string().optional(),
  },
  async ({ status, assignee }) => {
    const tasks = await getTasks({ status, assignee });
    const formatted = tasks
      .map((t: any) => `- #${t.id} [${t.status}] ${t.title} (@${t.assignee})`)
      .join("\n");
    return {
      content: [{ type: "text", text: formatted || "No tasks found." }],
    };
  }
);

Step 3: Add Resources

typescript
// Expose project data as readable resources
server.resource(
  "project-summary",
  "project://summary",
  async () => {
    const summary = await getProjectSummary();
    return {
      contents: [{
        uri: "project://summary",
        mimeType: "text/plain",
        text: `Project: ${summary.name}
Open tasks: ${summary.openTasks}
In progress: ${summary.inProgress}
Completed: ${summary.completed}
Team size: ${summary.teamSize}`,
      }],
    };
  }
);

Step 4: Configure and Test

Add to your Claude Desktop config:

json
{
  "mcpServers": {
    "project-manager": {
      "command": "node",
      "args": ["dist/index.js"],
      "env": {
        "API_TOKEN": "your-api-token"
      }
    }
  }
}

Step 5: Error Handling

typescript
server.tool(
  "update-task",
  "Update an existing task",
  {
    taskId: z.number().describe("Task ID"),
    status: z.enum(["open", "in-progress", "done"]).optional(),
    title: z.string().optional(),
  },
  async ({ taskId, status, title }) => {
    try {
      const updated = await updateTask(taskId, { status, title });
      return {
        content: [{
          type: "text",
          text: `Updated task #${taskId} successfully.`,
        }],
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: `Failed to update task #${taskId}: ${error instanceof Error ? error.message : "Unknown error"}`,
        }],
        isError: true,
      };
    }
  }
);

Troubleshooting

  • Server crashes on startup: Check Node.js version and ensure all dependencies are installed
  • Tools not showing in client: Restart the client after configuration changes
  • Authentication errors: Verify API tokens in environment variables
  • Timeout on long operations: Implement progress reporting for long-running tools

Conclusion

MCP servers are a powerful way to give AI models access to your tools and data. By building custom servers, you can create AI workflows that integrate seamlessly with your existing infrastructure.

Next Steps

  • Add authentication and rate limiting
  • Implement caching for frequently accessed resources
  • Build MCP servers for your other internal tools
  • Share your servers with the MCP community
MCPAPIIntegrationTypeScript

Related Articles

Stay Ahead in AI

Get the latest AI tutorials, tools, and news delivered to your inbox every week.

Join 12,000+ AI developers