hello-world-mcp-server

shivprasad/hello-world-mcp-server

3.1

If you are the rightful owner of hello-world-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

The Model Context Protocol (MCP) server is a standardized protocol server that facilitates communication between AI applications and external systems, enabling real-time data access, function execution, and resource management.

Tools
1
Resources
0
Prompts
0

Hello World MCP Server - Complete Beginner's Guide

A comprehensive tutorial for building your first Model Context Protocol (MCP) server from scratch.

Table of Contents

  1. What is MCP?
  2. Prerequisites
  3. Project Setup
  4. Understanding MCP Architecture
  5. Building the Server
  6. Testing Your Server
  7. Integration with Claude
  8. Advanced Features
  9. Best Practices
  10. Troubleshooting

What is MCP?

The Model Context Protocol (MCP) is a standardized protocol that allows AI applications (like Claude) to connect to external systems and data sources. It enables LLMs to:

  • Access real-time data
  • Execute functions
  • Read files and resources
  • Use custom prompts and templates

Core Concepts

MCP Servers provide three main capabilities:

  1. Resources 📄 - Static or dynamic data (like files, databases)
  2. Tools 🔧 - Functions that the LLM can execute
  3. Prompts 💬 - Pre-written templates for common tasks

Prerequisites

Required Software

  • Node.js (v18 or higher)
  • npm (comes with Node.js)
  • TypeScript (for development)
  • Text editor (VS Code recommended)

Required Knowledge

  • Basic JavaScript/TypeScript
  • Understanding of JSON
  • Command line basics
  • API concepts (requests/responses)

Project Setup

1. Initialize Project

mkdir hello-world-mcp-server
cd hello-world-mcp-server
npm init -y

2. Install Dependencies

# Core MCP dependencies
npm install @modelcontextprotocol/sdk zod

# Development dependencies
npm install -D typescript @types/node

3. Configure TypeScript

Create tsconfig.json:

{
  "compilerOptions": {
    "target": "es2020",
    "module": "es2020",
    "moduleResolution": "node",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

4. Update package.json

{
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch",
    "inspect": "npx @modelcontextprotocol/inspector dist/index.js"
  }
}

Understanding MCP Architecture

Transport Layer

MCP uses stdio (standard input/output) for communication:

┌─────────────┐    stdio    ┌─────────────┐
│   Claude    │ ◄────────►  │ MCP Server  │
│ (Client)    │             │             │
└─────────────┘             └─────────────┘

Message Format

All communication uses JSON-RPC 2.0:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "hello",
    "arguments": { "name": "World" }
  }
}

Server Lifecycle

  1. Initialization - Client connects and negotiates capabilities
  2. Capability Exchange - Server advertises what it can do
  3. Request Handling - Server responds to client requests
  4. Cleanup - Connection terminates gracefully

Building the Server

1. Basic Server Structure

Create src/index.ts:

#!/usr/bin/env node

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

// Initialize server with metadata
const server = new Server(
  {
    name: "hello-world-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      resources: {},
      tools: {},
      prompts: {},
    },
  }
);

2. Implementing Tools

Tools are functions the LLM can call:

import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "hello",
        description: "Returns a greeting message",
        inputSchema: {
          type: "object",
          properties: {
            name: {
              type: "string",
              description: "Name to greet (optional)",
            },
          },
        },
      },
    ],
  };
});

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "hello": {
      const nameArg = args?.name as string | undefined;
      const greeting = nameArg ? `Hello, ${nameArg}!` : "Hello, World!";
      return {
        content: [
          {
            type: "text",
            text: greeting,
          },
        ],
      };
    }
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

3. Implementing Resources

Resources provide data the LLM can read:

import {
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "greeting://hello-world",
        name: "Hello World Message",
        description: "A simple hello world greeting",
        mimeType: "text/plain",
      },
    ],
  };
});

// Handle resource reads
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const { uri } = request.params;

  switch (uri) {
    case "greeting://hello-world":
      return {
        contents: [
          {
            uri,
            mimeType: "text/plain",
            text: "Hello, World! This is a resource from the MCP server.",
          },
        ],
      };
    default:
      throw new Error(`Unknown resource: ${uri}`);
  }
});

4. Implementing Prompts

Prompts are templates for common tasks:

import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// List available prompts
server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: [
      {
        name: "greeting-template",
        description: "A template for generating personalized greetings",
        arguments: [
          {
            name: "name",
            description: "The name of the person to greet",
            required: true,
          },
        ],
      },
    ],
  };
});

// Handle prompt requests
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "greeting-template": {
      const personName = args?.name as string;
      
      if (!personName) {
        throw new Error("Name argument is required");
      }

      return {
        description: `Personalized greeting for ${personName}`,
        messages: [
          {
            role: "user",
            content: {
              type: "text",
              text: `Hello, ${personName}! How can I assist you today?`,
            },
          },
        ],
      };
    }
    default:
      throw new Error(`Unknown prompt: ${name}`);
  }
});

5. Starting the Server

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Hello World MCP Server running on stdio");
}

if (import.meta.url === `file://${process.argv[1]}`) {
  main().catch((error) => {
    console.error("Server error:", error);
    process.exit(1);
  });
}

Testing Your Server

1. Build the Server

npm run build

