Skip to content

Custom MCP Servers

If you are interested in building your own custom MCP server, you can follow these steps to get started.

Requirements

Getting Started

First, you will need to create a new MCP server project. This can be done using the mcp-framework. We recommend using this package as it provides pretty much everything you need to get started.

To get started, install the mcp-framework package:

npm install -g mcp-framework

Then, create a new project:

mcp create my-mcp-server
cd my-mcp-server

Next, you will need to create your first tool. For this example, we will create a simple weather tool that returns the current weather for a given location.

mcp add tool weather

This will create a new file called src/tools/WeatherTool.ts. Let's see what it looks like:

import { MCPTool } from "mcp-framework";
import { z } from "zod";
 
interface WeatherInput {
  message: string;
}
 
class WeatherTool extends MCPTool<WeatherInput> {
  name = "weather";
  description = "Weather tool description";
 
  schema = {
    message: {
      type: z.string(),
      description: "Message to process",
    },
  };
 
  async execute(input: WeatherInput) {
    return `Processed: ${input.message}`;
  }
 
}
export default WeatherTool;

Let's break down what's happening here:

  1. We have an interface for the input of the tool. These will be the parameters that the agent will pass to the tool when calling it.

  2. We have the metadata for the tool. This includes a name and description, very important for the agents to know what the tool actually does.

  3. We define the schema for the tool. This should match your input interface, and will be used to validate the input + give context on what the parameters are used for.

  4. We define the actual tool logic. This is where the magic happens. The execute method will be called by the agent when it calls the tool.

Now we can edit this tool to actually do something useful. Let's first change the WeatherInput interface to include a city parameter, and update the schema to match:

import { MCPTool } from "mcp-framework";
import { z } from "zod";
 
interface WeatherInput {
  city: string; 
}
 
class WeatherTool extends MCPTool<WeatherInput> {
  name = "weather";
  description = "Weather tool description";
 
  schema = { 
    city: { 
      type: z.string(), 
      description: "City to fetch current weather", 
    } 
  } 
 
  async execute(input: WeatherInput) {
    return `Processed: ${input.message}`;
  }
}
export default WeatherTool;

This will allow the agent to pass a city parameter to the tool, and the tool will use that to fetch the weather for that location.

Now let's update the execute method to actually fetch the weather for the location. In our example, we will use a mock API call, you'll have to replace this with a real one.

async execute({ city }: WeatherInput) {
  console.log(`Fetching weather for ${city}`);
  const response = await fetch(`https://api.weather.com/v1/weather?city=${city}`);
  const data = await response.json();
  return {
    city,
    temperature: {
      f: data.current.temp_f,
      c: data.current.temp_c,
    },
    condition: data.current.condition,
    humidity: data.current.humidity,
  };
}

Great, we now have a working weather tool. Let's setup our MCP server file to use the HTTP Streaming transport and session management.

In the src/index.ts file, add the following code:

import { MCPServer } from "mcp-framework";
 
const server = new MCPServer({
  transport: {
    type: "http-stream",
    options: {
      session: {
        enabled: true,
        headerName: "mcp-session-id",
        allowClientTermination: true,
      },
      responseMode: "batch",
      port: parseInt(process.env.PORT || "1337"),
      cors: {
        allowOrigin: "*",
      },
    },
  },
});
 
server.start();

This will start the server with the proper HTTP transport configuration.

Authentication

API Key authentication is required to integrate with the Hi.xyz platform. To add authentication to your MCP server, follow these steps.

In a file called src/auth.ts, add the following code:

import { APIKeyAuthProvider } from "@modelcontextprotocol/mcp-framework";
 
const authProvider = new APIKeyAuthProvider({
  keys: [`YOUR_API_KEY`],
  headerName: "X-API-Key",
})
 
export default authProvider;

Replace YOUR_API_KEY with your MCP server API Key from the Developer Dashboard.

Next, update the src/index.ts file to use the auth provider:

import { MCPServer } from "mcp-framework";
import authProvider from "./auth"; 
 
const server = new MCPServer({
  transport: {
    type: "http-stream",
    options: {
      session: {
        enabled: true,
        headerName: "mcp-session-id",
        allowClientTermination: true,
      },
      auth: { 
        provider: authProvider, 
        endpoints: { 

          sse: true, 
          messages: true, 
        }, 
      }, 
      responseMode: "batch",
      port: parseInt(process.env.PORT || "1337"),
      cors: {
        allowOrigin: "*",
      },
    },
  },
});
 
server.start();

This will add the proper authentication configuration to the server. Now when the hi.xyz platform requests to the MCP server, it will use the API key to authenticate and prove that it's coming from us.

Your API key from the MCP dashboard MUST be kept secret and never shared with anyone.

How the API Key is Used

When a custom MCP server is integrated with the Hi.xyz platform, the API key is only used by the hi.xyz backend to authenticate with the MCP server.

Building the Server

Now that we have our server configured, we can start building the server. Run the following command to build:

# Using pnpm (recommended)
pnpm build
# Using npm
npm run build
# Using yarn
yarn build

This will build the server and output the compiled code to the dist folder.

Use the start script to start the server after building.

To run the server locally in watch mode, run the following command:

# Using pnpm (recommended)
pnpm watch
# Using npm
npm run watch
# Using yarn
yarn watch

This will start the server in watch mode, which will automatically rebuild the server when changes are made to the source code. Useful for local development.

Testing the Server

To test the server, you can use the @modelcontextprotocol/inspector tool. This tool allows you to debug your MCP server and interact with in a browser UI.

After building the server, run the following command to start the inspector:

# With pnpm
pnpm dlx @modelcontextprotocol/inspector ./dist/index.js
# With npm
npx @modelcontextprotocol/inspector ./dist/index.js

This will start the inspector and open a browser window with the debug UI.