Building Your First MCP Server: A Beginner’s Guide



This content originally appeared on DEV Community and was authored by Kevin Toshihiro Uehara

Hi Folks! Are you all right? everything in peace? Everything calm? I hope you are well!

It’s been a while since I’ve posted anything here, but I’m back!
In this article I want to introduce the MCP (Model Context Protocol) and how to implement you own MCP Server from scratch using Typescript.

If you ask for “Give me 5 articles most rated of this week” in your AI Agent, probably it will not be able to bring that information, because it does not have access to this data. Probably it will suggest to create an integration with the API of dev.to, using some language. But this is not what we want. We want that the our AI agent bring the “5 articles of the week” after the prompt. Now the MCP Server enters.

In this tutorial, you’ll create a dev.to articles server that connects AI agents like Cursor or Github Copilot. We will use TypeScript for this demo but you can build MCP servers in other languages. By the end, you’ll be able to ask your AI for articles of dev.to searching by authors, recents posts, most rated and so on.

What is a MCP Server?

Model Context Protocol (MCP) servers are bridges that connect AI agents to external tools and data sources. Think of them as translators that help AI understand and interact with real-world applications.

MCP is an open protocol, created by Anthropic, that standardizes how applications provide context to large language models (LLMs). Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools. MCP enables you to build agents and complex workflows on top of LLMs and connects your models with the world.

MCP Example Diagram

You can connect your MCP Server to Github to bring the PR’s of some repository, or schedule a meet using the Google Calendar, for example.

In this example we going to integrate our MCP Server to Dev.TO API to bring the articles real-time information. So let’s do it!

Project Setup

Let’s create a new project and set up our development environment.

First, create a new directory and initialize it with npm:

mkdir mcp-devto-example
cd mcp-devto-example
npm init -y

Now, let’s create the entrypoint main file:

touch main.ts

Open the project in VS Code (or your preferred editor. In this example I’m using Cursor) and modify package.json to enable ES modules by adding the type field:

{
  "name": "mcp-devto-example",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

Installing Dependencies

Our MCP server needs two key libraries:

  • @modelcontextprotocol/sdk (the SDK to create the MCP)
  • zod (ensures our server receives valid data from AI agents, using type safe validation)
npm install @modelcontextprotocol/sdk zod

Building the Server

Now let’s create our MCP server. Open main.ts and let’s build it step by step.

Let’s add the import of our dependencies:

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

Now creating the server instance:

const server = new McpServer({
  name: "articles-mcp-example",
  version: "1.0.0",
});

The server manages all communication using the MCP protocol between clients (like VS Code or Cursor) and your tools.

Defining your first tool

Tools are functions that AI agents can call. Let’s create a get-articles tool:

server.tool(
  'get-articles',
  'Tool to get articles from dev.to',
  {
    tag: z.string().describe("The tag to search on dev.to api")
  },
  async({ tag }) => {
    // For now, return a simple static response
    return {
      content: [
        {
          type: "text",
          text: `Articles of tag:${tag}`
        }
      ]
    };
  }
);

Breaking down the tool definition:

  • Tool ID: ‘get-articles’ – Unique identifier
  • Description: Helps AI agents understand what this tool does
  • Schema: Defines parameters (tag must be a string)
  • Function: The actual code that runs when called

How it works:

  1. AI agent sees: “Tool to get articles from dev.to”
  2. AI agent calls it with: { tag: “javascript” }
  3. Zod validates the input
  4. Function returns: “Articles of tag:javascript”

Finally, we need to set up how our server communicates with AI clients:

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

What’s happening here:

  • StdioServerTransport uses your terminal’s input/output for communication
  • Perfect for local development
  • The server reads requests from stdin and writes responses to stdout
  • MCP protocol handles all the message formatting automatically

Your complete main.ts should now look like this:

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: "articles-mcp-example",
  version: "1.0.0",
});

server.tool(
  'get-articles',
  'Tool to get articles from dev.to',
  {
    tag: z.string().describe("The tag to search on dev.to api")
  },
  async({ tag }) => {
    // For now, return a simple static response
    return {
      content: [
        {
          type: "text",
          text: `Articles of tag:${tag}`
        }
      ]
    };
  }
);

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

🎉 Congratulations! You’ve built your first MCP server. Let’s test it!

Testing with MCP Inspector

Before adding real weather data, let’s test our server using the MCP Inspector, a web-based debugging tool for MCP servers.

Launch the Inspector with the command to open the MCP Inspector for your server:

npx -y @modelcontextprotocol/inspector npx -y tsx main.ts 

or you can add in your package.json scripts:

"scripts": {
    "inspect": "npx -y @modelcontextprotocol/inspector npx -y tsx src/main.ts"
  },

And running with:

npm run inspect

