Skip to main content
Glama
README.md
# MCP Server with Management UI

A production-ready [Model Context Protocol](https://modelcontextprotocol.io) server built with TypeScript, featuring:

- **6 built-in tools** – file read/write/list, HTTP fetch, system info, and env variable reader
- **Dual transport** – stdio (Claude Desktop) and SSE/HTTP (Claude.ai)
- **Management Dashboard** – live tool list, interactive tool tester, and request log
- **Extensible** – add new tools in minutes

---

## Prerequisites

- [Node.js](https://nodejs.org) v18 or higher (`node --version` to check)
- [Claude Desktop](https://claude.ai/download) (for local MCP integration)

---

## Quick Start

### 1 · Install dependencies

```bash
npm run install:all
```

Installs both server and UI packages.

### 2 · Configure environment

```bash
# Windows
copy .env.example .env

# macOS / Linux
cp .env.example .env
```

Edit `.env` to set `PORT`, `SERVER_NAME`, or any API keys you need.

### 3 · Development mode (hot-reload)

Open **two terminals**:

```bash
# Terminal 1 – backend server with hot-reload
npm run dev

# Terminal 2 – UI dev server (proxies API calls to the backend)
cd ui && npm run dev
```

- Backend API + SSE: `http://localhost:3000`
- Management UI (dev): `http://localhost:5173/ui`

### 4 · Production build

```bash
npm run build   # compiles TypeScript + builds React UI
npm start       # starts everything on :3000
```

Open `http://localhost:3000/ui` in your browser.

---

## Connecting to Claude Desktop (stdio)

Claude Desktop launches the server as a child process automatically — no running server needed.

### Step 1 — Build the server

```bash
npm run build
```

### Step 2 — Find the correct config file location

The location depends on how Claude Desktop was installed:

| Installation type | Config path |
|---|---|
| **Windows Store / MSIX** | `%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json` |
| **Windows direct installer** | `%APPDATA%\Claude\claude_desktop_config.json` |
| **macOS** | `~/Library/Application Support/Claude/claude_desktop_config.json` |

> **Tip (Windows):** Run this in PowerShell to find the right file automatically:
> ```powershell
> Get-ChildItem "$env:LOCALAPPDATA" -Recurse -Filter "claude_desktop_config.json" -ErrorAction SilentlyContinue
> Get-ChildItem "$env:APPDATA"      -Recurse -Filter "claude_desktop_config.json" -ErrorAction SilentlyContinue
> ```

### Step 3 — Write the config

Replace `YOU` and `C:/path/to` with your actual username and project path.

```json
{
  "mcpServers": {
    "mcp-server": {
      "command": "node",
      "args": ["C:/Users/YOU/Projects/mcp-server/dist/index.js", "--stdio"]
    }
  }
}
```

> **Windows tip:** If `node` isn't found by Claude Desktop, use the full path instead:
> ```powershell
> (Get-Command node).Source   # prints e.g. C:\Program Files\nodejs\node.exe
> ```
> Then set `"command": "C:/Program Files/nodejs/node.exe"`.

### Step 4 — Restart Claude Desktop

- Fully quit Claude Desktop (right-click tray icon → **Quit**, don't just close the window)
- Reopen it from the Start Menu / Applications
- Open a new chat and ask: *"What tools do you have available?"*

Claude will list all 6 tools automatically — no connect button required.

---

## Connecting to Claude.ai (SSE)

Claude.ai's custom connector requires a publicly accessible **HTTPS** URL.

### Step 1 — Start the server

```bash
npm start
```

### Step 2 — Expose it with a tunnel

```bash
# Using ngrok (https://ngrok.com)
ngrok http 3000
# Copy the https://xxxx.ngrok-free.app URL
```

Alternatives: [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), [localtunnel](https://github.com/localtunnel/localtunnel)

### Step 3 — Add the connector in Claude.ai

In Claude Desktop or Claude.ai:
- Click **+** → **Connectors** → **Add custom connector**
- **Name**: `mcp-server`
- **URL**: `https://xxxx.ngrok-free.app/sse`

---

## Available Tools

| Tool | Description |
|---|---|
| `read_file` | Read a file's contents (utf-8 or base64) |
| `write_file` | Write / append text to a file |
| `list_directory` | List files in a directory (optionally recursive) |
| `fetch_url` | Make HTTP requests (GET, POST, PUT, DELETE, PATCH) |
| `get_system_info` | OS, CPU, memory, network, and process details |
| `get_environment_variable` | Read environment variables from the server process |

---

## Adding New Tools

All tools live in `src/tools/`. Create a new file, export a `register*Tools` function, and call it from `src/server.ts`.

**Example – add a calculator tool:**

```typescript
// src/tools/calculator.ts
import { z } from 'zod';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerTool } from './registry.js';

export function registerCalculatorTools(server: McpServer): void {
  registerTool(
    server,
    'calculate',
    'Evaluate a simple arithmetic expression and return the result.',
    {
      expression: z.string().describe('Arithmetic expression, e.g. "2 + 2 * 10"'),
    },
    async ({ expression }) => {
      const result = Function(`"use strict"; return (${expression})`)();
      return { content: [{ type: 'text' as const, text: String(result) }] };
    },
  );
}
```

Then in `src/server.ts`:

```typescript
import { registerCalculatorTools } from './tools/calculator.js';
// inside createMcpServer():
registerCalculatorTools(server);
```

The new tool appears in the Management UI and is immediately available to Claude.

---

## Project Structure

```
mcp-server/
├── src/
│   ├── index.ts          # Entry point – detects --stdio vs HTTP mode
│   ├── server.ts         # McpServer factory, registers all tools
│   ├── web.ts            # Express server (SSE transport + REST API for UI)
│   ├── logger.ts         # Circular in-memory request log (200 entries)
│   ├── types.ts          # Shared TypeScript types
│   └── tools/
│       ├── registry.ts   # registerTool() wrapper + direct invocation map
│       ├── file.ts       # read_file, write_file, list_directory
│       ├── fetch.ts      # fetch_url
│       └── system.ts     # get_system_info, get_environment_variable
├── ui/
│   ├── src/
│   │   ├── App.tsx       # Tabbed layout (Tools / Tester / Logs)
│   │   ├── api.ts        # REST client for the management API
│   │   └── components/
│   │       ├── ServerStatus.tsx   # Live uptime, port, request count
│   │       ├── ToolList.tsx       # Expandable list with JSON schemas
│   │       ├── ToolTester.tsx     # Select tool → edit JSON → invoke
│   │       └── RequestLog.tsx     # Auto-refreshing log with expand
│   └── vite.config.ts
├── dist/                 # Compiled server output (after npm run build)
├── .env                  # Local config – not committed
├── .env.example          # Template for .env
├── claude_desktop_config.json   # Example config snippet
├── package.json
└── tsconfig.json
```

---

## Scripts

| Command | Description |
|---|---|
| `npm run dev` | Run server with hot-reload (tsx watch) |
| `npm run dev:stdio` | Run in stdio mode for Claude Desktop testing |
| `npm run build` | Compile TypeScript + build React UI |
| `npm start` | Run compiled server (HTTP/SSE + UI mode) |
| `npm run start:stdio` | Run compiled server in stdio mode |
| `npm run install:all` | Install all dependencies (server + UI) |
| `npm run setup` | Full first-time setup: install + build |