DEV Community

Đức Tài
Đức Tài

Posted on

Create your first MCP server using the MCP Framework. Guide to testing/debugging with MCP Inspector.

Introduction

Hi everyone, recently I’ve seen people talking about MCP (Model Context Protocol) everywhere. From AI assistants and tool-calling to extensions, MCP keeps popping up 😐.

Since I’m currently learning it too, I want to share how to build your first MCP server using MCP Framework. After that, we’ll test it with MCP Inspector, then finally add it to an MCP host — in this case, Copilot.

Goals of this article:

  • Understand MCP at its core: what it is, what problem it solves, and how its architecture works.
  • Build a practical MCP server where, when a user asks for the current gold price, the Agent calls our MCP tool and returns a formatted response.

Implementation steps:
1) Why MCP was created
2) Build an MCP server with MCP Framework
3) Create a GoldPriceTool to fetch gold prices
4) Test/Debug with MCP Inspector
5) Set up Add MCP Server in Visual Studio Code


1) Why MCP was created

Before using MCP servers from large ecosystems (Docker MCP, Figma MCP, Chrome DevTools MCP, ...), we should understand what MCP was designed to solve. Once you truly understand “why MCP exists,” implementation becomes much easier and more reliable.

Before MCP

If each AI Agent (M) wants to connect to each tool/app (N), you must build separate integrations, resulting in a complex $M \times N$ connection matrix.


beforeMCP.png


For example, with 5 AIs and 100 apps, you’d need 5x100 = 500 separate integrations. That’s painful — and whenever a tool changes its integration method, you have to update everything 😫😫

After MCP

Now MCP acts as a shared gateway where AI Agents (M) and tools/apps (N) each connect once, and MCP handles the interoperability layer. We only need $M + N$ connections instead of $M \times N$.

aftermcp.png

With 5 AIs and 100 apps, you only need 5 + 100 = 105 connections — much easier to manage 😎

MCP Architecture

MCP follows a client-server model. The MCP server exposes APIs for AI Agents to call when they need tools. The MCP server manages tools, receives requests from Agents, executes tools, and returns results.

kientrucmcp.png

That’s enough theory — let’s build our first MCP server to fetch gold prices 😁


2) Build an MCP server with MCP Framework

Initialize the project

# install MCP Framework
npm install -g mcp-framework
# create a new project
mcp create my-mcp-server
# enter the project directory
cd my-mcp-server
Enter fullscreen mode Exit fullscreen mode

After installation, the project structure will look like this:

mcp-server-server/
├─ src/
│  ├─ index.ts
│  └─ tools/
│     └─ ExampleTool.ts
├─ dist/
│  ├─ index.js
│  └─ tools/
│     └─ ExampleTool.js
├─ node_modules/
└─ ...
Enter fullscreen mode Exit fullscreen mode
  • src/index.ts: MCP server entry point, where server.start() is called.
  • src/tools/ExampleTool.ts: sample tool showing the name, schema, execute pattern.
  • package.json: build/run scripts and project dependencies.
  • dist/: compiled code; Inspector/Copilot runs from here.

Let’s edit ExampleTool at:
src/tools/ExampleTool.ts.

import { MCPTool } from "mcp-framework";
import { z } from "zod";

interface ExampleInput {
  message: string;
}

class ExampleTool extends MCPTool<ExampleInput> {
  name = "example_tool";
  description = "An example tool that processes messages";

  schema = {
    message: {
      type: z.string(),
      description: "Message to process",
    },
  };

  async execute(input: ExampleInput) {
    return `Processed: ${input.message}`;
  }
}

export default ExampleTool;

Enter fullscreen mode Exit fullscreen mode

Quick explanation of the code above:

  • MCPTool<ExampleInput>: declares this as an MCP tool and defines the input type.
  • name: tool identifier; it appears in tools/list.
  • schema: defines input data (here, message as a string).
  • execute(...): core logic; receives input and returns output to the client.
  • export default: lets the framework auto-load tools from src/tools.

Now we have a basic MCP server with a sample tool. Next, we’ll create a real tool to fetch gold prices.


3) Create GoldPriceTool to fetch gold prices

Create the tool with CLI

mcp add tool GoldPriceTool
Enter fullscreen mode Exit fullscreen mode

The framework will generate the tool file in src/tools/.

Update GoldPriceTool.ts to:

import { MCPTool } from 'mcp-framework'
import { z } from 'zod'

interface Input {
  message: string
}

type ApiRes = {
  success: boolean
  name: string
  buy: number
  sell: number
  change_buy: number
  change_sell: number
  time: string
  date: string
}

class GoldpriceTool extends MCPTool<Input> {
  // Tool name shown in MCP Inspector
  name = 'GoldpriceTool'
  description = 'Get current gold price'
  protected useStringify = false

  schema = {
    message: { type: z.string(), description: 'Input can be any text' },
  }

  private vnd = (n: number) => `${n.toLocaleString('vi-VN')} ₫`
  private trend = (n: number) =>
    n > 0
      ? `up ${this.vnd(n)}`
      : n < 0
        ? `down ${this.vnd(Math.abs(n))}`
        : 'no change'

