Nicolas Dabene
Back to blog
04 December 2025 Nicolas Dabene 5 min

MCP Menu: How AI Discovers and Uses Your Tools

The MCP Menu: How AI Discovers and Uses Your Tools

API IA SEO development Tutorial artificial intelligence
MCP Menu: How AI Discovers and Uses Your Tools

The MCP Menu: How AI Discovers and Uses Your Tools

The MCP Menu: How AI Discovers and Uses Your Tools

You created your first readFile tool in the previous article. Congratulations! But imagine arriving at a restaurant without a menu. How would you know what’s available? This is exactly the problem that the MCP discovery system solves. Today, we’ll implement the complete “menu” that allows an AI to automatically discover all your tools and use them intelligently.

Introduction

In my 15 years of API development, I’ve seen many integration systems. But MCP has something elegant: auto-discovery. Rather than hard-coding each integration, the AI queries your server to discover what it can do. It’s as if your API could introduce itself.

This approach changes everything. Instead of having specific connectors for each AI, you create a standard that all MCP-compatible AIs can understand. Once you master this system, you can expose dozens of tools without ever modifying the AI’s code.

Reminder: The Complete Journey of a Request

Before coding, let’s visualize the complete journey of an interaction between an AI and your MCP server. This is crucial to understand where the discovery system fits.

Phase 1: Discovery (what we’ll code today)

You: “Claude, list the files in my projects folder”

Claude: “I don’t know the tools of this server yet. Let me discover them…”

Claude → Server: GET /mcp/tools

Server → Claude: Here are all my available tools with their descriptions

Claude: “Ah! There’s a listFiles tool. That’s exactly what I need.”

Phase 2: Validation (handled by Claude application)

Claude Application → You: “Do you authorize the use of the listFiles tool on the /projects folder?”

You: “Yes, authorized”

Phase 3: Execution

Claude → Server: POST /mcp/execute with {"tool": "listFiles", "params": {"path": "/projects"}}

Server → Claude: Execution result

Claude → You: “Here are the files in your projects folder: …”

Today, we focus on Phase 1: discovery.

The Standard MCP Discovery Format

The MCP protocol defines a standard format for describing your tools. Here’s the JSON structure the AI expects:

<span class="p">{</span><span class="w">
  </span><span class="nl">"protocol_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"server_info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My MCP Server"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MCP server for local file access"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"readFile"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Reads the content of a text file"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"input_schema"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"file_path"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Path to the file to read"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"required"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"file_path"</span><span class="p">]</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

This structure contains three key pieces of information:

protocol_version: Which version of MCP you’re implementing

server_info: Metadata about your server

tools: The complete list of your tools with their input schema

The input_schema uses the JSON Schema standard. It’s like auto-generated documentation that the AI can read and understand.

Creating the Protocol Manager

Let’s start by structuring our MCP protocol implementation. Create the file src/mcp/protocol.ts:

<span class="c1">// src/mcp/protocol.ts</span>

<span class="cm">/**
 * Implemented MCP protocol version
 */</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">MCP_PROTOCOL_VERSION</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">1.0</span><span class="dl">"</span><span class="p">;</span>

<span class="cm">/**
 * Server information
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ServerInfo</span> <span class="p">{</span>
  <span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">version</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">author</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">capabilities</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="p">}</span>

<span class="cm">/**
 * Input schema for a tool (JSON Schema)
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">InputSchema</span> <span class="p">{</span>
  <span class="nl">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">;</span>
  <span class="nl">properties</span><span class="p">:</span> <span class="p">{</span>
    <span class="p">[</span><span class="na">paramName</span><span class="p">:</span> <span class="kr">string</span><span class="p">]:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
      <span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
      <span class="nl">enum</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[];</span>
      <span class="nl">default</span><span class="p">?:</span> <span class="kr">any</span><span class="p">;</span>
    <span class="p">};</span>
  <span class="p">};</span>
  <span class="nl">required</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="p">}</span>

<span class="cm">/**
 * Complete description of an MCP tool
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ToolDescription</span> <span class="p">{</span>
  <span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">input_schema</span><span class="p">:</span> <span class="nx">InputSchema</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/**
 * Complete discovery response
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">DiscoveryResponse</span> <span class="p">{</span>
  <span class="nl">protocol_version</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">server_info</span><span class="p">:</span> <span class="nx">ServerInfo</span><span class="p">;</span>
  <span class="nl">tools</span><span class="p">:</span> <span class="nx">ToolDescription</span><span class="p">[];</span>
<span class="p">}</span>

<span class="cm">/**
 * Our server information
 */</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">SERVER_INFO</span><span class="p">:</span> <span class="nx">ServerInfo</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MCP File Server</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">version</span><span class="p">:</span> <span class="dl">"</span><span class="s2">1.0.0</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MCP server for local file management</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">author</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Nicolas Dabène</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">capabilities</span><span class="p">:</span> <span class="p">[</span>
    <span class="dl">"</span><span class="s2">file_reading</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">directory_listing</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">file_search</span><span class="dl">"</span>
  <span class="p">]</span>
