runn-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@runn-mcp list my projects"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 suiteConfiguration
All configuration is via environment variables (see .env.example):
Variable | Default | Meaning |
| (required for calls) | Bearer token for the Runn API. Empty → tools error clearly. |
|
| API base URL (use |
|
| Sent as the mandatory |
|
| 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. Runnpm run buildfirst.Keep
RUNN_DRY_RUNas"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 byprojectId.runn_list_phases— phases; withprojectId→ that project's phases, else all.runn_list_milestones— milestones; withprojectId→ that project's, else all.runn_list_assignments— assignments; filters:projectId,personId,roleId,startDate,endDate.runn_list_people— people;includePlaceholders, name/email/externalIdfilters.runn_list_placeholders— placeholders.runn_list_roles— roles (resolveroleId).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_phaserunn_create_milestone/runn_update_milestonerunn_create_assignment/runn_delete_assignmentrunn_create_project/runn_update_projectrunn_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);listAllfollows it with a safety cap.Rate limits: 120 req/min. The client retries on
429/5xx/network errors with backoff, honouringretry-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 toolsrunn_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
Scored across 32 tools
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.
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.
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.
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
Related MCP Connectors
Ovanek MCP: Model Context Protocol server for Ovanek platform tools; writes are dry-run by default.
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server for Riveter's enrichment, scraping, and monitoring API
Related MCP Servers
- AlicenseAqualityDmaintenanceThin MCP server for Cisco CML2 that exposes the live OpenAPI specification and a generic API call tool, enabling models to interact with CML2 by reading the spec first.2MIT
- AlicenseDqualityBmaintenanceMCP server for Porkbun domains and DNS, enabling read-only and mutating operations with safety features like dry-run and write mode toggle.40135 npm3MIT
- FlicenseNot gradedqualityDmaintenanceMCP server for the Runn API, enabling resource management and reporting through natural language.-
- FlicenseAqualityBmaintenanceMCP server for the Gripp API, enabling CRUD operations on Gripp entities with built-in confirmation safety for mutations.911 npm1-