2. Test with MCP Inspector

npm run inspect

This opens a web interface where you can:

  • View server capabilities
  • Test tools interactively
  • Debug message flow
  • Validate responses

3. Manual Testing

Create a simple test script to verify functionality:

#!/usr/bin/env node
import { spawn } from 'child_process';

const serverProcess = spawn('node', ['dist/index.js'], {
  stdio: ['pipe', 'pipe', 'inherit']
});

// Send initialization message
const initMessage = {
  jsonrpc: '2.0',
  id: 1,
  method: 'initialize',
  params: {
    protocolVersion: '2025-06-18',
    capabilities: {},
    clientInfo: { name: 'test-client', version: '1.0.0' }
  }
};

serverProcess.stdin.write(JSON.stringify(initMessage) + '\\n');

Integration with AI Assistants

1. Claude Desktop Configuration

Add to Claude Desktop's configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "hello-world": {
      "command": "node",
      "args": ["/path/to/your/project/dist/index.js"]
    }
  }
}

2. Cline Configuration

To use this MCP server with Cline (VS Code extension):

Step 1: Build the Server
npm run build
Step 2: Configure Cline
  1. Open VS Code with the Cline extension installed
  2. Open Cline settings (Command Palette → "Cline: Open Settings")
  3. Navigate to the "MCP Servers" section
  4. Add a new MCP server configuration:
{
  "name": "hello-world-mcp-server",
  "command": "node",
  "args": ["/absolute/path/to/your/project/dist/index.js"],
  "env": {}
}

Important: Use the absolute path to your compiled dist/index.js file.

Step 3: Restart Cline

After adding the configuration, restart Cline to load the new MCP server.

Step 4: Test the Integration

Once configured, Cline can use your MCP server's capabilities:

  • Tools: Cline can call the hello tool to generate greetings
  • Resources: Cline can read the greeting resource for context
  • Prompts: Cline can use the greeting template for structured responses

Example usage in Cline:

User: Use the hello tool to greet Sarah
Cline: I'll use the hello tool from the MCP server to greet Sarah.
[MCP Tool Call: hello with name="Sarah"]
Result: Hello, Sarah!
Troubleshooting Cline Integration

Server Not Found:

  • Verify the absolute path to dist/index.js is correct
  • Ensure the server builds successfully with npm run build
  • Check Cline's output panel for error messages

Permission Issues:

  • Make sure the compiled JavaScript file is executable
  • On Unix systems, you may need: chmod +x dist/index.js

Connection Issues:

  • Restart VS Code completely after configuration changes
  • Check that Node.js is in your system PATH
  • Verify no other processes are using the same MCP server

3. Testing in Claude

Once configured, you can test in Claude:

User: Use the hello tool to greet John
Claude: I'll use the hello tool to greet John.
[Tool execution: hello with name="John"]
Result: Hello, John!

Advanced Features

Error Handling

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    // Tool logic here
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error.message}`,
        },
      ],
      isError: true,
    };
  }
});

Input Validation with Zod

import { z } from "zod";

const HelloArgsSchema = z.object({
  name: z.string().optional(),
  greeting: z.enum(["hello", "hi", "hey"]).default("hello"),
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  
  if (name === "hello") {
    const validatedArgs = HelloArgsSchema.parse(args);
    // Use validatedArgs.name, validatedArgs.greeting
  }
});

Progress Reporting

return {
  content: [
    {
      type: "text",
      text: "Processing request...",
    },
  ],
  progress: {
    progress: 50,
    total: 100,
  },
};

Best Practices

1. Security

  • ✅ Validate all inputs
  • ✅ Use allowlists for file paths
  • ✅ Sanitize data before processing
  • ❌ Never execute arbitrary code
  • ❌ Don't expose sensitive information

2. Performance

  • Use async/await for I/O operations
  • Implement caching for expensive operations
  • Stream large responses when possible
  • Set appropriate timeouts

3. Error Handling

  • Provide meaningful error messages
  • Use structured logging
  • Gracefully handle edge cases
  • Document error conditions

4. Documentation

  • Document all tools, resources, and prompts
  • Provide usage examples
  • Explain input/output formats
  • Include troubleshooting guides

Troubleshooting

Common Issues

Permission Denied (EACCES)

chmod +x dist/index.js

Module Resolution Errors

  • Check package.json has "type": "module"
  • Ensure TypeScript config uses correct module settings

Server Not Responding

  • Check stdio transport is working
  • Verify server initialization
  • Look for error messages in stderr

Tool Not Found

  • Ensure tool is listed in ListToolsRequestSchema handler
  • Check tool name matches exactly
  • Verify CallToolRequestSchema handler includes the tool

Debugging Tips

  1. Use console.error() for debugging (stdout is reserved for MCP protocol)
  2. Test with MCP Inspector before integrating with Claude
  3. Check Claude Desktop logs for configuration issues
  4. Validate JSON-RPC messages are properly formatted

Next Steps

Now that you have a working MCP server, consider:

  1. Adding real functionality (database access, API calls, file operations)
  2. Building more complex tools with multiple parameters
  3. Implementing dynamic resources that change over time
  4. Creating specialized prompts for your use case
  5. Contributing to the MCP ecosystem with your own servers

Resources

Happy coding! 🚀