leantime-mcp
by teopeurt
README.md
# Leantime MCP over HTTP
A stateless TypeScript MCP server for self-hosted Leantime. It exposes **66 tools** over Streamable HTTP and talks to Leantime only through its JSON-RPC API at `/api/jsonrpc` using `x-api-key`.
It does **not** connect to MySQL and does not require Redis or its own database.
## Architecture
```text
ChatGPT / Claude / Codex
|
| HTTPS MCP + Bearer token
v
https://mcp.example.com/mcp
|
| JSON-RPC + x-api-key
v
https://leantime.example.com/api/jsonrpc
```
## Tool surface
| Area | Tools |
|---|---|
| Projects | `getAllProjects`, `getProject`, `getFullProjectOverview`, `addProject`, `editProject`, `findProject`, `getUsersAssignedToProject` |
| Tasks | `findTasks`, `getTicket`, `addTask`, `editTask`, `addSubtask`, `bulkAddTasks`, `bulkEditTasks`, `getStatusLabels` |
| Milestones | `findMilestones`, `getMilestone`, `addMilestone`, `editMilestone`, `addMilestonesForProject` |
| Goals / OKRs | `getAllGoals`, `getGoal`, `createGoal`, `editGoal`, `createGoalboard`, `getGoalsByMilestone` |
| Calendar | `getCalendar`, `addEvent`, `editEvent`, `deleteEvent`, `scheduleTaskOnCalendar`, `scheduleDay`, `breakdownTask`, `getICalUrl` |
| Timesheets | `getUserTimesheets`, `getProjectTimesheets`, `logTime`, `getTimesheetSummary`, `getWeeklyTimesheets` |
| Sprints | `getSprint`, `getCurrentSprintId`, `getUpcomingSprint`, `getAllSprints`, `getAllFutureSprints`, `addSprint`, `editSprint`, `deleteSprint` |
| Wiki (docs) | `getAllProjectWikis`, `getWiki`, `createWiki`, `updateWiki`, `deleteWiki`, `getAllWikiHeadlines`, `getArticle`, `createArticle`, `updateArticle`, `deleteArticle`, `getArticleActivity` |
| Files | `getFilesByModule`, `deleteFile` |
| Comments / Status | `getComments`, `addComment`, `getAllProjectComments`, `addProjectStatusUpdate` |
| Timer | `startTimer`, `stopTimer` |
### Composed convenience tools
These orchestrate multiple Leantime calls or add aggregation/batch semantics:
- `getFullProjectOverview` — project + users + tasks + milestones + comments + timesheets, with partial-failure results.
- `bulkAddTasks` / `bulkEditTasks` — batch execution with a result for each item.
- `addMilestonesForProject` — batch milestone creation.
- `scheduleTaskOnCalendar` / `scheduleDay` — converts task selections into calendar blocks.
- `breakdownTask` — creates a supplied set of subtasks beneath a parent task.
- `getTimesheetSummary` — returns raw entries plus totals by project, user and day.
- `getAllProjectComments` / `addProjectStatusUpdate` — convenient project status/comment operations.
`breakdownTask` does not invent the decomposition itself. The AI client decides the subtasks and sends them to the tool; the MCP server executes them.
### Sprints, Wiki, and Files
- Leantime's Kanban board is typically scoped to a sprint. Tasks created via `addTask`/`bulkAddTasks` are not assigned to any sprint by default (`sprint: 0`); use `getAllSprints`/`getCurrentSprintId` to find the right sprint ID, then set `sprint` in the task's `values` (via `editTask`/`bulkEditTasks`) to make it appear on the board.
- Wiki tools operate on Leantime's project documentation feature. `createWiki` makes a documentation space; `createArticle` adds a page inside it (`values.canvasId` should be the wiki's ID).
- `getAllWikiHeadlines` requires a real `userId` — Leantime's server errors on `null` there, unlike most other endpoints' optional `userId` params.
- File upload is intentionally **not exposed**. Leantime's JSON-RPC transport has no multipart/file support, and testing confirmed the server's `upload` endpoint rejects a plain-JSON payload with a server error — there is no working way to upload file bytes through this API. `getFilesByModule` (list) and `deleteFile` (delete) work normally since they carry no binary payload; uploads must go through Leantime's web UI.
## 1. Leantime API key
Create a Leantime API key for the user/role you want the MCP server to act as. The key's Leantime permissions still apply, so project reporting, cross-user timesheets and writes can be denied by Leantime even though the MCP tool exists.
## 2. Environment
```env
LEANTIME_URL=https://leantime.example.com
LEANTIME_API_KEY=lt_your_real_key
MCP_BEARER_TOKEN=replace-with-a-separate-long-random-secret
PORT=3000
```
Generate the MCP-side secret separately:
```bash
openssl rand -hex 32
```
Do not reuse the Leantime API key as `MCP_BEARER_TOKEN`.
## 3. Coolify
A GitHub Actions workflow (`.github/workflows/docker-publish.yml`) builds this image and pushes it to GitHub Container Registry on every push to `main` (tagged `:latest`, `:<short-sha>`, and any `v*` release tag).
`docker-compose.coolify.yml` pulls that prebuilt image rather than building on the Coolify host:
```yaml
image: ghcr.io/<your-github-owner>/<your-repo>:latest
```
Update the `<your-github-owner>/<your-repo>` placeholder to match your fork, and make sure the GHCR package is public (or add registry credentials in Coolify) so Coolify can pull it.
In Coolify, create a Docker Compose resource pointing at this repo and `docker-compose.coolify.yml`, then set:
```env
LEANTIME_URL=https://leantime.example.com
LEANTIME_API_KEY=lt_your_real_key
MCP_BEARER_TOKEN=your-random-secret
```
Assign a domain to the service on container port `3000`, for example at:
```text
https://mcp.example.com
```
The MCP endpoint is:
```text
https://mcp.example.com/mcp
```
Health check:
```text
GET /health
```
## 4. MCP client
For clients that support remote Streamable HTTP and custom headers:
```json
{
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_BEARER_TOKEN"
}
}
```
## 5. Test Leantime before MCP
Verify the API key directly first:
```bash
curl -sS 'https://leantime.example.com/api/jsonrpc' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_LEANTIME_API_KEY' \
--data '{"jsonrpc":"2.0","id":1,"method":"leantime.rpc.Projects.Projects.getAll","params":{"showClosedProjects":false}}'
```
If that succeeds, the MCP server has a working backend connection.
## 6. Development
Requires Node.js 22+.
```bash
npm install
npm test
npm run build
npm start
```
Current tests cover JSON-RPC request/error handling, auth, project/task/milestone/goal/calendar/timesheet/comment/timer mappings, batch partial failures, project overview composition, timesheet aggregation, and the approved 45-tool MCP surface.
## Native-value tools
Some create/edit tools intentionally accept a `values` object instead of trying to duplicate every Leantime field in the MCP schema. This keeps the wrapper forward-compatible with Leantime fields such as assignees, status IDs, dates, tags and custom metadata.
Important examples:
- `addTask`: include at least `projectId` and `headline`.
- `editTask`: include the ticket `id` plus fields to change.
- `addMilestone`: include `projectId` and `headline`.
- `createGoal`: current Leantime expects a target `canvasId` in `values`.
- `editGoal`: include `id` or `itemId`.
- `addEvent` / `editEvent`: use Leantime calendar value names; this wrapper ensures a non-empty description because current Leantime requires one.
- `logTime`: requires `hours`, `kind`, and one of `date`, `dateString`, or `timestamp`.
## Security model
There are two independent credentials:
1. **MCP bearer token** — protects the public `/mcp` endpoint.
2. **Leantime API key** — determines what the MCP server may actually do in Leantime.
Keep the MCP service stateless and put HTTPS in front of it through Coolify/Traefik.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues