mcp-relay-kit
README.md
# mcp-relay-kit
One MCP server for all your tools. Two tools. ~2,000 tokens.
---
Every MCP server you add dumps its full tool list into your AI's context. The official Asana MCP alone uses 25,000+ tokens. Add Sentry, GitHub, Linear, and PostHog and you've burned 100k+ tokens before the conversation starts.
MCP Relay Kit sits between your AI tool and your services, exposing everything through just **2 tools** — `search` and `execute` — at a fixed cost of ~2,000 tokens. Add 5 services or 50; the context cost stays the same.
## How it works
```
Your AI tool
│
├─ search("post comment to asana")
│ → returns: asana.post_comment schema + params
│
└─ execute("asana", "post_comment", {"task_id": "123", "text": "Done"})
→ calls Asana API, returns result
```
Your AI calls `search` to discover what's available, then `execute` to use it.
## Why this approach
**Context cost.** Each MCP server injects its full tool list into the context window. One server with 20 tools might cost 15,000 tokens. Five servers and you've lost 75,000+ tokens to tool definitions alone — before your code, your conversation, or your files. The relay collapses all of that into 2 tool definitions at ~2,000 tokens, regardless of how many services sit behind it.
**Fewer mistakes.** With 5+ MCP servers loaded, your AI has 100+ tools to pick from. It often picks the wrong one or hallucinates parameters. With the relay, the AI searches first (`search("create issue")`), gets back the exact schema, then calls it. Less guesswork, fewer retries.
**One config, one process.** Instead of managing 5 separate MCP server configs, you manage one relay config. One process to start, one place to add credentials, one thing to debug when something breaks.
## Get started
### Install from npm
```
npm install -g mcp-relay-kit
# or: npm install -g @clockwork-is/mcp-relay-kit
```
### Interactive setup
The fastest way to configure everything:
```
npx mcp-relay-kit --setup
```
This walks you through picking services, entering credentials, and configuring your AI tool (Claude Code, Gemini CLI, Cursor, Windsurf, or Codex).
## Manual setup
### 1. Clone and build
```
git clone https://github.com/SGudbrandsson/mcp-relay-kit.git
cd mcp-relay-kit
npm install && npm run build
```
### 2. Create a config file
```json
{
"services": {
"asana": {
"token": "${ASANA_TOKEN}",
"workspace": "your-workspace-gid"
},
"sentry": {
"token": "${SENTRY_AUTH_TOKEN}",
"organization": "your-org",
"project": "your-project"
}
}
}
```
Save this as `~/.config/mcp-relay-kit/config.json` (or any path you prefer).
Values wrapped in `${VAR}` are resolved from environment variables at startup.
### 3. Point your AI tool at the relay
Add this to your project's `.mcp.json`:
```json
{
"mcpServers": {
"relay": {
"command": "mcp-relay-kit",
"env": {
"GATEWAY_CONFIG": "/path/to/your/config.json"
}
}
}
}
```
If installed globally, `mcp-relay-kit` is available as a command. Otherwise use `npx mcp-relay-kit` or the full path to `node_modules/.bin/mcp-relay-kit`.
## Proxy any MCP server
Already using an MCP server for GitHub, Slack, or anything else? Add it to the relay instead of loading it directly — same functionality, fraction of the context cost.
```json
{
"services": {
"asana": { "token": "..." }
},
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "ghp_..." }
}
}
}
```
The relay spawns each MCP server as a child process, discovers its tools at startup, and exposes them through the same `search` and `execute` interface. A server with 20+ tools still costs only ~2,000 tokens.
The `mcpServers` format is the same one used by Claude Code, Cursor, and other AI tools — so you can often just move an existing entry from your `.mcp.json` into the relay config.
## Tools
The relay exposes exactly 2 MCP tools:
### `search(query)`
Discover available actions across all configured services.
- **query** (string): Search text — matches service names, action names, and descriptions
- Returns: Array of matching actions with their parameter schemas
```
search("task comment")
→ [
{ service: "asana", action: "post_comment", params: { task_id: {...}, text: {...} } },
{ service: "asana", action: "get_task", params: { task_id: {...} } }
]
```
Pass an empty string to list all available actions.
### `execute(service, action, params)`
Call a specific action on a service.
- **service** (string): Service name (e.g., "asana")
- **action** (string): Action name (e.g., "post_comment")
- **params** (string): JSON string of parameters (e.g., `{"task_id": "123", "text": "Done"}`)
```
execute("asana", "post_comment", '{"task_id": "123", "text": "PR merged"}')
→ { success: true, data: { gid: "456", text: "PR merged" } }
```
## Built-in services
### Asana
Project management — tasks, comments, and project tracking.
| Action | Description |
|--------|-------------|
| `get_task` | Get task details by GID |
| `update_task` | Update task name, notes, status, due date, assignee, custom fields (e.g. Priority) |
| `create_task` | Create a new task in a project |
| `post_comment` | Post a comment on a task |
| `search_tasks` | Search tasks by text query |
| `list_project_tasks` | List tasks in a project (incomplete by default) |
| `list_project_sections` | List a project's sections (gid + name) |
| `create_section` | Create a section in a project |
| `update_section` | Rename a section |
| `delete_section` | Delete an empty section |
| `move_task_to_section` | Move a task into a section, with optional ordering |
| `list_tags` | List workspace tags (gid + name) |
| `create_tag` | Create a tag in the workspace |
| `add_tag_to_task` | Add a tag to a task |
| `remove_tag_from_task` | Remove a tag from a task |
| `list_project_custom_fields` | List a project's custom fields and enum option GIDs (e.g. Priority) |
| `create_project` | Create a new project (e.g. a sprint board) |
| `update_project` | Update project settings (name, notes, visibility, team, owner, color, view, archived) |
| `add_members_for_project` | Add members to a project |
| `remove_members_for_project` | Remove members from a project |
| `list_project_memberships` | List project members and their access levels |
| `update_project_membership` | Change a member's access level (e.g. promote to admin) |
| `list_teams` | List teams in the workspace (gid + name) |
**Config:**
```json
{
"token": "your-personal-access-token",
"workspace": "your-workspace-gid"
}
```
### Sentry
Error tracking — list, inspect, and manage issues and events.
| Action | Description |
|--------|-------------|
| `list_issues` | List issues for a project, with optional Sentry search query |
| `get_issue` | Get details of a specific issue by ID |
| `get_issue_events` | Get occurrences (events) for an issue |
| `get_event_details` | Get full event details including stack trace |
| `resolve_issue` | Mark an issue as resolved |
| `unresolve_issue` | Reopen a resolved issue |
| `update_issue` | Update an issue (assign, change status, set priority) |
**Config:**
```json
{
"token": "your-sentry-auth-token",
"organization": "your-org-slug",
"project": "your-project-slug"
}
```
### Linear
Issue tracking — create, update, search, and manage issues and projects via GraphQL.
| Action | Description |
|--------|-------------|
| `search_issues` | Search issues by text query |
| `get_issue` | Get details of an issue by identifier (e.g., "ENG-123") |
| `create_issue` | Create a new issue |
| `update_issue` | Update an existing issue |
| `delete_issue` | Archive an issue |
| `list_teams` | List all teams (use to find team IDs) |
| `list_projects` | List projects, optionally filtered by team |
| `list_workflow_states` | List workflow states for a team |
| `add_comment` | Add a comment to an issue |
| `list_labels` | List available issue labels |
**Config:**
```json
{
"token": "your-linear-api-key"
}
```
### PostHog
Product analytics — query events, persons, session recordings, and insights.
| Action | Description |
|--------|-------------|
| `query_events` | Query recent events, filterable by type, person, and date range |
| `get_person` | Get a person by ID or distinct_id |
| `search_persons` | Search persons by email or properties |
| `get_person_events` | Get all events for a person (activity timeline) |
| `query_insights` | Get a saved insight (trend, funnel, etc.) by ID |
| `list_cohorts` | List all cohorts |
| `get_session_recordings` | Find session recordings, filterable by person and date |
| `get_session_recording` | Get a specific session recording with events and details |
**Config:**
```json
{
"token": "your-posthog-personal-api-key",
"project_id": "your-project-id"
}
```
### Figma
Design platform — inspect files, export images, manage comments, and browse team libraries.
| Action | Description |
|--------|-------------|
| `get_file` | Get a Figma file by key (document structure, components, metadata) |
| `get_file_nodes` | Get specific nodes from a file by their IDs |
| `get_images` | Render nodes as images (PNG, JPG, SVG, or PDF) |
| `get_comments` | Get comments on a file |
| `post_comment` | Post a comment on a file (supports threaded replies) |
| `get_file_components` | Get published components in a file |
| `get_file_styles` | Get published styles in a file |
| `get_image_fills` | Get download URLs for all images used as fills (photos, textures, backgrounds) |
| `get_team_projects` | List projects for a team |
| `get_project_files` | List files in a project |
| `get_team_components` | Get published components for a team library |
| `get_team_styles` | Get published styles for a team library |
| `get_file_versions` | Get version history of a file |
**Config:**
```json
{
"token": "your-figma-personal-access-token",
"team_id": "your-team-id"
}
```
## Add your own service
1. Create `src/services/your-service.ts`:
```typescript
import type { ServiceAdapter, ServiceAction } from '../types.js';
const actions: ServiceAction[] = [
{
name: 'do_something',
description: 'What this action does',
params: {
id: { type: 'string', description: 'Resource ID', required: true },
optional_field: { type: 'string', description: 'Optional', required: false },
},
execute: async (params, config) => {
const token = config.token as string;
// Call your API...
return result;
},
},
];
export const yourServiceAdapter: ServiceAdapter = {
name: 'your-service',
description: 'What this service does',
actions,
};
```
2. Register in `src/services/index.ts`:
```typescript
import { yourServiceAdapter } from './your-service.js';
export const availableAdapters: Record<string, ServiceAdapter> = {
asana: asanaAdapter,
'your-service': yourServiceAdapter, // add here
};
```
3. Add config in your project's gateway config:
```json
{
"services": {
"asana": { "token": "..." },
"your-service": { "token": "...", "other_config": "..." }
}
}
```
The relay auto-registers any service that appears in both the adapter registry and the config file.
## Multiple instances of the same service
You can register the same service multiple times with different configurations using `service:label` syntax in your config keys:
```json
{
"services": {
"sentry:production": {
"token": "${SENTRY_PROD_TOKEN}",
"organization": "my-org",
"project": "prod-backend"
},
"sentry:staging": {
"token": "${SENTRY_STAGING_TOKEN}",
"organization": "my-org",
"project": "staging-backend"
},
"supabase": {
"token": "${SUPABASE_TOKEN}",
"service_role_key": "${SUPABASE_SERVICE_ROLE_KEY}",
"project_ref": "abcdefghij"
}
}
}
```
- Use `service:label` to create named instances (e.g., `"sentry:production"`, `"supabase:app-db"`)
- Plain keys like `"supabase"` still work for single instances
- Each instance gets its own config and appears separately in search results
- Use the full instance name when calling execute: `execute("sentry:production", "list_issues", "{}")`
- Search matches against both the service type and the label, so searching "production" finds all production instances
## Per-project configuration
Different projects can use different configs pointing to different services:
```
~/.config/mcp-relay-kit/
├── project-a.json → asana + slack + sentry
├── project-b.json → asana + github
└── personal.json → asana
```
Each project's `.mcp.json` points to its own config via `GATEWAY_CONFIG`.
## Testing
```
npm test # run all tests
npm run test:watch # watch mode
```
Tests cover the service registry (search, execute, validation), config loading (env var interpolation, error handling), each built-in adapter (mocked HTTP), and an E2E test that starts the real MCP server process and communicates via stdio.
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues