Skip to main content
Glama
ptamb3

Shortcut MCP Server

by ptamb3
README.md
# Shortcut MCP Server

MCP server for connecting Claude, Codex, or any MCP client to the [Shortcut](https://shortcut.com) project management API. Provides **73 tools** covering the full Shortcut API v3 surface.

## How it works

This project implements the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) — an open standard that lets AI assistants call external tools.

```
┌──────────────────┐       stdio        ┌──────────────────┐      HTTPS       ┌──────────────────┐
│   MCP Client     │◄──────────────────►│   MCP Server     │◄────────────────►│  Shortcut API    │
│ (Claude Desktop, │   JSON-RPC over    │ (this project)   │  REST API v3     │  api.app.        │
│  Claude Code,    │   stdin/stdout     │                  │  with API token  │  shortcut.com    │
│  VS Code, etc.)  │                    │  src/index.ts    │                  │                  │
└──────────────────┘                    └──────────────────┘                  └──────────────────┘
```

**Server** (`src/index.ts`): Registers 73 tools with the MCP SDK. When a client calls a tool, the server validates the input (via Zod schemas), makes the corresponding REST request to the Shortcut API, and returns the result. All write operations are protected by confirmation guards.

**Client** (`src/client.ts`): A standalone test client for development. It spawns the server as a child process, connects over stdio, and lets you call any tool from the command line:

```bash
# List available tools
npm run client

# Call a specific tool
npm run client -- list_epics
npm run client -- get_story '{"story_id": 123}'
npm run client -- create_story '{"name": "Test", "project_id": 101, "dry_run": true}'
```

In production, you don't use the test client — your MCP-compatible AI assistant (Claude Desktop, Claude Code, VS Code, etc.) acts as the client and calls tools automatically.

## Setup

1. Install dependencies:

```bash
npm install
```

2. Set your API token:

```bash
cp .env.example .env
# then edit .env and set SHORTCUT_API_TOKEN to your Shortcut API token
```

Or export directly:

```bash
export SHORTCUT_API_TOKEN="<your-token>"
```

You can generate a Shortcut API token at **Settings → API Tokens** in your Shortcut workspace.

3. Build:

```bash
npm run build
```

## Run

```bash
npm start
```

## Integration

### Claude Desktop

