Skip to main content
Glama

youtrack-mcp

An MCP server for YouTrack, built with TypeScript and Bun. It works against any YouTrack instance (Cloud or self-hosted) — you supply the base URL and an API token, nothing is hardcoded.

Setup

You need a YouTrack permanent token: in YouTrack, go to your profile → Account SecurityNew token....

Set two environment variables:

  • YOUTRACK_BASE_URL — the REST API base URL of your instance, e.g. https://mycompany.youtrack.cloud/api or https://youtrack.mycompany.com/api for self-hosted installs.

  • YOUTRACK_TOKEN — the permanent token.

Install dependencies with Bun:

bun install

Run directly

YOUTRACK_BASE_URL=https://mycompany.youtrack.cloud/api YOUTRACK_TOKEN=perm:xxxx bun run src/index.ts

Configure in an MCP client

By default the server speaks MCP over stdio, so any client that can spawn a command works — point it at the source directly with bun, or at the prebuilt Docker image, either way with YOUTRACK_BASE_URL and YOUTRACK_TOKEN in its environment. It can also run as a standalone HTTP service instead (see Running as a network service), for clients that connect over the network rather than spawning a process — including claude.ai's web/mobile "Connectors".

Claude Code / Claude Desktop

This (stdio) works with any Claude product that can spawn a local process — Claude Code and Claude Desktop. claude.ai (web/mobile) "Connectors" need a remote HTTP-based MCP server instead; see Running as a network service for that.

Claude Code — either the CLI shortcut:

claude mcp add youtrack -e YOUTRACK_BASE_URL=https://mycompany.youtrack.cloud/api -e YOUTRACK_TOKEN=perm:xxxx -- bun run /path/to/youtrack-mcp/src/index.ts

(run claude mcp add --help to confirm the flags for your version), or edit .mcp.json directly:

{
  "mcpServers": {
    "youtrack": {
      "command": "bun",
      "args": ["run", "/path/to/youtrack-mcp/src/index.ts"],
      "env": {
        "YOUTRACK_BASE_URL": "https://mycompany.youtrack.cloud/api",
        "YOUTRACK_TOKEN": "perm:xxxx"
      }
    }
  }
}

Claude Desktop — Settings → Developer → Edit Config, which opens claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\). Add the same mcpServers block above, then restart Claude Desktop.

Either way, no local Bun install is required — point command/args at the Docker image instead.

Once added, just ask Claude things like "list my unresolved issues in DEMO" or "create a bug in DEMO titled X" — it calls the tools directly.

VS Code