  async execute(_input: Input) {
    // Call SJC 9999 gold price API
    const r = await fetch('https://www.vang.today/api/prices?type=SJL1L10')
    if (!r.ok) throw new Error(`HTTP ${r.status}`)
    const d = (await r.json()) as ApiRes
    if (!d.success) throw new Error('API request failed')

    const buyTrendIcon =
      d.change_buy > 0 ? '📈' : d.change_buy < 0 ? '📉' : ''
    const sellTrendIcon =
      d.change_sell > 0 ? '📈' : d.change_sell < 0 ? '📉' : ''

    // Return formatted result
    return [
      `✨ ${d.name}`,
      `💰 Buy price: ${this.vnd(d.buy)}`,
      `🏷️ Sell price: ${this.vnd(d.sell)}`,
      `${buyTrendIcon} Buy change: ${this.trend(d.change_buy)}`,
      `${sellTrendIcon} Sell change: ${this.trend(d.change_sell)}`,
      `🕒 Time: ${d.time} ${d.date}`,
    ].join('\n')
  }

  protected createErrorResponse(error: Error) {
    return {
      content: [
        { type: 'text' as const, text: `Error fetching gold price: ${error.message}` },
      ],
    }
  }
}

export default GoldpriceTool
Enter fullscreen mode Exit fullscreen mode

After updating the tool, rebuild the project so dist/ has the latest code:

npm run build
Enter fullscreen mode Exit fullscreen mode

Run locally

node dist/index.js
Enter fullscreen mode Exit fullscreen mode

If you see logs like this:

[2026-03-04T14:48:13.579Z] [INFO] Initializing MCP Server: my-mcp-server@0.0.1
[2026-03-04T14:48:13.582Z] [INFO] Starting MCP server: (Framework: 0.2.18, SDK: 1.27.1)...
[2026-03-04T14:48:13.591Z] [INFO] Capabilities detected: {"tools":{}}
[2026-03-04T14:48:13.597Z] [INFO] Connecting transport (stdio) to SDK Server...
[2026-03-04T14:48:13.598Z] [INFO] Started my-mcp-server@0.0.1 successfully on transport stdio
[2026-03-04T14:48:13.598Z] [INFO] Tools (2): example_tool, GoldpriceTool
[2026-03-04T14:48:13.599Z] [INFO] Server running and ready
Enter fullscreen mode Exit fullscreen mode

At this point, you can already call the tool from an AI Agent such as Copilot Chat. But for easier testing and tool inspection, we’ll use MCP Inspector next. 😁


4) Test/Debug with MCP Inspector

Quick intro: this is the official tool from the Model Context Protocol ecosystem (repo modelcontextprotocol/inspector), and it’s very convenient for testing/debugging MCP servers. Inspector provides a UI to call tools, view input/output, track history, and debug errors in real time.

Note: after filling Command and Arguments, click Connect (if the status stays Disconnected, Inspector cannot reach your server yet).

anhmcpp.png

How to run Inspector

There are several ways; the fastest is using the official package:

npx @modelcontextprotocol/inspector
Enter fullscreen mode Exit fullscreen mode

In the Inspector UI, select:

  • Transport: STDIO
  • Command: node (the project runs on Node.js)
  • Arguments: path to dist/index.js

Example on Windows:

D:/TongHopProject/my-mcp-server/dist/index.js
Enter fullscreen mode Exit fullscreen mode

After a successful connection, click List Tools to verify tools available on the server. If you see GoldpriceTool, this step is done.

anh333.png

Next, test the tool by clicking GoldpriceTool, entering any prompt into message, then clicking Run Tool.
anh44.png

Ta-da, the returned result matches exactly what we designed 😍

Next, we’ll add the server to an MCP Host (here, Copilot Chat) so this tool can be called directly.


5) Set up Add MCP Server in Visual Studio Code

At this step, the fastest way is to add the server directly via Command Palette:

  1. Press Ctrl + Shift + P.
  2. Type > MCP: Add Server....
  3. Choose Command (stdio).
  4. In the Command field, enter: node.
  5. Enter a server name, for example: GoldpriceServer.
  6. Choose either Global or Workspace config

After these steps, open mcp.json. If saved as global config, it is located at C:\Users\ADMIN\AppData\Roaming\Code\User\mcp.json; if saved as workspace config, it is in your project root. The content should look similar to:

{
        "GoldpriceServer": {
            "type": "stdio",
            "command": "Node",
            "args": []
        }
}
Enter fullscreen mode Exit fullscreen mode

Here, add the dist/index.js path to args like this:

{
    "GoldpriceServer": {
      "type": "stdio",
      "command": "Node",
      "args": ["D:/TongHopProject/my-mcp-server/dist/index.js"]
    }
}
Enter fullscreen mode Exit fullscreen mode

Then:

  1. Reload VS Code window.
  2. Open Copilot Chat and select any model in Agent mode.
  3. Enter prompt: “use MCP GoldpriceTool to return the current gold price, keep the response short.”

Common issues

  • Tool returns Unknown tool → incorrect name or server not restarted.
  • Invalid tools/call result → response does not follow MCP content schema.
  • Extra trailing commas in saved JSON → caused by JSONC formatter; adjust settings if needed.
  • After rebuilding, restart Inspector/Copilot session to load latest version.

Conclusion

Now you have the full flow: build MCP server → create tool → test with Inspector → add to mcp.json so Copilot can call it.

No enterprise drama needed — if you can run it, debug it, and understand the request flow, you’re already ahead of many beginners 👏

SourceCode

Contact me

References

Top comments (0)