After running the command, you’ll see terminal output with:

  • A localhost URL (like http://127.0.0.1:6274)
  • A unique session token
  • A direct link with the token pre-filled

MCP Inspector

Connecting and Test

  1. Connect: Click the “Connect” button in the Inspector
  2. Navigate: Click “Tools” in the top navigation
  3. Select: Choose your get-articles tool
  4. Test: Enter a tag (like “javascript”) and click “Run Tool”

You should see the response: “Articles of tag:javascript”

MCP inspector With Article Response

Integrating with Dev.To API

Time to make our server actually useful! We’ll integrate with dev.to API, API of dev.to that requires no API key.

Updating the tools

For better organization I’m going to separate the integration and types of zod in another files.

Let’s create the the file types.ts:

touch types.ts

And now let’s add the zod schemas:

import { z } from "zod";

export const getArticlesSchema = z.object({
  tags: z.string().describe("The tag to search on dev.to api"),
  quantity: z
    .number()
    .optional()
    .default(5)
    .describe("The number of articles to return"),
});

export const getArticlesByUserNameSchema = z.object({
  userName: z.string().describe("The user name to search on dev.to api"),
});

And for the integrations with the API, I’m going to create the service.ts file:

touch service.ts

Let’s add two functions, fist to get the articles by tag and the second to get the articles by username:

const BASE_URL = "https://dev.to/api";

export const getArticles = async (tags: string, quantity: number) => {
  try {
    const response = await fetch(
      `${BASE_URL}/articles?tag=${tags}&per_page=${quantity}`
    );
    const data = await response.json();

    return {
      content: [
        {
          type: "text" as const,
          text: JSON.stringify(data, null, 2),
        },
      ],
    };
  } catch (error) {
    return {
      content: [
        {
          type: "text" as const,
          text: `Error fetching articles data: ${
            error instanceof Error ? error.message : "Unknown error"
          }`,
        },
      ],
    };
  }
};

export const getArticlesByUserName = async (userName: string) => {
  try {
    const response = await fetch(`${BASE_URL}/articles?username=${userName}`);
    const data = await response.json();

    return {
      content: [
        {
          type: "text" as const,
          text: JSON.stringify(data, null, 2),
        },
      ],
    };
  } catch (error) {
    return {
      content: [
        {
          type: "text" as const,
          text: `Error fetching articles data: ${
            error instanceof Error ? error.message : "Unknown error"
          }`,
        },
      ],
    };
  }
};

Now on main.ts, I will add two tools and use the files that we created:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { getArticlesByUserNameSchema, getArticlesSchema } from "./types";
import { getArticles, getArticlesByUserName } from "./service";

const server = new McpServer({
  name: "articles-mcp-example",
  version: "1.0.0",
});

server.tool(
  "get-articles",
  "Tool to get articles from dev.to",
  getArticlesSchema.shape,
  async ({ tags, quantity }) => getArticles(tags, quantity)
);

server.tool(
  "get-articles-by-user-name",
  "Tool to get articles from dev.to by user name",
  getArticlesByUserNameSchema.shape,
  async ({ userName }) => getArticlesByUserName(userName)
);

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

Testing the Real Data

  1. Restart your MCP Inspector (Ctrl+C, then re-run the command)
  2. Reconnect in the web interface
  3. Test with a tag like “javascript” or “react” on get-articles or username like “kevin-uehara” on get-articles-by-user-name

get-articles

MCP Inspector with integration

get-articles-by-user-name

MCP Inspector with integration

Integration with Cursor

Now let’s connect your articles dev.to server to Cursor:

Create the directory: .cursor/mcp.json with the following code:

{
    "mcpServers": {
      "articles-mcp-example": {
        "command": "npx",
        "args": ["tsx", "src/main.ts"],
        "env": {}
      }
    }
  }

If you are using the VSCode with Copilot you need to create the directory .vscode/mcp.json.

Using the Cursor you need to register the server. Type CMD + P and type > View: Open MCP Settings and enable the mcp server created.

Cursor MCP Settings

Open the Cursor Chat with CMD + I and type: “what is the 5 most rated articles of dev.to of this week?”

Cursor Chat

And that’s it!!!

Why this is powerful?

Your articles dev.to server demonstrates the true power of MCP:

🤖 AI Does the Heavy Lifting

  • You provide raw data, AI creates beautiful presentations
  • No need to format responses, the AI handles user experience

🔗 Universal Compatibility

  • Works with any MCP-compatible tool (VS Code, Cursor, Claude, etc.)
  • Write once, use everywhere

⚡ Real-time Integration

  • Always current data, no caching issues
  • Works seamlessly within your development environment

Conclusion

🎉 Congratulations! You’ve successfully built your first MCP articles server!

What You’ve Accomplished:

✅ Created a functional MCP server from scratch
✅ Integrated real-time weather data from an external API
✅ Connected it to Cursor
✅ Learned the fundamentals of the Model Context Protocol

Happy building! 🚀

📚 Resources and Further Reading

Official Documentation

Model Context Protocol Documentation – Complete MCP reference
Typescript SDK

APIs Used in This Tutorial

Dev.TO API – Dev.TO API data service
Zod Documentation – TypeScript schema validation

Demo Repo

Demo available on GitHub

Thank you very much for reading this far and stay well always!

Thats all folks

Contacts:

Linkedin: https://www.linkedin.com/in/kevin-uehara/
Instagram: https://www.instagram.com/uehara_kevin/
Twitter: https://x.com/ueharaDev
Github: https://github.com/kevinuehara
dev.to: https://dev.to/kevin-uehara
Youtube: https://www.youtube.com/@ueharakevin/


This content originally appeared on DEV Community and was authored by Kevin Toshihiro Uehara