VS Code (via GitHub Copilot Chat's agent mode) reads MCP server definitions from a workspace's .vscode/mcp.json (or your user settings — run MCP: Add Server from the command palette to create either). It supports prompting for secrets instead of hardcoding them, using ${input:...}:

{
  "servers": {
    "youtrack": {
      "type": "stdio",
      "command": "bun",
      "args": ["run", "/path/to/youtrack-mcp/src/index.ts"],
      "env": {
        "YOUTRACK_BASE_URL": "https://mycompany.youtrack.cloud/api",
        "YOUTRACK_TOKEN": "${input:youtrack-token}"
      }
    }
  },
  "inputs": [
    {
      "id": "youtrack-token",
      "type": "promptString",
      "description": "YouTrack permanent token",
      "password": true
    }
  ]
}

You'll be prompted for the token the first time the server starts, and it's stored securely rather than committed to the workspace file. Once added, enable it for a chat by opening the tools picker (🔧) in Copilot Chat's agent mode.

Using the Docker image instead of bun

Any of the configs above also work by swapping the command/args for Docker, so the client doesn't need Bun or this repo checked out locally — just Docker and the environment variables:

{
  "command": "docker",
  "args": [
    "run", "--rm", "-i",
    "-e", "YOUTRACK_BASE_URL", "-e", "YOUTRACK_TOKEN",
    "ghcr.io/bacali95/youtrack-mcp:latest"
  ],
  "env": {
    "YOUTRACK_BASE_URL": "https://mycompany.youtrack.cloud/api",
    "YOUTRACK_TOKEN": "perm:xxxx"
  }
}

-i keeps stdin open (required for MCP's stdio transport) and --rm cleans up the container once the client disconnects. -e YOUTRACK_BASE_URL (without a =value) tells Docker to forward that variable from the env block above rather than duplicating it inline.

Related MCP server: YouTrack MCP

Running as a network service (HTTP transport)

Set MCP_TRANSPORT=http to run the server as a standalone process listening on the network instead of speaking stdio to a spawned-in client. This is what lets it be hosted on a PaaS like Coolify, or reached by clients that connect over HTTP rather than spawning a command (e.g. claude.ai Connectors).

Environment variables, in addition to YOUTRACK_BASE_URL/YOUTRACK_TOKEN:

  • MCP_TRANSPORT=http — switches from the stdio default to HTTP.

  • MCP_HTTP_TOKENrequired in HTTP mode. A secret bearer token clients must send; without this, anyone who reaches the URL could use your YouTrack token through it.

  • PORT — port to listen on (default 3000).

It serves two routes:

  • GET /health — unauthenticated liveness check, for the PaaS's health probe.

  • ALL /mcp — the MCP endpoint (Streamable HTTP), gated on Authorization: Bearer <MCP_HTTP_TOKEN>.

docker run --rm -p 3000:3000 \
  -e YOUTRACK_BASE_URL=https://mycompany.youtrack.cloud/api \
  -e YOUTRACK_TOKEN=perm:xxxx \
  -e MCP_TRANSPORT=http \
  -e MCP_HTTP_TOKEN=$(openssl rand -hex 32) \
  ghcr.io/bacali95/youtrack-mcp:latest

Point an HTTP-capable MCP client at http://<host>:3000/mcp with that bearer token, e.g. Claude Code's CLI:

claude mcp add --transport http youtrack http://<host>:3000/mcp --header "Authorization: Bearer <MCP_HTTP_TOKEN>"

or the generic client JSON shape most others use:

{
  "mcpServers": {
    "youtrack": {
      "type": "http",
      "url": "http://<host>:3000/mcp",
      "headers": { "Authorization": "Bearer <MCP_HTTP_TOKEN>" }
    }
  }
}

Put this behind TLS (a PaaS's built-in proxy, or your own reverse proxy) before exposing it beyond your own machine — the bearer token is the only thing standing between the internet and your YouTrack instance, and it travels in a plain header.

Docker image

Every push to main publishes an image to GitHub Container Registry (see .github/workflows/docker-build.yml):

docker pull ghcr.io/bacali95/youtrack-mcp:latest
docker run --rm -i \
  -e YOUTRACK_BASE_URL=https://mycompany.youtrack.cloud/api \
  -e YOUTRACK_TOKEN=perm:xxxx \
  ghcr.io/bacali95/youtrack-mcp:latest

You can also build it locally with docker build -t youtrack-mcp ..

Hosting on a PaaS (Coolify, etc.)

Coolify (and similar platforms — Railway, Render, etc.) deploy long-running services that listen on a port and answer health checks, which is exactly what HTTP transport mode gives you. In Coolify:

  1. New Resource → Docker Image, and set it to ghcr.io/bacali95/youtrack-mcp:latest (or point it at this repo as a Dockerfile-based deployment if you'd rather it build from source).

  2. Set the Port to 3000 (matches the image's EXPOSE; change it and PORT together if you want a different one).

  3. Set the Health check path to /health.

  4. Add environment variables: YOUTRACK_BASE_URL, YOUTRACK_TOKEN, MCP_TRANSPORT=http, and MCP_HTTP_TOKEN (generate one with openssl rand -hex 32 — mark it "secret" in Coolify's UI so it isn't shown in logs).

  5. Deploy. Coolify gives you a domain with TLS already handled by its proxy — use https://<your-domain>/mcp with that bearer token in any HTTP-capable MCP client, as shown above.

This isn't specific to Coolify: any platform that deploys a Docker image, sets environment variables, and can health-check a path works the same way.

Deploying to a VPS (stdio, no exposed port)

If you'd rather not expose a port at all, the stdio approach still works on a plain VPS — there's nothing to run continuously, and an MCP client works the same way it does locally: by spawning the process itself and talking to it over that process's stdin/stdout. "Deploying" it just means making sure the client can spawn it, wherever that client runs:

  • Running the MCP client itself on the VPS (e.g. a headless Claude Code / Claude Code on the web session, or any agent you SSH into and drive interactively): just pull the image there and use the Docker config from above in that environment's mcp.json — no different from a local setup.

  • Running the MCP client on your laptop, with the server on the VPS: have the client spawn the process over SSH instead of locally, so Docker only needs to exist on the VPS:

    {
      "command": "ssh",
      "args": [
        "user@your-vps",
        "docker", "run", "--rm", "-i",
        "-e", "YOUTRACK_BASE_URL=https://mycompany.youtrack.cloud/api",
        "-e", "YOUTRACK_TOKEN=perm:xxxx",
        "ghcr.io/bacali95/youtrack-mcp:latest"
      ]
    }

    Set up an SSH key (no passphrase prompt) for that host so the client can spawn it non-interactively. Since the token is passed as a literal argument here, restrict that SSH key/user as tightly as you can (e.g. a dedicated user that can only run this one docker run command via a forced command in authorized_keys).

Tools

Tools are grouped by resource and share a fields argument (YouTrack's partial-response syntax) with a sensible default, so you only need to specify it when you want more or less data back.

  • Issueslist_issues (search), get_issue, create_issue, update_issue, delete_issue, execute_command (apply any YouTrack command — state, assignee, priority, links, tags, sprint, etc. via its natural query syntax, e.g. "State Fixed assignee John.Doe")

  • Commentslist_issue_comments, add_issue_comment, update_issue_comment, delete_issue_comment

  • Linkslist_issue_links, list_issue_link_types

  • Tagslist_tags, create_tag, list_issue_tags, add_issue_tag, remove_issue_tag

  • Time trackinglist_issue_work_items, add_issue_work_item, update_issue_work_item, delete_issue_work_item, list_work_items

  • Projectslist_projects, get_project, create_project, update_project, list_project_custom_fields

  • Users & groupsget_current_user, list_users, get_user, list_groups

  • Knowledge baselist_articles, get_article, create_article, update_article, delete_article

  • Agile boardslist_agile_boards, get_agile_board, list_sprints, get_sprint

  • Saved searcheslist_saved_queries

  • Escape hatchyoutrack_raw_request calls any endpoint of the YouTrack REST API directly (method, path, query, body), for the long tail of admin/settings endpoints not covered by a dedicated tool.

Design

  • src/youtrack/client.ts — the only place that talks HTTP: auth header, URL/query building, JSON + error handling. Every tool goes through it.

  • src/youtrack/fields.ts — default fields presets per entity, taken from YouTrack's own OpenAPI defaults.

  • src/youtrack/params.ts — shared Zod schema fragments (fields, pagination, IDs) reused across tools.

  • src/tool.tsdefineTool + registerTools, so each tool file only declares a name/description/schema/handler and the MCP response/error wrapping happens in one place.

  • src/youtrack/tools/*.ts — one file per resource, each tool a few lines built on the shared pieces above.

  • src/transport/http.ts — the HTTP transport, reusing the exact same allTools/registerTools as stdio; it just runs a session's McpServer behind a bearer-token check instead of over stdin/stdout.

Development

bun run typecheck   # tsc --noEmit
bun run dev         # run with --watch

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for Yandex Tracker API, enabling AI assistants to search, read, create, and edit issues, as well as manage comments, attachments, and links in Yandex Tracker.
    22 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for interacting with YouTrack, enabling issue management, project operations, and search via natural language.
    50 npm
    96
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    A Model Context Protocol (MCP) server for YouTrack integration, providing a standardized interface for LLMs to interact with YouTrack's issue tracking, agile management, and knowledge base features.
    12
    11 npm
    MIT