Skip to main content

Command Palette

Search for a command to run...

How To Build MCP Server from Scratch with TypeScript and Groq

Updated
7 min read
How To Build MCP Server from Scratch with TypeScript and Groq
O
Full-stack Ai Engineer

Three core concepts while building MCP

  1. Resources - Acts as a read-only data source—similar to a local file or static API endpoint—that an LLM can parse for immediate context.

  2. Tools - Executable functions that empower the LLM to trigger dynamic actions and interact directly with external environments.

  3. Prompts - pre-written template that user can re-use to perform a task

How to build MCP server

1. Local mcp server

1. Setting Up Your Environment

  • Create and set up our project:

    # Create project directory
    mkdir mcp-local
    cd mcp-local
    
    # Initialize npm project
    npm init -y
    
    # Install dependencies
    npm install zod @modelcontextprotocol/sdk
    
    # Install dev dependencies
    npm install -D @types/node typescript
    
    # Create src dir
    mkdir src
    cd src
    
    #create index file
    touch index.ts
    
  • Update package.json

    {
      "type": "module",
      "scripts": {
        "build": "tsc",
        "start": "node dist/index.js"
      }
    }
    
  • Create a tsconfig.json in the root of your project

    {
      "compilerOptions": {
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules"]
    }
    

2. Creating local mcp server

create a src/index.ts file

  1. McpServer instance:

    Here we create instance of McpServer by providing name and version

    //src/index.ts
    
    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    
    // Create server instance
    const server = new McpServer({
      name: "Student",
      version: "1.0.0",
    });
    
  2. Resources: The "Read-Only" Data:

    There is a method called server.registerResource it is acts like a file or a URL that the AI can "read" to get background information.

    The following things we provide to server.registerResource method

    1. name - "greeting-resource"

    2. uriOrTemplate - "https://example.com/greetings/default"

    3. config - resource meta data title , description and mimeType

    4. callback - it return object of uri and text

    //src/index.ts
    
    import {
      ReadResourceResult,
    } from "@modelcontextprotocol/sdk/types";
    
    server.registerResource(
      "greeting-resource",
      "https://example.com/greetings/default",
      {
        title: "Default Greeting", // Display name for UI
        description: "A simple greeting resource",
        mimeType: "text/plain",
      },
      async (): Promise<ReadResourceResult> => {
        return {
          contents: [
            {
              uri: "https://example.com/greetings/default",
              text: "Hello! I'm, your virtual assistant. I'm here to help you streamline your workflow and answer any questions you might have about. How can I assist you today?",
            },
          ],
        };
      },
    );
    
  3. Prompts: The "Templates”

    The server.registerPrompt section provides pre-defined templates that user can reuse to perform a task

    //src/index.ts
    
    import { z } from "zod";
    server.registerPrompt(
      "student_list",
      {
        title: "student list",
        description: "A sample prompt to get list of student",
        argsSchema: {
          limit: z.string().describe("no between 1 to 9"),
        },
      },
      async ({ limit }): Promise<GetPromptResult> => {
        return {
          messages: [
            {
              role: "user",
              content: {
                type: "text",
                text: `give me list of student, give only ${limit} students`,
              },
            },
          ],
        };
      },
    );
    
  4. Tools: The "Action" Functions

    The server.registerTool section is the most powerful part. It allows the AI to execute code to get real-time data.

    //src/index.ts
    
    import { z } from "zod";
    server.registerTool(
      "get_student",
      {
        description: "Get list of all student with their enrollment no.",
        inputSchema: {
          limit: z.number().describe("Constrains the input parameter evaluation to a clean range between 1 and 10 records."),
        },
      },
      async ({ limit }) => {
        const student = [
          {
            id: "STU-001",
            name: "Elena Rodriguez",
            age: 20,
            major: "Marine Biology",
            gpa: 3.85,
            email: "elena.r@university.edu",
            enrolled: true,
          },
          {
            id: "STU-002",
            name: "Marcus Chen",
            age: 22,
            major: "Cybersecurity",
            gpa: 3.92,
            email: "m.chen99@university.edu",
            enrolled: true,
          },
          {
            id: "STU-003",
            name: "Sarah Jenkins",
            age: 19,
            major: "Graphic Design",
            gpa: 3.4,
            email: "s.jenkins@university.edu",
            enrolled: true,
          },
          {
            id: "STU-004",
            name: "Amara Okafor",
            age: 21,
            major: "Mechanical Engineering",
            gpa: 3.78,
            email: "a.okafor@university.edu",
            enrolled: false,
          }
        ];
    
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(student.slice(0, limit)),
            },
          ],
        };
      },
    );
    
  5. Main Execution and Communication Transport

    We create StdioServerTransport and connect to McpServerinstance to start listen through stdio transport.

    //src/index.ts
    
    import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
    async function main() {
      const transport = new StdioServerTransport();
      await server.connect(transport);
      console.error("Student MCP Server running on stdio");
    }
    
    main().catch((error) => {
      console.error("Fatal error in main():", error);
       process.exit(1);
    });
    