<span class="p">};</span>

These TypeScript types give us a solid structure. Each tool will have to provide a ToolDescription compliant with this format.

Converting Our Tools to MCP Format

Currently, our tools have a simple definition. Let’s transform them to the complete MCP format. Modify src/tools/readFile.ts:

<span class="c1">// src/tools/readFile.ts</span>
<span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">path</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">InputSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../mcp/protocol</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ReadFileParams</span> <span class="p">{</span>
  <span class="nl">file_path</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">encoding</span><span class="p">?:</span> <span class="dl">'</span><span class="s1">utf-8</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">ascii</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">base64</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">readFile</span><span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="nx">ReadFileParams</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">file_path</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The 'file_path' parameter is required</span><span class="dl">"</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">absolutePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">file_path</span><span class="p">);</span>

    <span class="k">try</span> <span class="p">{</span>
      <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">access</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="s2">`File not found: </span><span class="p">${</span><span class="nx">params</span><span class="p">.</span><span class="nx">file_path</span><span class="p">}</span><span class="s2">`</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">stats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">stats</span><span class="p">.</span><span class="nx">isFile</span><span class="p">())</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The specified path is not a file</span><span class="dl">"</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">MAX_FILE_SIZE</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">size</span> <span class="o">></span> <span class="nx">MAX_FILE_SIZE</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="s2">`File too large (max </span><span class="p">${</span><span class="nx">MAX_FILE_SIZE</span> <span class="o">/</span> <span class="mi">1024</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">}</span><span class="s2"> MB)`</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">encoding</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">encoding</span> <span class="o">||</span> <span class="dl">'</span><span class="s1">utf-8</span><span class="dl">'</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">content</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">,</span> <span class="nx">encoding</span><span class="p">);</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="na">content</span><span class="p">:</span> <span class="nx">content</span><span class="p">,</span>
      <span class="na">metadata</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">path</span><span class="p">:</span> <span class="nx">absolutePath</span><span class="p">,</span>
        <span class="na">size</span><span class="p">:</span> <span class="nx">stats</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span>
        <span class="na">encoding</span><span class="p">:</span> <span class="nx">encoding</span><span class="p">,</span>
        <span class="na">lastModified</span><span class="p">:</span> <span class="nx">stats</span><span class="p">.</span><span class="nx">mtime</span>
      <span class="p">}</span>
    <span class="p">};</span>

  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="s2">`Error reading file: </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
    <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Input schema in JSON Schema format
 */</span>
<span class="kd">const</span> <span class="nx">readFileInputSchema</span><span class="p">:</span> <span class="nx">InputSchema</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">file_path</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Absolute or relative path to the file to read</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="na">encoding</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">File encoding</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">enum</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ascii</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">base64</span><span class="dl">"</span><span class="p">],</span>
      <span class="na">default</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">file_path</span><span class="dl">"</span><span class="p">]</span>
<span class="p">};</span>

<span class="cm">/**
 * Complete MCP description of the tool
 */</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">readFileDescription</span><span class="p">:</span> <span class="nx">ToolDescription</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">readFile</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Reads the content of a text file from the local file system. Supports different encodings (UTF-8, ASCII, Base64).</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">input_schema</span><span class="p">:</span> <span class="nx">readFileInputSchema</span>
<span class="p">};</span>

See the difference? We now have:

A formal JSON schema that describes exactly the expected parameters

Enriched metadata like default values and enumerations

A detailed description that helps the AI understand when to use this tool

Let’s do the same for listFiles. Modify src/tools/listFiles.ts:

<span class="c1">// src/tools/listFiles.ts</span>
<span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">fs/promises</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">path</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">InputSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../mcp/protocol</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ListFilesParams</span> <span class="p">{</span>
  <span class="nl">directory_path</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">include_hidden</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">;</span>
  <span class="nl">recursive</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">listFiles</span><span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="nx">ListFilesParams</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">directory_path</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The 'directory_path' parameter is required</span><span class="dl">"</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">absolutePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">directory_path</span><span class="p">);</span>

    <span class="kd">const</span> <span class="nx">stats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">stats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">())</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The specified path is not a directory</span><span class="dl">"</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="c1">// Read directory content</span>
    <span class="kd">let</span> <span class="nx">files</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readdir</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">);</span>

    <span class="c1">// Filter hidden files if necessary</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">params</span><span class="p">.</span><span class="nx">include_hidden</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">files</span> <span class="o">=</span> <span class="nx">files</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">file</span> <span class="o">=></span> <span class="o">!</span><span class="nx">file</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="dl">'</span><span class="s1">.</span><span class="dl">'</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="c1">// Get details for each file</span>
    <span class="kd">const</span> <span class="nx">filesWithDetails</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span>
      <span class="nx">files</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">absolutePath</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">fileStats</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="nx">filePath</span><span class="p">);</span>

        <span class="k">return</span> <span class="p">{</span>
          <span class="na">name</span><span class="p">:</span> <span class="nx">file</span><span class="p">,</span>
          <span class="na">type</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">()</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">directory</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">file</span><span class="dl">'</span><span class="p">,</span>
          <span class="na">size</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span>
          <span class="na">lastModified</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">mtime</span><span class="p">,</span>
          <span class="na">permissions</span><span class="p">:</span> <span class="nx">fileStats</span><span class="p">.</span><span class="nx">mode</span>
        <span class="p">};</span>
      <span class="p">})</span>
    <span class="p">);</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="na">content</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">filesWithDetails</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
      <span class="na">metadata</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">path</span><span class="p">:</span> <span class="nx">absolutePath</span><span class="p">,</span>
        <span class="na">count</span><span class="p">:</span> <span class="nx">filesWithDetails</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
        <span class="na">include_hidden</span><span class="p">:</span> <span class="nx">params</span><span class="p">.</span><span class="nx">include_hidden</span> <span class="o">||</span> <span class="kc">false</span>
      <span class="p">}</span>
    <span class="p">};</span>

  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="s2">`Error reading directory: </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
    <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">listFilesInputSchema</span><span class="p">:</span> <span class="nx">InputSchema</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">directory_path</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Absolute or relative path to the directory to list</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="na">include_hidden</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Include hidden files (starting with .)</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">default</span><span class="p">:</span> <span class="kc">false</span>
    <span class="p">},</span>
    <span class="na">recursive</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">List subdirectories recursively</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">default</span><span class="p">:</span> <span class="kc">false</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">directory_path</span><span class="dl">"</span><span class="p">]</span>
