Skip to main content
Glama

runn-mcp

A generic MCP server that wraps the Runn REST API v1 (read + write). It exposes ID-based, "dumb" tools — list projects, create assignments, read actuals, bulk-write timesheets, and so on — and knows nothing about any specific project, journey, course or Gantt. That mapping logic lives on the orchestrator (Claude) side, in a project-specific mapping file (e.g. dlp2-runn-mapping.json), never in this server.

Requirements

  • Node.js >= 18 (uses the built-in fetch).

  • A Runn API token — generated in the Runn app under Settings > API (admins only). Tokens are scoped read or write and tied to an account.

  • Strongly recommended: develop and verify against a Test Account (in Runn: profile menu → Switch to test account; it has its own API tokens).

Related MCP server: porkbun-mcp

Install & build

cd runn-mcp
npm install
npm run build      # compiles TypeScript to dist/
npm test           # builds + runs the unit/integration test suite

Configuration

All configuration is via environment variables (see .env.example):

Variable

Default

Meaning

RUNN_API_TOKEN

(required for calls)

Bearer token for the Runn API. Empty → tools error clearly.

RUNN_API_BASE_URL

https://api.runn.io

API base URL (use https://api.us.runn.io for the US region).

RUNN_API_VERSION

1.0.0

Sent as the mandatory Accept-Version header.

RUNN_DRY_RUN

true

Write safety. When true, write tools only preview.

Write safety (dry-run)

By default (RUNN_DRY_RUN=true) every write tool returns a preview of the exact request it would send ({ dryRun: true, wouldCall: { method, path, body } }) and makes no API call. To apply writes for real, set RUNN_DRY_RUN=false. Read tools are unaffected by this flag.

Connecting it to the Claude app

The Claude app launches the server over stdio. Add an entry to your MCP servers config (mcpServers), pointing node at the built entry file and supplying the token in env:

{
  "mcpServers": {
    "runn": {
      "command": "node",
      "args": ["/home/sly/Dokumentumok/runn-mcp/dist/index.js"],
      "env": {
        "RUNN_API_TOKEN": "PASTE_YOUR_TOKEN_HERE",
        "RUNN_DRY_RUN": "true"
      }
    }
  }
}

Notes:

  • Use an absolute path to dist/index.js. Run npm run build first.

  • Keep RUNN_DRY_RUN as "true" until you're ready to write; flip to "false" for a session where you intend to push changes.

  • For the safest setup, use a read-scoped token for everyday querying and a separate write-scoped token only when you actually push.

  • The token lives only in this config (and/or a gitignored .env); it is never committed.

Quick check

runn_server_info (no token needed) reports the effective config. runn_whoami (needs a token) confirms the credentials by calling GET /me.

Tools

Diagnostics

  • runn_server_info — server version + effective config (no token, no API call).

Read

  • runn_whoami — GET /me; verify token/account.

  • runn_list_projects — projects; filters: name, includeArchived, externalId, modifiedAfter.

  • runn_get_project — one project by projectId.

  • runn_list_phases — phases; with projectId → that project's phases, else all.

  • runn_list_milestones — milestones; with projectId → that project's, else all.

  • runn_list_assignments — assignments; filters: projectId, personId, roleId, startDate, endDate.

  • runn_list_people — people; includePlaceholders, name/email/externalId filters.

  • runn_list_placeholders — placeholders.

  • runn_list_roles — roles (resolve roleId).

  • runn_list_workstreams — workstreams.

  • runn_list_actuals — actuals; filters: projectId, personId, roleId, workstreamId, minDate, maxDate.

  • runn_report_hours_project — by-day plan-vs-actual for a project.

  • runn_report_hours_person — by-day plan-vs-actual for a person.

  • runn_report_totals_project — aggregated plan/actual totals for a project.

Listing tools accept limit and cursor, plus fetchAll: true to follow the cursor and return every item ({ count, values }). Without fetchAll they return a single page ({ values, nextCursor }).

Write (respect RUNN_DRY_RUN)

  • runn_create_phase / runn_update_phase / runn_delete_phase

  • runn_create_milestone / runn_update_milestone

  • runn_create_assignment / runn_delete_assignment

  • runn_create_project / runn_update_project

  • runn_bulk_actuals — create/update up to 100 actuals in one call.

  • runn_create_placeholder — create a placeholder (auto-named after the role).

  • runn_update_person — update a person/placeholder (rename, email, team, archive).

  • runn_create_workstream / runn_update_workstream / runn_delete_workstream — manage account-level workstreams.

  • runn_add_project_workstream / runn_remove_project_workstream — attach/detach a workstream to/from a project.

Behaviour notes

  • Pagination: cursor-based (values + nextCursor); listAll follows it with a safety cap.

  • Rate limits: 120 req/min. The client retries on 429/5xx/network errors with backoff, honouring retry-after.

  • Errors: any non-2xx becomes a structured tool error ({ error, status, message, request }, isError: true) — the server never crashes on an API error.

  • Actuals: each bulk entry overwrites the existing actual for the same project/person/role/date; if any entry is invalid, none are written.

  • Assignments: creating one may return multiple segments if the period overlaps scheduled leave.

Scope

This server is intentionally project-agnostic and reusable for any Runn account. DLP2-specific decisions (monogram → person/placeholder IDs, phase mapping, the "everyone is Content Creator except EduPMs = PM, all billable" rule) belong in a separate mapping file that the orchestrator reads — not here. Out of scope: background/scheduled sync, writing back into spreadsheets, and multi-user hosting.

Project layout

runn-mcp/
  src/
    config.ts            env config (incl. dry-run default)
    index.ts             stdio server entry; wires up all tools
    runn/
      client.ts          generic Runn HTTP client (auth, pagination, retries)
      types.ts           shared API shapes
    tools/
      diagnostics.ts     runn_server_info
      read.ts            read tools
      write.ts           write tools (dry-run aware)
      helpers.ts         result formatting, dry-run, pagination helpers
      *.test.ts          unit/integration tests (node:test)
  scripts/
    smoke-read.mjs       read-only live smoke test (node --env-file=.env ...)

Available Tools

32 tools
runn_add_project_workstreamRunn: add workstream to projectA

Attach an existing workstream to a project (POST /projects/{projectId}/project-workstreams/{workstreamId}/). No body. Do this once per workstream/project before allocating assignments or actuals on that workstream.

Inputs: projectId (required), workstreamId (required).

runn_bulk_actualsRunn: bulk create/update actualsA

Create or update up to 100 actuals in one call (POST /actuals/bulk). Each entry overwrites any existing actual for the same project/person/role/date. Either billableMinutes or nonbillableMinutes must be > 0. If any entry is invalid, none are written.

Inputs: actuals (required).

runn_create_assignmentRunn: create assignmentA

Create an assignment (POST /assignments). Required: personId, projectId, roleId, startDate, endDate, minutesPerDay. Note: the response may contain multiple segments if the period overlaps leave.

Inputs: roleId (required), endDate (required), personId (required), projectId (required), +7 more.

runn_create_milestoneRunn: create milestoneA

Create a milestone on a project (POST /projects/{id}/milestones). title, icon, date required. icon may be a preset (start|end|flag|dollar|warning) or an emoji.

Inputs: date (required), icon (required), title (required), projectId (required), +1 more.

runn_create_phaseRunn: create phaseA

Create a phase on a project (POST /projects/{id}/phases). name, startDate, endDate required; color must be one of Runn's allowed hex values (defaults to #67D0D5).

Inputs: name (required), endDate (required), projectId (required), startDate (required), +1 more.

runn_create_placeholderRunn: create placeholderA

Create a placeholder (POST /placeholders) for allocating external/contractor work. Only roleId is required; the placeholder is auto-named after the role (e.g. 'Content creator Placeholder NN'). To give it a real name, follow up with runn_update_person on the returned id. NOTE: a placeholder with no project/assignment is deleted by Runn within 24 hours.

Inputs: roleId (required), tags, teamId, costPerHour.

runn_create_projectRunn: create projectA

Create a project (POST /projects). Standard mode requires name + clientId. Alternatively pass fromTemplate { templateId, startDate } to duplicate a template.

Inputs: name, emoji, budget, teamId, +9 more.

runn_create_workstreamRunn: create workstreamA

Create a workstream (POST /workstreams). Workstreams are account-level bands of work; create one, then attach it to a project with runn_add_project_workstream. To allocate work to it, pass its id as workstreamId on runn_create_assignment / runn_bulk_actuals. Assignments on the SAME person/role but DIFFERENT workstreams may overlap (Runn sums the load).

Inputs: name (required).

runn_delete_assignmentRunn: delete assignmentA

Delete an assignment by ID (DELETE /assignments/{id}). This is a hard delete.

Inputs: assignmentId (required).

runn_delete_phaseRunn: delete phaseA

Delete a phase (DELETE /projects/{id}/phases/{phaseId}). Intended for removing a phase AFTER its actuals/assignments have been migrated elsewhere. May fail if the phase still has attached data — migrate first.

Inputs: phaseId (required), projectId (required).

runn_delete_workstreamRunn: delete workstreamA

Delete a workstream account-wide (DELETE /workstreams/{workstreamId}). Hard delete.

Inputs: workstreamId (required).

runn_get_projectRunn: get projectA

Fetch a single project by ID (GET /projects/{id}).

Inputs: projectId (required).

runn_list_actualsRunn: list actualsB

List actual (timesheet) entries, filterable by project, person, role, workstream and date range.

Inputs: limit, cursor, roleId, maxDate, +5 more.

runn_list_assignmentsRunn: list assignmentsA

List assignments, filterable by project, person, role and date range.

Inputs: limit, cursor, roleId, endDate, +4 more.

runn_list_milestonesRunn: list milestonesA

List milestones. With projectId, lists that project's milestones (GET /projects/{id}/milestones); without it, lists milestones across all projects.

Inputs: limit, cursor, fetchAll, projectId.

runn_list_peopleRunn: list peopleC

List people. Optionally include placeholders and filter by name/email/externalId.

Inputs: email, limit, cursor, fetchAll, +4 more.

runn_list_phasesRunn: list phasesA

List project phases. With projectId, lists that project's phases (GET /projects/{id}/phases); without it, lists phases across all projects.

Inputs: limit, cursor, fetchAll, projectId.

runn_list_placeholdersRunn: list placeholdersB

List placeholders (unfilled roles used to allocate external/contractor work).

Inputs: limit, cursor, fetchAll.

runn_list_projectsRunn: list projectsA

List projects. Optional case-insensitive name substring filter and archived toggle.

Inputs: name, limit, cursor, fetchAll, +3 more.

runn_list_rolesRunn: list rolesA

List roles (for resolving roleId values used in assignments/actuals).

Inputs: limit, cursor, fetchAll.

runn_list_workstreamsRunn: list workstreamsC

List workstreams.

Inputs: limit, cursor, fetchAll.

runn_remove_project_workstreamRunn: remove workstream from projectA

Detach a workstream from a project (DELETE /projects/{projectId}/project-workstreams/{workstreamId}). Removes the project link only; the workstream itself is not deleted.

Inputs: projectId (required), workstreamId (required).

runn_report_hours_personRunn: person hours report (by day)C

By-day plan-vs-actual hours for a person (GET /reports/hours/people/{id}).

Inputs: personId (required), limit, cursor, endDate, +2 more.

runn_report_hours_projectRunn: project hours report (by day)B

By-day plan-vs-actual hours for a project (GET /reports/hours/projects/{id}). billableMinutes/totalMinutes use actuals where present, else assignments.

Inputs: projectId (required), limit, cursor, endDate, +2 more.

runn_report_totals_projectRunn: project totals reportB

Aggregated plan/actual minutes for a project (GET /reports/totals/projects/{id}). Returns a single totals object (no pagination).

Inputs: projectId (required).

runn_server_infoRunn MCP server infoA

Returns the runn-mcp server version and effective configuration (base URL, API version, whether a token is configured, dry-run state). Does NOT reveal the token and does NOT call the Runn API.

Inputs: no named parameters.

runn_update_milestoneRunn: update milestoneA

Update a milestone (PATCH /projects/{id}/milestones/{milestoneId}). Only provided fields change.

Inputs: projectId (required), milestoneId (required), date, icon, +2 more.

runn_update_personRunn: update person/placeholderA

Update a person or placeholder (PATCH /people/{id}). Use to rename a freshly created placeholder (firstName/lastName), change email/team, or archive. Only provided fields change.

Inputs: personId (required), email, teamId, lastName, +2 more.

runn_update_phaseRunn: update phaseA

Update a phase (PATCH /projects/{id}/phases/{phaseId}). Only provided fields are changed.

Inputs: phaseId (required), projectId (required), name, color, +2 more.

runn_update_projectRunn: update projectA

Update a project (PATCH /projects/{id}). Only provided fields change.

Inputs: projectId (required), name, budget, teamId, +8 more.

runn_update_workstreamRunn: update workstreamA

Rename a workstream (PATCH /workstreams/{workstreamId}).

Inputs: name (required), workstreamId (required).

runn_whoamiRunn: who am IA

Verify the token/account by calling GET /me. Returns the authenticated identity.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 32 tools

    Disambiguation5/5

    Each tool targets a distinct resource/action combination. Even closely related tools like runn_list_people vs runn_list_placeholders are clearly separated by entity, and runn_list_actuals vs runn_bulk_actuals are list vs create/update. No two tools appear to do the same thing.

    Naming Consistency3/5

    Most tools follow the `runn_<verb>_<noun>` pattern (e.g., runn_create_project, runn_list_phases). However, several deviate: runn_report_totals_project, runn_report_hours_project, runn_bulk_actuals, runn_server_info, and runn_whoami are not clearly verb_noun. This mixed convention slightly reduces predictability.

    Tool Count2/5

    32 tools is well above the recommended range for a coherent set. Even accounting for the broad domain (projects, phases, milestones, assignments, actuals, workstreams, reports, auth), the count feels heavy and could overwhelm agents. Some consolidation might be possible.

    Completeness4/5

    The set covers the core lifecycle for projects, phases, milestones, assignments, actuals, workstreams, and people. Minor gaps exist (e.g., no delete_project, no update_assignment, no delete_milestone), but these may reflect API constraints and are typically workable (e.g., delete and recreate assignment). Reports cover the key planning vs. actuals use cases.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers