Runn MCP Server
by mmorey
README.md
# Runn MCP Server
An MCP (Model Context Protocol) server for [Runn.io](https://runn.io) - a resource planning and project forecasting platform. This server allows AI assistants like Claude, ChatGPT, and Cline to interact with your Runn data.
## Features
**37 tools** covering all Runn API endpoints, with built-in **in-memory caching** to reduce API calls:
### People
| Tool | Description |
|------|-------------|
| `get_all_people` | Fetches all people from Runn (with optional filters) |
| `get_person_by_id` | Fetches a specific person by their ID |
| `get_person_by_email` | Fetches a specific person by their email address |
### Projects
| Tool | Description |
|------|-------------|
| `get_all_projects` | Fetches all projects from Runn (with optional filters) |
| `get_project_by_id` | Fetches a specific project by its ID |
| `get_project_phases` | Fetches all phases for a project |
| `get_project_milestones` | Fetches all milestones for a project |
### Clients
| Tool | Description |
|------|-------------|
| `get_all_clients` | Fetches all clients from Runn (with optional filters) |
| `get_client_by_id` | Fetches a specific client by its ID |
| `get_client_projects` | Fetches all projects for a specific client |
### Assignments
| Tool | Description |
|------|-------------|
| `get_all_assignments` | Fetches all assignments (who is assigned to what) |
| `get_active_assignments` | Fetches only currently active assignments |
### Actuals (Logged Time)
| Tool | Description |
|------|-------------|
| `get_all_actuals` | Fetches all logged time with optional filters |
| `get_person_actuals` | Fetches logged time for a specific person |
| `get_project_actuals` | Fetches logged time for a specific project |
### Roles
| Tool | Description |
|------|-------------|
| `get_all_roles` | Fetches all job roles defined in Runn |
| `get_role_by_id` | Fetches a specific role by its ID |
### Contracts
| Tool | Description |
|------|-------------|
| `get_all_contracts` | Fetches all employment contracts |
| `get_person_contracts` | Fetches contracts for a specific person |
### Time Off & Holidays
| Tool | Description |
|------|-------------|
| `get_all_holidays` | Fetches all public holidays |
| `get_all_time_off` | Fetches all time off/leave entries |
| `get_person_time_off` | Fetches time off for a specific person |
### Milestones
| Tool | Description |
|------|-------------|
| `get_all_milestones` | Fetches all project milestones |
| `get_project_milestones` | Fetches milestones for a specific project |
### Other Resources
| Tool | Description |
|------|-------------|
| `get_all_rate_cards` | Fetches all billing rate cards |
| `get_all_placeholders` | Fetches all placeholder resources |
| `get_all_tags` | Fetches all tags/labels |
| `get_account` | Fetches account information and settings |
### Cache Management
| Tool | Description |
|------|-------------|
| `clear_cache` | Clears the in-memory cache to force fresh data on next request |
### Reports - Utilization & Revenue
| Tool | Description |
|------|-------------|
| `get_all_people_metrics` | BETA: Utilization metrics for all people |
| `get_person_metrics` | BETA: Person utilization metrics (billable %, revenue, costs, capacity) |
| `get_all_project_metrics` | BETA: Financial metrics for all projects |
| `get_project_metrics` | BETA: Project financial metrics (revenue, profit, margin, budget) |
### Reports - Hours
| Tool | Description |
|------|-------------|
| `get_person_hours_report` | Day-by-day hours report for a person |
| `get_project_hours_report` | Day-by-day hours report for a project |
### Reports - Totals
| Tool | Description |
|------|-------------|
| `get_all_project_totals` | Aggregated totals for all projects |
| `get_project_totals` | Aggregated totals for a specific project |
### Reports - Composite
| Tool | Description |
|------|-------------|
| `get_weekly_schedule_vs_actuals_report` | Compares scheduled hours vs actual timesheet hours per project for a given week |
| `get_weekly_person_schedule_vs_actuals_report` | Compares scheduled hours vs actual timesheet hours per person for a given week |
| `get_schedule_variance_report` | Person × project variance showing over-scheduled assignments over multiple weeks |
| `get_project_schedule_vs_actuals_report` | Project-level schedule vs actuals for any date range |
| `get_timesheet_compliance_report` | Flags people who haven't logged enough hours against their schedule |
| `get_availability_report` | Shows who has available capacity (bench time) for a given week |
| `get_project_health_report` | Portfolio health summary with red/amber/green status for all active projects |
| `get_revenue_forecast_report` | Compares forecasted revenue against actual recognized revenue |
| `get_client_profitability_report` | Aggregates revenue, costs, profit, and margin by client across all projects |
## In-Memory Caching
All API responses (except actuals and reports) are cached in-memory with automatic TTL-based expiration. This significantly reduces redundant API calls when the same data is requested multiple times within a session.
### Cache TTL Tiers
| TTL | Category | Endpoints |
|-----|----------|-----------|
| **10 minutes** | Stable reference data | Roles, tags, holidays, rate cards, account, placeholders |
| **5 minutes** | Semi-stable data | People, clients, contracts, time off |
| **2 minutes** | Dynamic data | Projects, assignments, milestones, phases |
| **No cache** | Time-sensitive data | Actuals, all reports (metrics, hours, totals) |
### Manual Cache Invalidation
Use the `clear_cache` tool to immediately invalidate all cached entries when you need guaranteed fresh data. The cache is also automatically cleared when the MCP server process restarts.
## Prerequisites
- Node.js 20.0.0 or higher
- A valid [Runn API key](https://app.runn.io/settings/api)
## Installation
### 1. Clone and Build
```bash
# Navigate to the project directory
cd runn-mcp
# Install dependencies
npm install
# Build the project
npm run build
```
### 2. Get Your Runn API Key
1. Log in to [Runn](https://app.runn.io)
2. Go to **Settings** → **API**
3. Generate a new API key
4. Copy the key for use in configuration
## Configuration
The server supports two transport modes:
- **stdio** — for local development (runs as a child process)
- **HTTP** — for production deployment (runs as a web server)
---
### Local Mode (stdio) — For Development
#### Claude Desktop
Add the following to your Claude Desktop configuration file:
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"runn": {
"command": "node",
"args": ["/absolute/path/to/runn-mcp/build/index.js"],
"env": {
"RUNN_API_KEY": "your-runn-api-key-here"
}
}
}
}
```
#### ChatGPT Desktop
```json
{
"mcpServers": {
"runn": {
"command": "node",
"args": ["/absolute/path/to/runn-mcp/build/index.js"],
"env": {
"RUNN_API_KEY": "your-runn-api-key-here"
}
}
}
}
```
#### Cline (VS Code Extension)
```json
{
"mcpServers": {
"runn": {
"command": "node",
"args": ["/absolute/path/to/runn-mcp/build/index.js"],
"env": {
"RUNN_API_KEY": "your-runn-api-key-here"
}
}
}
}
```
**Important**: Replace `/absolute/path/to/runn-mcp` with the actual absolute path to your runn-mcp directory.
---
### Remote Mode (HTTP) — For Production / Team Access
When deployed as an HTTP server, coworkers connect via URL instead of running the server locally.
#### Claude Desktop (Remote)
```json
{
"mcpServers": {
"runn": {
"url": "https://mcp-runn.gigaplayops.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
}
}
}
```
#### Cline (Remote)
Same format — point to the URL with the auth header in your MCP server settings.
---
## Role-Based Access Control
The HTTP server supports three access tiers. Each bearer token is mapped to a role, and the role determines which tools are available.
| Role | Description |
|---|---|
| `god` | Full access to all tools |
| `executive` | All tools (customizable — can restrict to financial/portfolio views) |
| `project-manager` | All tools (customizable — can restrict to operational tools only) |
Currently all roles have access to all tools. To restrict tools per role, edit `src/tool-permissions.ts` and change the minimum role for each tool.
### Token Configuration
Set the `MCP_AUTH_TOKENS` environment variable as JSON:
```
MCP_AUTH_TOKENS={"god":"token-aaa","executive":"token-bbb,token-ccc","project-manager":"token-ddd,token-eee"}
```
- Each role maps to one or more bearer tokens (comma-separated)
- Tokens must be unique across all roles
- Use long, random strings for production tokens (e.g., `openssl rand -hex 32`)
---
## Production Deployment
### Environment Variables
| Variable | Required | Description |
|---|---|---|
| `RUNN_API_KEY` | Yes | Your Runn API key |
| `MCP_AUTH_TOKENS` | Yes (HTTP mode) | JSON mapping roles to bearer tokens |
| `PORT` | No | HTTP port (default: 3000) |
### Running Locally (HTTP mode)
```bash
npm run build
RUNN_API_KEY=your-key MCP_AUTH_TOKENS='{"god":"test-token"}' npm run start:http
```
### Docker
```bash
# Build
docker build -t runn-mcp .
# Run
docker run -p 3000:3000 \
-e RUNN_API_KEY=your-key \
-e MCP_AUTH_TOKENS='{"god":"token-aaa","executive":"token-bbb"}' \
runn-mcp
```
Health check: `GET /health`
### AWS App Runner
The server is designed for AWS App Runner with Docker image source from ECR.
**One-time setup:**
1. **Create an ECR repository** named `runn-mcp`
2. **Create an App Runner service** with:
- Source: ECR image (`<account>.dkr.ecr.<region>.amazonaws.com/runn-mcp:latest`)
- Port: 3000
- Health check: HTTP `/health`
- Environment variables: `RUNN_API_KEY`, `MCP_AUTH_TOKENS`, `PORT=3000`
3. **Add custom domain** `mcp-runn.gigaplayops.com` in App Runner console
4. **Create CNAME** in Route53 pointing to the App Runner domain
### GitHub Actions CI/CD
The repo includes `.github/workflows/deploy.yml` that automatically builds and deploys on push to `main`.
**Required GitHub Secrets:**
| Secret | Description |
|---|---|
| `AWS_ACCESS_KEY_ID` | IAM credentials with ECR + App Runner permissions |
| `AWS_SECRET_ACCESS_KEY` | Corresponding secret key |
| `AWS_REGION` | e.g. `us-east-1` |
| `ECR_REPOSITORY` | ECR repository name (e.g. `runn-mcp`) |
| `APPRUNNER_SERVICE_ARN` | ARN of the App Runner service |
The workflow:
1. Checks out code and builds TypeScript
2. Builds Docker image and pushes to ECR (tagged with commit SHA + `latest`)
3. Triggers App Runner deployment via `aws apprunner start-deployment`
## Usage Examples
Once configured, you can ask your AI assistant questions like:
**People:**
- "Show me all active people in Runn"
- "Find the person with email john@example.com in Runn"
**Projects:**
- "What projects are currently active?"
- "Show me details for project ID 12345"
**Clients:**
- "List all our clients"
- "What projects does client X have?"
**Assignments:**
- "Who is assigned to which projects?"
- "Show me all active assignments"
## Available Tools
### People Tools
#### get_all_people
Fetches all people from Runn with optional filtering.
- `onlyActive` (optional, boolean): If true, only returns active people
- `modifiedAfter` (optional, string): ISO 8601 date string to filter by modification date
#### get_person_by_id
Fetches a specific person by their unique ID.
- `personId` (required, string): The unique identifier of the person
#### get_person_by_email
Fetches a specific person by their email address.
- `email` (required, string): The email address of the person
### Project Tools
#### get_all_projects
Fetches all projects from Runn with optional filtering.
- `onlyActive` (optional, boolean): If true, only returns active projects
- `modifiedAfter` (optional, string): ISO 8601 date string to filter by modification date
#### get_project_by_id
Fetches a specific project by its unique ID.
- `projectId` (required, string): The unique identifier of the project
### Client Tools
#### get_all_clients
Fetches all clients from Runn with optional filtering.
- `onlyActive` (optional, boolean): If true, only returns active clients
- `modifiedAfter` (optional, string): ISO 8601 date string to filter by modification date
#### get_client_by_id
Fetches a specific client by its unique ID.
- `clientId` (required, string): The unique identifier of the client
#### get_client_projects
Fetches all projects assigned to a specific client.
- `clientId` (required, string): The unique identifier of the client
### Assignment Tools
#### get_all_assignments
Fetches all assignments from Runn with optional filtering.
- `onlyActive` (optional, boolean): If true, only returns active assignments
- `modifiedAfter` (optional, string): ISO 8601 date string to filter by modification date
#### get_active_assignments
Fetches only currently active assignments from Runn.
- No parameters required
### Actuals Tools
#### get_all_actuals
Fetches all logged time (actuals) from Runn with optional filtering.
- `minDate` (optional, string): Minimum date filter (YYYY-MM-DD format)
- `maxDate` (optional, string): Maximum date filter (YYYY-MM-DD format)
- `projectId` (optional, string): Filter by project ID
- `personId` (optional, string): Filter by person ID
- `roleId` (optional, string): Filter by role ID
#### get_person_actuals
Fetches logged time for a specific person.
- `personId` (required, string): The unique identifier of the person
- `minDate` (optional, string): Minimum date filter (YYYY-MM-DD format)
- `maxDate` (optional, string): Maximum date filter (YYYY-MM-DD format)
#### get_project_actuals
Fetches logged time for a specific project.
- `projectId` (required, string): The unique identifier of the project
- `minDate` (optional, string): Minimum date filter (YYYY-MM-DD format)
- `maxDate` (optional, string): Maximum date filter (YYYY-MM-DD format)
### Reports Tools
#### get_person_metrics
BETA: Fetches utilization metrics for a person (billable utilization, revenue, costs, capacity).
- `personId` (required, string): The unique identifier of the person
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
#### get_project_metrics
BETA: Fetches financial metrics for a project (revenue, profit, costs, margin, budget).
- `projectId` (required, string): The unique identifier of the project
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
#### get_person_hours_report
Fetches day-by-day hours report for a person (scheduled vs actual).
- `personId` (required, string): The unique identifier of the person
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
#### get_project_hours_report
Fetches day-by-day hours report for a project (scheduled vs actual).
- `projectId` (required, string): The unique identifier of the project
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
#### get_all_project_totals
Fetches aggregated totals for all projects (hours, revenue, costs).
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
#### get_project_totals
Fetches aggregated totals for a specific project.
- `projectId` (required, string): The unique identifier of the project
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
#### get_all_people_metrics
BETA: Fetches utilization metrics for all people.
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
#### get_all_project_metrics
BETA: Fetches financial metrics for all projects.
- `startDate` (required, string): Start date for the report period (YYYY-MM-DD)
- `endDate` (required, string): End date for the report period (YYYY-MM-DD)
- `todayDate` (optional, string): Reference date for calculations (YYYY-MM-DD)
### Roles Tools
#### get_all_roles
Fetches all job roles defined in Runn.
- No parameters required
#### get_role_by_id
Fetches a specific role by its unique ID.
- `roleId` (required, string): The unique identifier of the role
### Contracts Tools
#### get_all_contracts
Fetches all employment contracts from Runn.
- No parameters required
#### get_person_contracts
Fetches all employment contracts for a specific person.
- `personId` (required, string): The unique identifier of the person
### Holidays Tools
#### get_all_holidays
Fetches all public holidays configured in Runn.
- No parameters required
### Time Off Tools
#### get_all_time_off
Fetches all time off (leave/vacation) entries.
- `personId` (optional, string): Filter by person ID
- `minDate` (optional, string): Minimum date filter (YYYY-MM-DD)
- `maxDate` (optional, string): Maximum date filter (YYYY-MM-DD)
#### get_person_time_off
Fetches time off for a specific person.
- `personId` (required, string): The unique identifier of the person
- `minDate` (optional, string): Minimum date filter (YYYY-MM-DD)
- `maxDate` (optional, string): Maximum date filter (YYYY-MM-DD)
### Milestones Tools
#### get_all_milestones
Fetches all project milestones from Runn.
- `projectId` (optional, string): Filter by project ID
#### get_project_milestones
Fetches all milestones for a specific project.
- `projectId` (required, string): The unique identifier of the project
### Phases Tools
#### get_project_phases
Fetches all phases for a specific project.
- `projectId` (required, string): The unique identifier of the project
### Rate Cards Tools
#### get_all_rate_cards
Fetches all billing rate cards from Runn.
- No parameters required
### Placeholders Tools
#### get_all_placeholders
Fetches all placeholder resources from Runn.
- No parameters required
### Tags Tools
#### get_all_tags
Fetches all tags/labels from Runn.
- No parameters required
### Account Tools
#### get_account
Fetches your Runn account information and settings.
- No parameters required
### Cache Management Tools
#### clear_cache
Clears the in-memory cache for all Runn API responses. Use this if you need fresh data immediately without waiting for cached entries to expire.
- No parameters required
### Composite Report Tools
#### get_weekly_schedule_vs_actuals_report
Generates a composite report comparing scheduled assignment hours vs actual timesheet hours per project for a given week (Monday–Sunday). Fetches assignments, actuals, and projects in parallel, calculates overlap days, and returns structured JSON with per-project and total rows including a delta (scheduled − actual).
- `weekOf` (optional, string): Any date (YYYY-MM-DD) within the desired week. The tool automatically resolves to the Monday–Sunday window containing that date. Defaults to last week if omitted.
#### get_client_profitability_report
Aggregates financial metrics (revenue, costs, profit, margin) by client across all their projects. Ranks clients from most to least profitable, with per-project breakdowns nested within each client. Also shows projects with no client assignment. Useful for executives to understand which clients are most/least profitable and where to focus account management.
- `startDate` (optional, string): Start date for the report period (YYYY-MM-DD). Defaults to first day of current month.
- `endDate` (optional, string): End date for the report period (YYYY-MM-DD). Defaults to last day of current month.
## Development
```bash
# Install dependencies
npm install
# Build the project
npm run build
# Watch mode (auto-rebuild on changes)
npm run dev
# Run the server (for testing)
npm start
```
## Troubleshooting
### "RUNN_API_KEY environment variable is required"
Make sure you've added the `RUNN_API_KEY` to your MCP server configuration's `env` section.
### Server not connecting
1. Verify the path to `build/index.js` is correct and absolute
2. Ensure you've run `npm run build` after any changes
3. Check that Node.js 20+ is installed: `node --version`
### API errors
1. Verify your Runn API key is valid
2. Check that your Runn account has API access enabled
3. Ensure you have permissions to access the requested data
## License
MIT
## Credits
- Uses the [runn-api-client](https://www.npmjs.com/package/runn-api-client) npm package
- Built with the [Model Context Protocol SDK](https://modelcontextprotocol.io)