<span class="p">};</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">listFilesDescription</span><span class="p">:</span> <span class="nx">ToolDescription</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">listFiles</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Lists files and folders in a given directory. Can include hidden files and support recursion.</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">input_schema</span><span class="p">:</span> <span class="nx">listFilesInputSchema</span>
<span class="p">};</span>

Creating the Centralized Tool Registry

Now, let’s create a registry that gathers all our tools. Create src/mcp/registry.ts:

<span class="c1">// src/mcp/registry.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ToolResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../types/mcp</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">readFile</span><span class="p">,</span> <span class="nx">readFileDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../tools/readFile</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">listFiles</span><span class="p">,</span> <span class="nx">listFilesDescription</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../tools/listFiles</span><span class="dl">'</span><span class="p">;</span>

<span class="cm">/**
 * Type for a tool function
 */</span>
<span class="kd">type</span> <span class="nx">ToolFunction</span> <span class="o">=</span> <span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span><span class="p">;</span>

<span class="cm">/**
 * Central registry of all available tools
 */</span>
<span class="kd">class</span> <span class="nx">ToolRegistry</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">tools</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">ToolFunction</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>
  <span class="k">private</span> <span class="nx">descriptions</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">ToolDescription</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>

  <span class="cm">/**
   * Register a new tool
   */</span>
  <span class="nx">register</span><span class="p">(</span><span class="nx">description</span><span class="p">:</span> <span class="nx">ToolDescription</span><span class="p">,</span> <span class="nx">implementation</span><span class="p">:</span> <span class="nx">ToolFunction</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">implementation</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">description</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Tool registered: </span><span class="p">${</span><span class="nx">description</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Get all available tools
   */</span>
  <span class="nx">getAllDescriptions</span><span class="p">():</span> <span class="nx">ToolDescription</span><span class="p">[]</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="nx">values</span><span class="p">());</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Get the description of a specific tool
   */</span>
  <span class="nx">getDescription</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">ToolDescription</span> <span class="o">|</span> <span class="kc">undefined</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">descriptions</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Execute a tool
   */</span>
  <span class="k">async</span> <span class="nx">execute</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">ToolResponse</span><span class="o">></span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">tool</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">tool</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="s2">`Tool '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' not found. Available tools: </span><span class="p">${</span><span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">keys</span><span class="p">()).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">`</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="k">try</span> <span class="p">{</span>
      <span class="k">return</span> <span class="k">await</span> <span class="nx">tool</span><span class="p">(</span><span class="nx">params</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="s2">`Error executing '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">': </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
      <span class="p">};</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Check if a tool exists
   */</span>
  <span class="nx">has</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Get the number of registered tools
   */</span>
  <span class="nx">count</span><span class="p">():</span> <span class="kr">number</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">size</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Singleton instance of the registry</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">toolRegistry</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ToolRegistry</span><span class="p">();</span>

<span class="c1">// Register all our tools at startup</span>
<span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="nx">readFileDescription</span><span class="p">,</span> <span class="nx">readFile</span><span class="p">);</span>
<span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="nx">listFilesDescription</span><span class="p">,</span> <span class="nx">listFiles</span><span class="p">);</span>

This registry is the heart of our system. It’s the one that:

Maintains the list of all available tools

Manages tool execution in a unified way

Provides descriptions for discovery

Handles errors in a centralized manner

Implementing MCP Endpoints

Now let’s modify our Express server to implement standard MCP endpoints. Replace src/index.ts:

<span class="c1">// src/index.ts</span>
<span class="k">import</span> <span class="nx">express</span><span class="p">,</span> <span class="p">{</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">Response</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span> <span class="nx">SERVER_INFO</span><span class="p">,</span> <span class="nx">DiscoveryResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">toolRegistry</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/registry</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">PORT</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>

<span class="c1">// Middleware</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>

<span class="c1">// Logging middleware</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`[</span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()}</span><span class="s2">] </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">method</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">path</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">next</span><span class="p">();</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// STANDARD MCP ROUTES</span>
<span class="c1">// ============================================</span>

