Skip to main content
Glama
sampritidas

Context Hub MCP Server

by sampritidas
README.md
# Hello this is README

# Context Hub MCP Server

A minimal Model Context Protocol (MCP) server implemented in TypeScript exposing sample tools, a dynamic resource, and a prompt. Intended as a starting point for building richer servers or composing with other MCP servers.

## Features

- **Tools**:
  - `echo` - Echoes back a provided message
  - `add_numbers` - Adds two numbers together
- **Resource**: Dynamic greeting (`greeting://{name}`)
- **Prompt**: `review_code` - Code review prompt template
- **Transport**: `stdio` for local integration (VS Code, Claude Desktop, MCP Inspector)

## Aggregator & API Extensions

Beyond the minimal example, this project now includes a multi-server aggregator (`src/multi-server.ts`) that:

- Connects to multiple external MCP servers defined in `mcp_servers.json` (`servers` array)
- Dynamically lists & delegates remote tools/resources (`list_remote_tools`, `call_remote_tool`, etc.)
- Adapts declarative REST API definitions (the `apis` array) into MCP tools (e.g. `google_search`)
- Provides experimental OAuth-backed tools (Google Drive listing) via `oauthProviders` configuration

### Running the Aggregator

```bash
npm run build
node build/multi-server.js
```

Then use MCP Inspector or a client to call new tools.

### Configuration File (`mcp_servers.json`)

Key sections:

- `servers`: External MCP processes (filesystem, memory, etc.)
- `apis`: In-process REST adapters with query param mapping & env injection
- `oauthProviders`: OAuth client credentials + refresh token for token refresh grant

Example OAuth provider (placeholders – replace with real values):

```jsonc
"oauthProviders": [
  {
    "name": "google",
      "clientId": "${GOOGLE_CLIENT_ID}",
      "clientSecret": "${GOOGLE_CLIENT_SECRET}",
      "refreshToken": "${GOOGLE_REFRESH_TOKEN}",
    "tokenEndpoint": "https://oauth2.googleapis.com/token",
    "scopes": ["https://www.googleapis.com/auth/drive.readonly"]
  }
]
```

Values wrapped like `${VARNAME}` are substituted from environment variables at startup. Set them before launching:

```bash
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your_client_secret"
export GOOGLE_REFRESH_TOKEN="1//04ABCDEF..."
```

### Obtaining Google OAuth Credentials & Refresh Token

1. Create/Select a project in Google Cloud Console.
2. Enable required APIs (e.g. Drive API, Custom Search already uses API key not OAuth).
3. Configure OAuth consent screen (external if personal use; publish if needed).
4. Create OAuth Client ID:

- Application type: "Desktop" (simplifies installed app flow) OR "Web" with a loopback redirect `http://127.0.0.1:8085/callback`.

5. Generate authorization URL:

```bash
AUTH_URL="https://accounts.google.com/o/oauth2/v2/auth?client_id=$GOOGLE_CLIENT_ID&redirect_uri=http://127.0.0.1:8085/callback&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.readonly&access_type=offline&prompt=consent"
echo $AUTH_URL
open "$AUTH_URL"  # macOS opens browser
```

6. Capture authorization code:

- Run a tiny listener (Python example):

```bash
python - <<'PY'
import http.server, sys
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
 from urllib.parse import urlparse, parse_qs
 qs=parse_qs(urlparse(self.path).query)
 code=qs.get('code',[None])[0]
 print('Auth code:', code)
 self.send_response(200); self.end_headers(); self.wfile.write(b'You may close this tab.')
 sys.exit(0)
http.server.HTTPServer(('127.0.0.1',8085), H).serve_forever()
PY
```

7. Exchange code for tokens (includes refresh_token on first consent):

```bash
curl -s -X POST https://oauth2.googleapis.com/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "code=YOUR_CODE" \
-d "client_id=$GOOGLE_CLIENT_ID" \
-d "client_secret=$GOOGLE_CLIENT_SECRET" \
-d "redirect_uri=http://127.0.0.1:8085/callback" \
-d "grant_type=authorization_code" | jq
```

Save `refresh_token` from the JSON and export as `GOOGLE_REFRESH_TOKEN`.

### Device Code Flow (Alternative)

```bash
curl -s -X POST https://oauth2.googleapis.com/device/code \
  -d "client_id=$GOOGLE_CLIENT_ID" \
  -d "scope=https://www.googleapis.com/auth/drive.readonly" | jq
```

Response includes `user_code`, `verification_url`, and `device_code`. Visit verification URL, enter code, then poll:

```bash
while true; do
  curl -s -X POST https://oauth2.googleapis.com/token \
   -d "client_id=$GOOGLE_CLIENT_ID" \
   -d "client_secret=$GOOGLE_CLIENT_SECRET" \
   -d "device_code=YOUR_DEVICE_CODE" \
   -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" | jq; sleep 5; done
```