Here is a complete GitHub source code

2. Remote MCP Server

1. Setting Up Your Environment

  • Create and set up our project:

    # Create project directory
    mkdir mcp-remote
    cd mcp-remote
    
    # Initialize npm project
    npm init -y
    
    # Install dependencies
    npm install zod @modelcontextprotocol/sdk hono @hono/mcp @hono/node-server
    
    # Install dev dependencies
    npm install -D @types/node typescript
    
    # Create src dir
    mkdir src
    cd src
    
    #create index file
    touch index.ts
    touch app.ts
    
  • Update package.json

    {
      "type": "module",
      "scripts": {
        "build": "tsc",
        "start": "node dist/index.js"
      }
    }
    
  • Create a tsconfig.json in the root of your project

    {
      "compilerOptions": {
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules"]
    }
    

2. Creating mcp remote serve

In remote MCP Server the steps for creating Resources, Prompts and Tools are same as local mcp server, only difference in communication part.

Communication Transport

create app.ts file

  1. Setting up the Web Server

    //src/app.ts
    
    import { StreamableHTTPTransport } from "@hono/mcp";
    import { Hono } from "hono";
    
    const app = new Hono();
    const transport = new StreamableHTTPTransport();
    
    • Hono - We leverage Hono to orchestrate our HTTP layer due to its exceptional routing speed and zero-dependency footprint.

    • StreamableHTTPTransport - this is HTTP Transport Layer for remote mcp server and also it leverage SSE for streaming data.

  2. MCP end point

    In the /mcp endpoint where actual transport happens.

    //src/app.ts
    
    import { server } from ".";
    
    app.all("/mcp", async (c) => {
      if (!server.isConnected()) {
        await server.connect(transport);
      }
    
      return transport.handleRequest(c);
    });
    
    • app.all() - This listen all HTTP methods GET POST PATCH DELETE etc

    • server.connect - we connect transport to server at 1st time of connection.

    • transport.handleRequest(c) - It intercepts incoming transport protocol payloads and routes them through the execution pipeline.

  3. Listening the server

    We listen the remote mcp server on port 8787

    //src/app.ts
    
    import { serve } from "@hono/node-server";
    serve({
      fetch: app.fetch,
      port: 8787,
    });
    

Here is a complete GitHub source code

How to Debug MCP Inspector

Now we build local/remote mcp servers to test or debug the mcp server there is official library provided by modelcontextprotocol i.e MCP Inspector

This provides interactive developer tool UI which is used for testing and debugging MCP servers very easily.

Here are steps of how to use this tool:

Start the Interactive development UI server

npx @modelcontextprotocol/inspector

Interactive development UI server get started on port http://localhost:6274 it is look like this:

Server connection panel

Left side of UI is the server connection panel and following things are required to connect local/remote server

  1. MCP local server

    1. Transport type - STDIO

    2. Command - node

    3. Arguments - path/to/server/index.js

  2. MCP remote server

    1. Transport type - Stremable HTTP

    2. URL - http://localhost:8787/mcp

    3. Connection Type - Via Proxy

Resources tab

  • Lists all available resources

  • Shows resource metadata (MIME types, descriptions)

  • Allows resource content inspection

  • Supports subscription testing

Prompts tab

  • Displays available prompt templates

  • Shows prompt arguments and descriptions

  • Enables prompt testing with custom arguments

  • Previews generated messages

Tools tab

  • Lists available tools

  • Shows tool schemas and descriptions

  • Enables tool testing with custom inputs

  • Displays tool execution results

Resources

  1. MCP Official Document - Document

  2. How To Build Local MCP Server Source Code - GitHub

  3. How To Build Remote MCP Server Source Code - GitHub

Read more

  1. Gen AI Using JS - Github

More from this blog

Onkar K | Full-Stack AI Engineering

19 posts

Production-grade GenAI & multi-agent apps with Next.js & TypeScript. Explore deep architectures using LangGraph.js, LangChain.js, and backends via Hono, Express, & Node.js. Master advanced RAG with Qdrant, Pinecone, and Redis caching. Track execution with Langfuse and LangSmith. Zero fluff—just type-safe code, terminal logs, and robust deployments with Docker, Kafka, and Kubernetes for modern builders