Add the following to your Claude Desktop config file:

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "shortcut": {
      "command": "node",
      "args": ["/absolute/path/to/shortcut-mcp-server/dist/index.js"],
      "env": {
        "SHORTCUT_API_TOKEN": "<your-shortcut-token>"
      }
    }
  }
}
```

Restart Claude Desktop after saving.

### Claude Code (CLI)

Add to `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "shortcut": {
      "command": "node",
      "args": ["/absolute/path/to/shortcut-mcp-server/dist/index.js"],
      "env": {
        "SHORTCUT_API_TOKEN": "<your-shortcut-token>"
      }
    }
  }
}
```

### VS Code (Claude Extension)

Add to your project's `.vscode/mcp.json` or workspace settings:

```json
{
  "mcpServers": {
    "shortcut": {
      "command": "node",
      "args": ["/absolute/path/to/shortcut-mcp-server/dist/index.js"],
      "env": {
        "SHORTCUT_API_TOKEN": "<your-shortcut-token>"
      }
    }
  }
}
```

> **Note**: In all configs above, replace `/absolute/path/to/shortcut-mcp-server` with the actual path where you cloned this repo, and `<your-shortcut-token>` with your Shortcut API token.

## Guardrails for write tools

All mutating tools support safety guardrails:

- `confirm="CONFIRM_WRITE"` is required to execute any write/update/create.
- `dry_run=true` returns the exact request payload without changing Shortcut data.
- `confirm_archive=true` is required when archiving.
- `confirm_delete=true` is required for delete operations (in addition to `confirm`).
- Read tools do not support `dry_run`; passing it returns a validation error.

Recommended flow for writes:

1. Run with `dry_run=true` and validate the payload.
2. Re-run with `confirm="CONFIRM_WRITE"` (and `confirm_archive=true` / `confirm_delete=true` if applicable).

## Tools (73)

| # | Tool | Description | Type |
|---|------|-------------|------|
| | **Stories** | | |
| 1 | `search_stories` | Search stories with Shortcut query syntax | Read |
| 2 | `get_story` | Get a story by ID | Read |
| 3 | `create_story` | Create a story in a project | Write |
| 4 | `update_story` | Update fields on an existing story | Write |
| 5 | `delete_story` | Permanently delete a story | Delete |
| 6 | `archive_story` | Archive a story | Archive |
| 7 | `transition_story_state` | Move a story to a workflow state by ID or name | Write |
| 8 | `get_story_history` | Get change history of a story | Read |
| 9 | `bulk_update_stories` | Update multiple stories at once | Write |
| 10 | `bulk_move_stories` | Move multiple stories to a different project/state | Write |
| 11 | `bulk_delete_stories` | Permanently delete multiple stories | Delete |
| | **Story Comments** | | |
| 12 | `list_story_comments` | List comments for a story | Read |
| 13 | `create_story_comment` | Add a comment to a story | Write |
| 14 | `update_story_comment` | Update a comment on a story | Write |
| 15 | `delete_story_comment` | Delete a comment | Delete |
| | **Story Tasks (Checklists)** | | |
| 16 | `list_story_tasks` | List checklist tasks on a story | Read |
| 17 | `create_story_task` | Add a checklist task | Write |
| 18 | `update_story_task` | Update a checklist task (e.g. mark complete) | Write |
| 19 | `delete_story_task` | Delete a checklist task | Delete |
| | **Story Links** | | |
| 20 | `create_story_link` | Link two stories (blocks, relates to, duplicates) | Write |
| 21 | `delete_story_link` | Delete a story link | Delete |
| | **Story Labels** | | |
| 22 | `add_story_label` | Add a label to a story | Write |
| 23 | `remove_story_label` | Remove a label from a story | Write |
| | **Story VCS Integration** | | |
| 24 | `list_story_branches` | List Git branches associated with a story | Read |
| 25 | `list_story_commits` | List Git commits associated with a story | Read |
| 26 | `list_story_pull_requests` | List pull requests associated with a story | Read |
| 27 | `list_story_external_links` | List external links on a story | Read |
| | **Epics** | | |
| 28 | `list_epics` | List epics | Read |
| 29 | `get_epic` | Get an epic by ID | Read |
| 30 | `create_epic` | Create an epic | Write |
| 31 | `update_epic` | Update an epic | Write |
| 32 | `delete_epic` | Permanently delete an epic | Delete |
| 33 | `archive_epic` | Archive an epic | Archive |
| 34 | `search_epics` | Search epics with query syntax | Read |
| 35 | `list_epic_stories` | List stories in an epic | Read |
| 36 | `add_epic_label` | Add a label to an epic | Write |
| | **Epic Comments** | | |
| 37 | `list_epic_comments` | List comments on an epic | Read |
| 38 | `create_epic_comment` | Add a comment to an epic | Write |
| | **Iterations (Sprints)** | | |
| 39 | `list_iterations` | List iterations | Read |
| 40 | `get_iteration` | Get an iteration by ID | Read |
| 41 | `create_iteration` | Create an iteration | Write |
| 42 | `update_iteration` | Update an iteration | Write |
| 43 | `delete_iteration` | Permanently delete an iteration | Delete |
| 44 | `get_iteration_stories` | List stories in an iteration | Read |
| | **Milestones** | | |
| 45 | `list_milestones` | List milestones | Read |
| 46 | `get_milestone` | Get a milestone by ID | Read |
| 47 | `create_milestone` | Create a milestone | Write |
| 48 | `update_milestone` | Update a milestone | Write |
| 49 | `delete_milestone` | Permanently delete a milestone | Delete |
| 50 | `list_milestone_epics` | List epics in a milestone | Read |
| | **Projects** | | |
| 51 | `list_projects` | List projects | Read |
| 52 | `get_project` | Get a project by ID | Read |
| 53 | `create_project` | Create a project | Write |
| 54 | `update_project` | Update a project | Write |
| 55 | `delete_project` | Permanently delete a project | Delete |
| | **Labels** | | |
| 56 | `list_labels` | List all labels | Read |
| 57 | `get_label` | Get a label by ID | Read |
| 58 | `create_label` | Create a label | Write |
| 59 | `update_label` | Update a label | Write |
| 60 | `delete_label` | Permanently delete a label | Delete |
| | **Workflows** | | |
| 61 | `list_workflows` | List all workflows and their states | Read |
| | **Members & Groups** | | |
| 62 | `list_members` | List workspace members | Read |
| 63 | `get_member` | Get a member by UUID | Read |
| 64 | `list_groups` | List teams/groups | Read |
| 65 | `get_group` | Get a team/group by UUID | Read |
| | **Linked Files** | | |
| 66 | `list_linked_files` | List all linked files | Read |
| 67 | `get_linked_file` | Get a linked file by ID | Read |
| 68 | `create_linked_file` | Create a linked file reference (e.g. Google Doc, Figma) | Write |
| 69 | `update_linked_file` | Update a linked file | Write |
| 70 | `delete_linked_file` | Permanently delete a linked file | Delete |
| | **Other** | | |
| 71 | `list_custom_fields` | List all custom fields and their values | Read |
| 72 | `list_entity_templates` | List all story/epic templates | Read |
| 73 | `list_repositories` | List linked Git repositories | Read |

**Type legend**: Read = no confirmation needed | Write = requires `confirm="CONFIRM_WRITE"` | Delete = requires `confirm_delete=true` + `confirm="CONFIRM_WRITE"` | Archive = requires `confirm_archive=true` + `confirm="CONFIRM_WRITE"`

## Example tool calls

### Create a story (dry run first)

```json
{
  "name": "MCP integration test story",
  "project_id": 101,
  "story_type": "feature",
  "description": "Created from Shortcut MCP server",
  "dry_run": true
}
```

### Create a story (execute)

```json
{
  "name": "MCP integration test story",
  "project_id": 101,
  "story_type": "feature",
  "description": "Created from Shortcut MCP server",
  "confirm": "CONFIRM_WRITE"
}
```

### Transition a story to a new state

```json
{
  "story_id": 12345,
  "workflow_state_name": "In Progress",
  "workflow_name": "Engineering Workflow",
  "confirm": "CONFIRM_WRITE"
}
```

### Update an epic

```json
{
  "epic_id": 14,
  "state": "in progress",
  "confirm": "CONFIRM_WRITE"
}
```

### Archive a story

```json
{
  "story_id": 12345,
  "confirm_archive": true,
  "confirm": "CONFIRM_WRITE"
}
```

### Delete a story (double confirmation)

```json
{
  "story_id": 12345,
  "confirm_delete": true,
  "confirm": "CONFIRM_WRITE"
}
```

### Bulk move stories

```json
{
  "story_ids": [123, 456, 789],
  "project_id": 202,
  "workflow_state_id": 500000010,
  "confirm": "CONFIRM_WRITE"
}
```

### Search stories

```json
{
  "query": "owner:johndoe state:\"In Progress\" type:bug"
}
```

### Create an iteration

```json
{
  "name": "Sprint 42",
  "start_date": "2025-03-10",
  "end_date": "2025-03-24",
  "confirm": "CONFIRM_WRITE"
}
```

### Link two stories

```json
{
  "subject_id": 123,
  "object_id": 456,
  "verb": "blocks",
  "confirm": "CONFIRM_WRITE"
}
```

### Add a comment to a story

```json
{
  "story_id": 12345,
  "text": "Looks good, moving to review.",
  "confirm": "CONFIRM_WRITE"
}
```

## Using the test client

The included test client (`src/client.ts`) lets you test tools from the command line without an AI assistant:

```bash
# Build first
npm run build

