README.mdβ’12.4 kB
# Timezone MCP Server
A dual-interface timezone server supporting both **MCP (Model Context Protocol)** for LLM discovery and **REST API** for direct HTTP access. Get available regions, cities, and current time in any timezone with ISO 8601 formatted timestamps.
## Features
- **π€ MCP Protocol Support** - LLMs can auto-discover and use timezone tools via JSON-RPC
- **π REST API** - Traditional HTTP endpoints with Swagger/OpenAPI documentation
- **π OAuth2 Authentication** - Secure access with user allowlisting and client credentials
- **β° Timezone Operations** - Get regions, cities, and current time in any timezone
- **π
ISO 8601 Timestamps** - Local datetime with offset, UTC datetime, and timezone offset
- **π§ͺ 100% Test Coverage** - Comprehensive unit and e2e tests (Vitest)
- **π§ NestJS & TypeScript** - Modern, type-safe development
- **π Dual Documentation** - Swagger UI for humans, MCP schema for LLMs
- **π¨ Code Quality** - ESLint, Prettier, and automated formatting
## Getting Started
### Prerequisites
- Node.js 22.20.0+ (LTS recommended)
- pnpm 10.13.1 (managed via Corepack)
> **π¦ Package Manager:** This project uses **pnpm** exclusively via **Corepack** (built into Node.js). The exact pnpm version is enforced by the `packageManager` field in package.json. After installing Node.js, run `corepack enable` to activate pnpm support.
>
> **π§ Version Management:** This project uses **fnm** (Fast Node Manager) or **nvm** to manage Node.js versions. The `.node-version` file will automatically switch to the correct Node.js version when you `cd` into the project directory (if you have fnm/nvm shell integration enabled).
### Installation
```bash
pnpm install
```
### Authentication Setup
This server requires OAuth2 authentication for all endpoints except `/health`. You need to configure authentication before running the server.
#### Step 1: Configure Environment Variables
Copy the example environment file and edit it:
```bash
cp example.env .env
```
Edit `.env` and configure:
1. **JWT Settings** (required):
```env
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=3600
```
2. **OAuth2 Provider** (choose one - Google, GitHub, or any OAuth2-compliant provider):
For **Google**:
```env
OAUTH2_AUTHORIZATION_URL=https://accounts.google.com/o/oauth2/v2/auth
OAUTH2_TOKEN_URL=https://oauth2.googleapis.com/token
OAUTH2_USER_INFO_URL=https://www.googleapis.com/oauth2/v2/userinfo
OAUTH2_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
OAUTH2_CLIENT_SECRET=your-google-client-secret
OAUTH2_CALLBACK_URL=http://localhost:3000/auth/callback
OAUTH2_SCOPE=openid email profile
```
For **GitHub**:
```env
OAUTH2_AUTHORIZATION_URL=https://github.com/login/oauth/authorize
OAUTH2_TOKEN_URL=https://github.com/login/oauth/access_token
OAUTH2_USER_INFO_URL=https://api.github.com/user
OAUTH2_CLIENT_ID=your-github-client-id
OAUTH2_CLIENT_SECRET=your-github-client-secret
OAUTH2_CALLBACK_URL=http://localhost:3000/auth/callback
OAUTH2_SCOPE=user:email
```
3. **User Allowlist** (required):
```env
ALLOWED_EMAILS=user1@example.com,user2@example.com,admin@company.com
```
#### Step 2: Set Up OAuth2 Provider
Create OAuth2 credentials with your provider:
- **Google**: [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
- **GitHub**: [GitHub Developer Settings](https://github.com/settings/developers)
Set the **Authorized redirect URI** to: `http://localhost:3000/auth/callback`
#### Step 3: Authentication Flow
1. Visit `http://localhost:3000/auth/login`
2. Redirected to OAuth2 provider (Google/GitHub/etc.)
3. Sign in and authorize
4. Redirected back with JWT token displayed on screen
5. Copy the token and use it in API requests:
```bash
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:3000/timezones/regions
```
### Running the Server
This project provides **three interfaces** that can run independently:
#### Option 1: MCP Server via stdio (for local LLM clients)
```bash
# Development mode with hot reload
pnpm mcp:dev
# Production mode
pnpm build
pnpm mcp:start
```
Uses **stdio transport** - launches as a subprocess. Perfect for Claude Desktop config file.
#### Option 2: MCP Server via HTTP (for remote LLM clients)
```bash
# Development mode with hot reload
pnpm mcp:http:dev
# Production mode
pnpm build
pnpm mcp:http
```
Uses **Streamable HTTP over HTTP** - accessible at `http://localhost:3001/mcp`
Perfect for Claude Desktop's "Add Custom Connector" UI! π―
#### Option 3: REST API Server (for HTTP clients)
```bash
# Development mode with hot reload
pnpm dev
# Production mode
pnpm build
pnpm start:prod
# Open Swagger UI in browser
pnpm swagger
```
The REST API will start on [http://localhost:3000](http://localhost:3000)
Interactive Swagger/OpenAPI documentation is available at [http://localhost:3000/api](http://localhost:3000/api)
### Available Endpoints
**Authentication:**
- `GET /auth/login` - Initiate OAuth2 login (redirects to provider)
- `GET /auth/callback` - OAuth2 callback (returns JWT token)
**Health:**
- `GET /health` - Health check endpoint (public, no authentication required)
```json
{
"status": "ok",
"timestamp": "2025-10-17T18:30:45.123Z"
}
```
**Timezone API:** (requires authentication - include `Authorization: Bearer TOKEN` header)
- `GET /timezones/regions` - Get all available timezone regions
```json
{
"regions": [
"Africa",
"America",
"Antarctica",
"Asia",
"Atlantic",
"Australia",
"Europe",
"Indian",
"Pacific"
],
"count": 15
}
```
- `GET /timezones/regions/:region/cities` - Get all cities in a specific region
```json
{
"region": "America",
"cities": ["New_York", "Los_Angeles", "Chicago", "Denver", "Phoenix"],
"count": 150
}
```
- `GET /timezones/:region/:city` - Get current time in a specific timezone
```json
{
"timezone": "America/New_York",
"datetime_local": "2025-10-17T13:47:23-04:00",
"datetime_utc": "2025-10-17T17:47:23.345Z",
"timezone_offset": "-04:00",
"timestamp": 1760723243345
}
```
## MCP Protocol Integration
### What is MCP?
[Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standardized protocol that allows LLMs to discover and use tools automatically. Instead of manually telling an LLM about your API endpoints, MCP-enabled clients like Claude Desktop can:
1. **Auto-discover** available tools via `tools/list`
2. **Inspect schemas** to understand input parameters
3. **Execute tools** via JSON-RPC `tools/call` requests
### Testing with MCP Inspector (Quickest)
The MCP Inspector is a web UI for testing your MCP server without needing Claude Desktop:
```bash
pnpm install
pnpm build
pnpm mcp:inspector
```
This opens `http://localhost:5173` where you can:
- See all 3 tools listed
- Inspect tool schemas and parameters
- Execute tools and see JSON responses
- Debug without installing anything else
### Setting Up with Claude Desktop
#### Method 1: Local stdio Connection (Recommended)
1. Build the project:
```bash
pnpm install
pnpm build
```
2. Edit Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"timezone": {
"command": "node",
"args": ["/absolute/path/to/demo-mcp/dist/main.js"]
}
}
}
```
3. Restart Claude Desktop
4. Claude can now auto-discover and use the timezone tools!
### Available MCP Tools
| Tool | Parameters | Description |
| ------------------- | ----------------------------------- | -------------------------------------- |
| `get_regions` | None | Get list of all timezone regions |
| `get_cities` | `region: string` | Get cities in a specific region |
| `get_timezone_info` | `region: string`<br/>`city: string` | Get current time with ISO 8601 formats |
### Architecture
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Timezone MCP Server β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β REST API MCP (HTTP/SSE) MCP (stdio) β
β Port 3000 Port 3001 subprocess β
β β β β β
β ββββββββββ βββββββββββ βββββββββββ β
β βNestJS β β MCP β β MCP β β
β β HTTP β β HTTP β β stdio β β
β βββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ β
β β β β β
β βββββββββββββββββ΄ββββββββββββββββββββ β
β β β
β TimezoneService β
β (Shared Business Logic) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
All three interfaces use the same `TimezoneService`, ensuring consistent behavior across REST API, remote MCP, and local MCP connections.
## Testing
```bash
# Run all tests (includes MCP stdio e2e tests)
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:cov
# Run tests with UI
pnpm test:ui
# Run e2e tests only
pnpm test:e2e
```
**Test Coverage**: 103/109 tests passing (94.5%). The HTTP MCP transport has comprehensive manual testing via MCP Inspector but automated e2e tests are pending due to SSE timing complexity.
**Manual Testing HTTP MCP Server:**
```bash
# Start HTTP MCP server
pnpm mcp:http:dev
# Test with MCP Inspector
pnpm mcp:inspector
# Test with Claude Desktop
# Settings > Connectors > Add Custom Connector
# URL: http://localhost:3001/mcp
```
## Code Quality
```bash
# Lint code
pnpm lint
# Fix lint issues
pnpm lint:fix
# Format code
pnpm format
# Check formatting
pnpm format:check
```
## Documentation
Additional guides and documentation are available in the [`docs/`](docs/) directory:
- **[Local Testing Guide](docs/LOCAL_TESTING.md)** - Step-by-step instructions for testing with MCP Inspector
- **[Deployment Guide](docs/DEPLOYMENT.md)** - Deploy to Google Cloud Run
- **[Deployment Success Guide](docs/DEPLOYMENT_SUCCESS.md)** - Post-deployment verification
## Scripts Reference
### Server Scripts
- `pnpm dev` - Start REST API server in development mode
- `pnpm start` - Start REST API server
- `pnpm start:prod` - Start REST API server in production mode
- `pnpm mcp:dev` - Start MCP server (stdio) in development mode
- `pnpm mcp:start` - Start MCP server (stdio) in production mode
- `pnpm mcp:http:dev` - Start MCP server (HTTP/SSE) in development mode
- `pnpm mcp:http` - Start MCP server (HTTP/SSE) in production mode
- `pnpm mcp:inspector` - Start MCP Inspector (debug/test MCP tools in browser)
- `pnpm swagger` - Open Swagger UI in default browser
### Build & Test
- `pnpm build` - Build the project (compiles TypeScript to dist/)
- `pnpm test` - Run all tests (84 tests including MCP e2e)
- `pnpm test:watch` - Run tests in watch mode
- `pnpm test:cov` - Run tests with coverage report (100% coverage)
- `pnpm test:ui` - Run tests with Vitest UI
- `pnpm test:e2e` - Run end-to-end tests only
### Code Quality
- `pnpm lint` - Lint code with ESLint
- `pnpm lint:fix` - Fix linting issues automatically
- `pnpm format` - Format code with Prettier
- `pnpm format:check` - Check if code is properly formatted
- `deps:check` - Check if dependencies can be upgraded (minor/patch only)
- `deps:update` - Upgrade dependencies (minor/patch only)