<span class="cm">/**
 * Root endpoint - Server information
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">MCP File Server</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">version</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">.</span><span class="nx">version</span><span class="p">,</span>
    <span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
    <span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">operational</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">endpoints</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">discovery</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">execute</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">health</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/health</span><span class="dl">'</span>
    <span class="p">}</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="cm">/**
 * Discovery endpoint - The complete "menu"
 * This is where the AI discovers all your tools
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="na">response</span><span class="p">:</span> <span class="nx">DiscoveryResponse</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
    <span class="na">server_info</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">,</span>
    <span class="na">tools</span><span class="p">:</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getAllDescriptions</span><span class="p">()</span>
  <span class="p">};</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Discovery requested - </span><span class="p">${</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">()}</span><span class="s2"> available tools`</span><span class="p">);</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
<span class="p">});</span>

<span class="cm">/**
 * Unified execution endpoint
 * Format: POST /mcp/execute
 * Body: { "tool": "toolName", "params": {...} }
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>

  <span class="c1">// Validation</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">tool</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The 'tool' parameter is required</span><span class="dl">"</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️  Execution requested: </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>

  <span class="c1">// Execution via registry</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span> <span class="o">||</span> <span class="p">{});</span>

  <span class="c1">// Log result</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Execution successful: </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`❌ Execution failed: </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
<span class="p">});</span>

<span class="cm">/**
 * Endpoint for specific tool description
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/tools/:toolName</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">toolName</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">description</span> <span class="o">=</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getDescription</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">description</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">404</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="s2">`Tool '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">' not found`</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">description</span><span class="p">);</span>
<span class="p">});</span>

<span class="cm">/**
 * Health check
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/health</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">healthy</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">uptime</span><span class="p">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">uptime</span><span class="p">(),</span>
    <span class="na">tools_count</span><span class="p">:</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">(),</span>
    <span class="na">timestamp</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// BACKWARD COMPATIBILITY (optional)</span>
<span class="c1">// ============================================</span>

<span class="cm">/**
 * Old direct endpoint (for quick tests)
 * @deprecated Use /mcp/execute instead
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/tools/:toolName</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">toolName</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚠️  Using old endpoint /tools/</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2"> (deprecated)`</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="nx">toolName</span><span class="p">,</span> <span class="nx">params</span><span class="p">);</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// SERVER STARTUP</span>
<span class="c1">// ============================================</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🚀 MCP File Server</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📍 URL: http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Discovery: http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/tools`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️  Execution: POST http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/execute`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`🔧 Available tools: </span><span class="p">${</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">()}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

Our server now implements the complete MCP protocol with three main endpoints:

GET /mcp/tools: Discovery of all tools

POST /mcp/execute: Unified execution of any tool

GET /mcp/tools/:toolName: Details of a specific tool

Testing the Discovery System

Let’s restart our server and test the complete system:

npm run dev

You should see:

═══════════════════════════════════════
🚀 MCP File Server
═══════════════════════════════════════
📍 URL: http://localhost:3000
📋 Discovery: http://localhost:3000/mcp/tools
⚙️  Execution: POST http://localhost:3000/mcp/execute
🔧 Available tools: 2
═══════════════════════════════════════
✅ Tool registered: readFile
✅ Tool registered: listFiles

Test 1: Complete Discovery

curl http://localhost:3000/mcp/tools | json_pp

Perfect! The AI can now discover all your tools with their complete descriptions.

Test 2: Execution via Unified Endpoint

curl <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{
    "tool": "readFile",
    "params": {
      "file_path": "test.txt"
    }
  }'</span>

Understanding the Architectural Impact

This discovery system fundamentally changes the architecture of AI integrations:

Before MCP: Rigid Integrations

Each AI had to be specifically programmed for each tool:

<span class="c1">// Code in the AI to integrate a specific tool</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">userWantsToReadFile</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">callReadFileAPI</span><span class="p">(</span><span class="nx">userParams</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userWantsToListFiles</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">callListFilesAPI</span><span class="p">(</span><span class="nx">userParams</span><span class="p">);</span>
<span class="p">}</span>

After MCP: Auto-Discovery

The AI dynamically discovers capabilities:

<span class="c1">// The AI can now discover and use any tool</span>
<span class="kd">const</span> <span class="nx">tools</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">discoverTools</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">tool</span> <span class="o">=</span> <span class="nx">tools</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="nx">matchesUserRequest</span><span class="p">(</span><span class="nx">t</span><span class="p">));</span>
<span class="k">await</span> <span class="nx">executeTool</span><span class="p">(</span><span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">userParams</span><span class="p">);</span>

Conclusion: A New Paradigm

The MCP discovery system represents a paradigm shift in AI integration. Instead of creating specific connectors for each use case, you create standardized “building blocks” that all compatible AIs can dynamically assemble.

In this article, we saw how to:

  • Implement the complete MCP protocol with discovery and execution
  • Create a centralized tool registry
  • Structure tools with JSON Schema
  • Test the system with HTTP clients
  • Simulate complete AI-server interaction

The next step? Connect your MCP server to a real AI like Claude Desktop. You’ll then see the magic happen: your AI will be able to actually read your files, list your folders, and use all your custom tools.

MCP is not just a technical protocol: it’s a new way of thinking about human-AI integration. Your tools become natural extensions of AI capabilities, without technical friction.


Article published on November 12, 2025 by Nicolas Dabène - PHP & PrestaShop Expert with 15+ years of experience in software architecture and AI integration

Also read:

LinkedIn

Follow my AI and e-commerce analysis

I share practical notes on AI agents, PrestaShop architecture, MCP and automation for e-commerce teams.

Follow on LinkedIn