# List all available tools
npm run client

# Read operations (no confirmation needed)
npm run client -- list_epics
npm run client -- get_story '{"story_id": 123}'
npm run client -- search_stories '{"query": "state:\"In Progress\""}'
npm run client -- list_workflows

# Write operations (need confirmation)
npm run client -- create_story '{"name": "Test", "project_id": 101, "dry_run": true}'
npm run client -- create_story '{"name": "Test", "project_id": 101, "confirm": "CONFIRM_WRITE"}'
```

## Project structure

```
shortcut-mcp-server/
├── src/
│   ├── index.ts      # MCP server — all 73 tool registrations and Shortcut API logic
│   └── client.ts     # Standalone test client for development
├── dist/             # Compiled JavaScript (generated by npm run build)
├── package.json
├── tsconfig.json
├── .env.example      # Template for API token
└── .gitignore
```

## License

MIT

TDQS

B3/5.0

Scored across 73 tools

Disambiguation4/5

Most tools are clearly distinct by resource and action (e.g., list_story_comments vs list_epic_comments). However, generic update_story overlaps with transition_story_state and add_story_label, which could cause misselection.

Naming Consistency5/5

All tools follow a strict snake_case verb_noun pattern: list/get for reads, create/update/delete for writes, and resource-specific nouns. Even bulk operations and transition_story_state fit the pattern, with no mixed conventions.

Tool Count1/5

With 73 tools, this server far exceeds the 50+ extreme threshold. The massive surface is likely to overwhelm agents and create unnecessary selection overhead, despite each tool potentially having a specific purpose.

Completeness4/5

The tool set provides comprehensive CRUD coverage for stories, epics, projects, iterations, milestones, labels, linked files, and comments, plus search and bulk operations. Minor gaps include no update/delete for epic comments and no remove_epic_label.

Maintenance

ActivityInactive
ResponsivenessNo issues