Skip to main content
Glama
jorben
by jorben
README.md
# MCP Server

A unified MCP (Model Context Protocol) server with pluggable tools. Each tool has an independent endpoint, allowing clients to selectively add specific tools.

[中文文档](./README_zh.md)

## Features

- **Unified Endpoint with Path Routing**: Each tool has its own MCP endpoint (`/mcp/{toolName}`)
- **Loosely Coupled Tools**: Tools are independently loaded from `src/tools/` directory
- **Fault Isolation**: Individual tool failures don't affect other tools (timeout protection + error handling)
- **StreamableHTTP Transport**: Modern MCP transport protocol
- **Bearer Token Authentication**: Secure API access
- **Docker Support**: Ready for containerized deployment

## Project Structure

```
mcp_server/
├── src/
│   ├── index.ts                    # Entry point
│   ├── core/
│   │   ├── tool-registry.ts        # Tool registry
│   │   ├── tool-loader.ts          # Dynamic tool loader
│   │   └── tool-executor.ts        # Isolated executor (timeout + error handling)
│   ├── server/
│   │   ├── app.ts                  # Express + StreamableHTTP
│   │   └── mcp-server.ts           # MCP Server instance
│   ├── middleware/
│   │   └── auth.ts                 # Bearer token authentication
│   ├── tools/                      # Loosely coupled tools directory
│   │   ├── calculator/index.ts     # Calculator tool (add/subtract/multiply/divide)
│   │   ├── echo/index.ts           # Echo tool (echo/reverse/info)
│   │   └── time/index.ts           # Time tool (get_current_time/convert_time/format_time)
│   ├── types/mcp.ts                # Type definitions
│   └── utils/logger.ts             # Terminal logger
├── Dockerfile                      # Multi-stage build
├── docker-compose.yml
├── package.json
└── tsconfig.json
```

## Quick Start

### Local Development

```bash
# Install dependencies
npm install

# Create .env file
echo "AUTHORIZATION_KEY=your-secret-key" > .env

# Start development server
npm run dev
```

### Docker Deployment

```bash
# Build and run with docker-compose
docker-compose up -d --build

# Or build manually
docker build -t mcp-server .
docker run -e AUTHORIZATION_KEY=your-secret-key -p 3000:3000 mcp-server
```

## API Endpoints

| Endpoint          | Method | Description                                  |
| ----------------- | ------ | -------------------------------------------- |
| `/mcp/{toolName}` | POST   | MCP JSON-RPC request for specific tool       |
| `/mcp/{toolName}` | GET    | SSE connection (requires session-id)         |
| `/mcp/{toolName}` | DELETE | Close session                                |
| `/health`         | GET    | Health check                                 |
| `/tools`          | GET    | List all available tools and their endpoints |

## Available Tools

### Calculator (`/mcp/calculator`)

Math expression evaluator supporting various operators and functions.

| Method     | Description                    | Parameters             |
| ---------- | ------------------------------ | ---------------------- |
| `evaluate` | Evaluate a math expression     | `expression`: string   |

**Supported Operators:**
- Arithmetic: `+`, `-`, `*`, `/`
- Floor Division: `//`
- Modulo: `%`
- Power: `**` or `^`

**Supported Constants:** `pi`, `e`

**Supported Functions:** `sin`, `cos`, `tan`, `sqrt`, `abs`, `log`, `log10`, `exp`, `floor`, `ceil`, `round`, `pow`, `min`, `max`

**Examples:**
```
2 + 3 * 4        → 14
(2 + 3) * 4      → 20
2^10             → 1024
sqrt(16)         → 4
2 * pi           → 6.283...
sin(0)           → 0
pow(2, 8)        → 256
17 // 5          → 3
17 % 5           → 2
```

### Echo (`/mcp/echo`)

Echo tool for testing and debugging.

| Method    | Description                 | Parameters        |
| --------- | --------------------------- | ----------------- |
| `echo`    | Echo back the input message | `message`: string |
| `reverse` | Reverse the input string    | `text`: string    |
| `info`    | Get server information      | -                 |

### Time (`/mcp/time`)

Time tool for timezone conversion and formatting.

| Method             | Description                             | Parameters                                                                   |
| ------------------ | --------------------------------------- | ---------------------------------------------------------------------------- |
| `get_current_time` | Get current time in a specific timezone | `timezone`: string (default: "Etc/UTC")                                      |
| `convert_time`     | Convert time between timezones          | `source_timezone`: string, `target_timezone`: string, `time`: string (HH:MM) |
| `format_time`      | Convert timestamp to formatted string   | `timestamp`: number (ms), `timezone`: string                                 |

## Client Configuration

### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):

```json
{
  "mcpServers": {
    "calculator": {
      "url": "http://localhost:3000/mcp/calculator",
      "transport": "streamable-http",
      "headers": {
        "Authorization": "Bearer your-secret-key"
      }
    },
    "time": {
      "url": "http://localhost:3000/mcp/time",
      "transport": "streamable-http",
      "headers": {
        "Authorization": "Bearer your-secret-key"
      }
    }
  }
}
```

### Cursor / VS Code

Create `.cursor/mcp.json` in project root:

```json
{
  "mcpServers": {
    "calculator": {
      "url": "http://localhost:3000/mcp/calculator",
      "transport": "streamable-http",
      "headers": {
        "Authorization": "Bearer your-secret-key"
      }
    }
  }
}
```

### Programmatic Usage (Node.js)

```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = new Client({
  name: "my-client",
  version: "1.0.0",
});

const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:3000/mcp/calculator"),
  {
    requestInit: {
      headers: {
        Authorization: "Bearer your-secret-key",
      },
    },
  }
);

await client.connect(transport);

// List available tools
const tools = await client.listTools();
console.log(tools);

// Call a tool
const result = await client.callTool({
  name: "add",
  arguments: { a: 10, b: 5 },
});
console.log(result);
```

## Adding New Tools

1. Create a new directory under `src/tools/`:

```
src/tools/my-tool/
└── index.ts
```

2. Implement the `MCPTool` interface:

```typescript
import { z } from "zod";
import { MCPTool, MCPMethodDefinition } from "../../types/mcp.js";

const myTool: MCPTool = {
  name: "my-tool",
  description: "Description of my tool",
  version: "1.0.0",

  getMethods(): MCPMethodDefinition[] {
    return [
      {
        name: "my-method",
        description: "Description of my method",
        inputSchema: {
          param1: z.string().describe("Parameter description"),
        },
        handler: async (params) => {
          const { param1 } = params as { param1: string };
          return { result: param1 };
        },
      },
    ];
  },

  async initialize() {
    // Initialization logic
  },

  async healthCheck() {
    return true;
  },
};

export default myTool;
```

3. The tool will be automatically loaded on server startup.

## Scripts

```bash
npm run dev        # Start development server with hot reload
npm run build      # Build for production
npm start          # Start production server
npm test           # Run tests
npm run lint       # Check code style
npm run lint:fix   # Auto-fix code style issues
```

## License

MIT