Creating an MCP Server Using TypeScript - CloudFronts

Creating an MCP Server Using TypeScript

Posted On August 15, 2025 by Suchit Chaudhari Posted in 

As artificial intelligence continues to transform how we build and interact with software, AI agents are emerging as the new interface for users. Instead of clicking through menus or filling out forms, users can simply instruct agents to fetch reports, analyze datasets, or trigger workflows.

The challenge is: how do these agents access external tools, APIs, or enterprise systems in a secure and standardized way?

This is where the Model Context Protocol (MCP) comes into play. MCP is a protocol designed to connect AI agents to tools in a structured, consistent manner. Instead of building ad-hoc integrations for each agent and each tool, developers can expose their tools once via MCP — making them discoverable and callable by any MCP-compliant AI agent.

In this article, we’ll explore:

What an MCP server is and how it works

a) How MCP uses JSON-RPC 2.0 as its communication layer

b) How MCP solves the M×N integration problem

c) How to implement a simple Weather Data MCP server in TypeScript

d) How to test it locally using Postman or cURL

What is an MCP Server?

An MCP server is an HTTP or WebSocket endpoint that follows the Model Context Protocol, allowing AI systems to query, interact with, and call tools hosted by developers.

MCP consists of several components:

-Base Protocol – Core JSON-RPC message types

-Lifecycle Management – Connection initialization, capability negotiation, and session handling

-Server Features – Resources, prompts, and tools exposed by servers

-Client Features – Sampling and root directory lists provided by clients

-Utilities – Cross-cutting features such as logging or argument completion

All MCP implementations must support the Base Protocol and Lifecycle Management. Other features are optional depending on the use case.

Architecture:

JSON-RPC 2.0 in MCP

MCP messages follow the JSON-RPC 2.0 specification — a stateless, lightweight remote procedure call protocol that uses JSON for request and response payloads.

Request format: json Copy

Edit

{

  “jsonrpc”: “2.0”,

  “id”: 1,

  “method”: “methodName”,

  “params”: {

    “key”: “value”

  }

}

id is required, must be a string or number, and must be unique within the session. method specifies the operation. params contains the method arguments.

Response format: json Copy

Edit

{

  “jsonrpc”: “2.0”,

  “id”: 1,

  “result”: {

    “key”: “value”

  }

}

Or, if an error occurs:

json Copy

Edit

{

  “jsonrpc”: “2.0”,

  “id”: 1,

  “error”: {

    “code”: -32603,

    “message”: “Internal error”

  }

}

The ID must match the request it is responding to. The M×N Problem and How MCP Solves It. Without MCP, connecting M AI agents to N tools requires M×N separate integrations. This is inefficient and unscalable.

With MCP, each agent implements a single MCP client, and each tool implements a single MCP server. Agents and tools can then communicate through a shared protocol, reducing integration effort from M×N to M+N.

Project Setup

Create the project directory:

mkdir weather-mcp-sdk cd weather-mcp-sdk npm init -y

Install dependencies: npm install @modelcontextprotocol/sdk zod axios express npm install –save-dev typescript ts-node @types/node @types/express npx tsc –init

Implementing the Weather MCP Server

We’ll use the WeatherAPI to fetch real-time weather data for a given city and expose it via MCP as a getWeather tool.

src/index.ts

import express from “express”; import axios from “axios”;

import { McpServer } from “@modelcontextprotocol/sdk/server/mcp.js”; import { StreamableHTTPServerTransport } from “@modelcontextprotocol/sdk/server/streamableHttp.js”; import { z } from “zod”; const API_KEY = “YOUR_WEATHER_API_KEY”; // replace with your API key

function getServer() {   const server = new McpServer({

    name: “Weather MCP Server”,     version: “1.0.0”,

  });

  server.tool(     “getWeather”,

    { city: z.string() },     async ({ city }) => {

      const res = await axios.get(“http://api.weatherapi.com/v1/current.json”, {         params: { key: API_KEY, q: city, aqi: “no” },

      });

      const data = res.data;

      return {

        content: [

          {

            type: “text”,

            text: `Weather in ${data.location.name}, ${data.location.country}: ${data.current.temp_c}°C, ${data.current.condition.text}`,

          },

        ],

      };

    }

  );

  return server;

}

const app = express(); app.use(express.json());

app.post(“/mcp”, async (req, res) => {   try {

    const server = getServer();

    const transport = new StreamableHTTPServerTransport({});

    res.on(“close”, () => {       transport.close();

      server.close();

    });

    await server.connect(transport);

    await transport.handleRequest(req, res, req.body);

  } catch (error) {     if (!res.headersSent) {       res.status(500).json({         jsonrpc: “2.0”,

        error: { code: -32603, message: “Internal server error” },         id: null,

      });

    }

  } });

const PORT = 3000; app.listen(PORT, () => {

  console.log(`MCP Stateless HTTP Server running at http://localhost:${PORT}/mcp`);

});

Testing the MCP Server

Since MCP requires specific request formats and content negotiation, use Content-Type: application/json and Accept: application/json, text/event-stream headers.

Step 1 — Initialize

curl -X POST http://localhost:3000/mcp \

  -H “Content-Type: application/json” \

  -H “Accept: application/json, text/event-stream” \

  -d ‘{

    “jsonrpc”: “2.0”,

    “id”: 1,

    “method”: “initialize”,

    “params”: {

      “protocolVersion”: “2025-06-18”,

      “capabilities”: { “elicitation”: {} },

      “clientInfo”: { “name”: “example-client”, “version”: “1.0.0” }

    }

  }’

Example response:

{

  “jsonrpc”: “2.0”,

  “id”: 1,

  “result”: {

    “protocolVersion”: “2025-06-18”,

    “capabilities”: { “tools”: { “listChanged”: true } },

    “serverInfo”: { “name”: “Weather MCP Server”, “version”: “1.0.0” }

  }

}

Step 2 — Call the getWeather Tool

curl -X POST http://localhost:3000/mcp \

  -H “Content-Type: application/json” \

  -H “Accept: application/json, text/event-stream” \

  -d ‘{

    “jsonrpc”: “2.0”,

    “id”: 2,

    “method”: “tools/call”,

    “params”: {

      “name”: “getWeather”,

      “arguments”: { “city”: “London” }

    }   }’

Example response:

{

  “jsonrpc”: “2.0”,

  “id”: 2,

  “result”: {

    “content”: [

      {

        “type”: “text”,

        “text”: “Weather in London, United Kingdom: 21°C, Partly cloudy”

      }

    ]

  } }

To conclude, we have built an MCP-compliant server in TypeScript that exposes a weather-fetching tool over HTTP. This simple implementation demonstrates: How to define and register tools with MCP

How JSON-RPC 2.0 structures communication, and how to make your server compatible with any MCP-compliant AI agent. From here, you can extend the server with additional tools, such as news lookups, database queries, or enterprise system integrations, and deploy it for production use.

By adopting MCP, you ensure that your tools can be discovered and used by a growing ecosystem of AI agents without reinventing the integration wheel.

I hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfronts.com.


Share Story :

SEARCH BLOGS :

FOLLOW CLOUDFRONTS BLOG :


Secured By miniOrange