When authorized, JSON contains `refresh_token`.

### Secure Handling

- Do NOT commit real secrets; keep `.env` / shell exports local.
- Rotate refresh tokens on compromise or developer departure.
- Restrict scopes to minimum required.

## OAuth (Experimental)

The OAuth scaffold enables token refresh for providers defined in `oauthProviders` and exposes:

- `oauth_refresh_token` – Forces refresh & returns access token + expiry.
- `google_drive_list_files` – Lists first 10 Drive files using a cached access token.

### Current Limitations

- Assumes you already possess a valid `refreshToken` (obtained out-of-band via standard Google OAuth consent; device code flow not yet implemented).
- Tokens cached in-memory only (no persistence across restarts).
- Only refresh-token grant supported (no authorization code PKCE exchange; no service account flow).
- No automatic scope negotiation or incremental consent—scopes are informational.
- Error handling is basic; rate limiting & retries not implemented.

### Next Steps (Roadmap)

1. Implement device code or local loopback auth helper to acquire refresh tokens interactively.
2. Persist token cache securely (file with restricted permissions or OS keychain).
3. Add structured error classes + exponential backoff for transient 5xx / 429 responses.
4. Generalize to additional Google APIs (Gmail, Calendar) and non-Google providers.
5. Introduce fine-grained tool schema generation from OpenAPI specs.

### Security Notes

- Never commit real `clientSecret` / `refreshToken`. Use environment overrides or a local, ignored secrets file.
- Rotate credentials periodically; audit scopes granted.
- Consider separating untrusted external servers from OAuth-enabled processes.

---

For questions or to extend further, start from `src/multi-server.ts` and follow the pattern used for API + OAuth tools.

## Prerequisites

- Node.js >= 18
- npm or compatible package manager

## Quick Start

### 1. Install Dependencies

**Important**: If using a corporate npm registry, switch to public registry first:

```bash
npm config set registry https://registry.npmjs.org/
```

Then install:

```bash
npm install
```

### 2. Build

```bash
npm run build
```

This compiles TypeScript to `build/index.js`.

### 3. Test with MCP Inspector

```bash
npx @modelcontextprotocol/inspector node build/index.js
```

**What is MCP Inspector?**

The Inspector is a **debugging and testing tool** (think "Postman for MCP servers"). It provides a web UI to:

- Discover what your server exposes (tools/resources/prompts)
- Test individual operations without writing client code
- View JSON-RPC messages and responses in real-time
- Debug issues before integrating with AI applications

**Why use it?**

✅ **Rapid development cycle**: Test changes immediately without restarting Claude/VS Code  
✅ **Isolation**: Debug server logic separately from client behavior  
✅ **Visibility**: See exact request/response payloads for troubleshooting  
✅ **Documentation**: Understand what parameters tools accept

**What the Inspector shows:**

The browser opens to `http://localhost:6274` with 4 main tabs:

#### **Tools Tab**

Lists all callable tools with their schemas. Try:

- Click `echo` → Enter `{"message": "hello"}` → Click "Call Tool"
- Click `add_numbers` → Enter `{"a": 5, "b": 3}` → See result `{"result": 8}`

#### **Resources Tab**

Shows available resources (data sources). Try:

- Click `greeting://Alice` → See response: "Hello, Alice! Welcome to the context-hub."
- Change to `greeting://World` → Get custom greeting

#### **Prompts Tab**

Displays prompt templates. Try:

- Click `review_code` → Enter `{"code": "function add(a,b){return a+b}"}`
- See the generated prompt message with your code embedded

#### **Notifications Pane**

Shows server logs and JSON-RPC traffic for debugging.

**Quick Test Commands:**

```bash
# If port 6274 is busy, kill existing inspector:
lsof -ti:6274 | xargs kill -9

# Then restart:
npx @modelcontextprotocol/inspector node build/index.js
```

Press **Ctrl+C** in terminal to stop both inspector and server.

### 4. Use with VS Code

Add to `.vscode/mcp.json` (already created):

```json
{
  "servers": {
    "context-hub": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/MCP_Spike/build/index.js"]
    }
  }
}
```

Replace `/absolute/path/to/MCP_Spike` with your project path.

### 5. Use with Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "context-hub": {
      "command": "node",
      "args": ["/absolute/path/to/MCP_Spike/build/index.js"]
    }
  }
}
```

Restart Claude Desktop.

## Development Mode

Run directly with ts-node (no build step):

```bash
npm run dev
```

**Note**: The server runs indefinitely, waiting for MCP client connections over stdio. This is normal behavior—press Ctrl+C to stop.

## Extending

- Add new tools with `server.registerTool(name, config, handler)`.
- Add resources with `server.registerResource(name, templateOrUri, metadata, handler)`.
- Add prompts with `server.registerPrompt(name, config, handler)`.

Refer to the TypeScript SDK docs: https://github.com/modelcontextprotocol/typescript-sdk

## License

MIT