Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/sessions.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"active":[],"closed":[{"id":"oauth-scopes","focus":"Fix OAuth device flow missing required scopes","branch":"feat/oauth-scopes","worktree":"/Users/joaodotwork/Dev/plantas-github-projects-mcp-session-oauth-scopes","areas":["src/auth/**"],"createdAt":"2026-04-07T16:14:58+0200","issues":["18"],"endedAt":"2026-04-07T16:37:54+0200"}],"schema_version":"2.0","last_updated":"2026-04-07T16:37:54+0200"}
{"active":[],"closed":[{"id":"oauth-scopes","focus":"Fix OAuth device flow missing required scopes","branch":"feat/oauth-scopes","worktree":"/Users/joaodotwork/Dev/plantas-github-projects-mcp-session-oauth-scopes","areas":["src/auth/**"],"createdAt":"2026-04-07T16:14:58+0200","issues":["18"],"endedAt":"2026-04-07T16:37:54+0200"},{"id":"iterations","focus":"Close all open iteration tickets: PR-item support in assign_issue_to_iteration, verify+close schema/assignment fixes","branch":"fix/iterations-pr-items","worktree":"/Users/joaodotwork/Dev/plantas-github-projects-mcp-session-iterations","areas":["src/tools/**"],"createdAt":"2026-08-14T10:18:35+0200","issues":["20","21","22","24","25"],"endedAt":"2026-08-14T10:19:34+0200"}],"schema_version":"2.0","last_updated":"2026-08-14T10:19:34+0200"}
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [main]
pull_request:

# A second push to the same PR cancels the first — no point finishing a run whose
# commit is already superseded.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# This repo carried two committed lockfiles for four minor versions with nothing
# arbitrating between them (see #28). pnpm is the one package manager now, pinned via
# `packageManager` in package.json; this fails loudly if npm's lockfile comes back.
lockfile:
name: single lockfile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Fail if package-lock.json reappears
run: |
if [ -f package-lock.json ]; then
echo "::error file=package-lock.json::package-lock.json is back. This project uses pnpm (see packageManager in package.json) — delete it and commit pnpm-lock.yaml instead."
exit 1
fi
if [ -f yarn.lock ]; then
echo "::error file=yarn.lock::yarn.lock found. This project uses pnpm (see packageManager in package.json)."
exit 1
fi
echo "pnpm-lock.yaml is the only lockfile."
build-test:
name: build & test (node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [20, 22]

steps:
- uses: actions/checkout@v4

# Reads the pinned version from `packageManager` in package.json. Must run before
# setup-node, which needs pnpm on PATH to resolve its cache directory.
- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: pnpm

# --frozen-lockfile turns a lockfile that disagrees with package.json into a hard
# failure, which is the drift that went unnoticed through the 1.5.x releases.
- name: Install
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm run build

- name: Test
run: pnpm test
83 changes: 68 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ npm install -g @joaodotwork/plantas-github-projects-mcp
```bash
git clone https://github.com/joaodotwork/plantas-github-projects-mcp.git
cd plants-github-projects-mcp
npm install
npm run build
pnpm install
pnpm run build
```

## Configuration
Expand Down Expand Up @@ -181,6 +181,38 @@ Create a milestone in a repository.

---

### `set_issue_milestone`

Set or change the milestone on an **existing** issue or pull request. `create_issue` can attach a milestone at creation time; this handles everything after that, including removing one.

**Parameters:**
- `owner` (string, required): Repository owner
- `repo` (string, required): Repository name
- `issueNumber` (number, required): Issue **or pull request** number
- `milestoneNumber` (number or null, required): Milestone number to assign, or `null` to clear the current milestone

**Example:**
```typescript
{
"owner": "joaodotwork",
"repo": "dpds-arkiv",
"issueNumber": 80,
"milestoneNumber": 4
}
```

**Returns:**
```json
{
"type": "Issue",
"number": 80,
"url": "https://github.com/joaodotwork/dpds-arkiv/issues/80",
"milestone": { "number": 4, "title": "Epic 1: GitHub Metadata Workflow" }
}
```

---

### `create_issue`

Create an issue with optional milestone, labels, and assignees.
Expand Down Expand Up @@ -246,7 +278,9 @@ Add an issue to a Projects v2 board.

### `create_iteration_field`

Create an iteration field with weekly sprints.
Create an iteration field with weekly sprints. The field and its iterations are created in a single mutation, so a failure leaves nothing behind to clean up.

If a field of that name already exists but is **empty** — typically stranded by a failed create on an older version — it is adopted and configured, and the result carries `"adopted": true`. If it already holds iterations, the call is refused rather than reconfigured: replacing an iteration configuration regenerates every iteration ID and would detach all item assignments. Use `add_iteration` or `update_iteration` on a populated field.

**Parameters:**
- `projectId` (string, required): Project node ID
Expand Down Expand Up @@ -293,17 +327,21 @@ Create an iteration field with weekly sprints.

### `assign_issue_to_iteration`

Assign an issue to a specific iteration.
Assign an issue **or pull request** to a specific iteration. The item must already be on the board.

Identify the item either by number or by project item ID:

**Parameters:**
- `owner` (string, required): Repository owner
- `repo` (string, required): Repository name
- `projectNumber` (number, required): Project number
- `issueNumber` (number, required): Issue number
- `fieldId` (string, required): Iteration field ID
- `iterationId` (string, required): Iteration ID

**Example:**
- `owner` (string): Repository owner — required unless both `itemId` and `projectId` are given
- `repo` (string): Repository name — required when looking up by number
- `projectNumber` (number): Project number — required unless `projectId` is given
- `issueNumber` (number): Issue **or PR** number — required unless `itemId` is given
- `itemId` (string): Project item ID (`PVTI_...`) — skips the number lookup
- `projectId` (string): Project node ID (`PVT_...`) — skips the project lookup

**Example — by number (works for issues and PRs alike):**
```typescript
{
"owner": "joaodotwork",
Expand All @@ -315,12 +353,24 @@ Assign an issue to a specific iteration.
}
```

**Example — by item ID (same escape hatch `update_item_status` offers):**
```typescript
{
"projectId": "PVT_kwHOAwJiCM4BUd_L",
"itemId": "PVTI_lAHOAwJiCM4BNC20zgYd5tc",
"fieldId": "PVTIF_lAHOAwJiCM4BNC20zg8J544",
"iterationId": "bab3ba50"
}
```

---

### `add_iteration`

Add a new iteration to an existing iteration field.

> **Assignments are preserved automatically.** GitHub has no per-iteration mutation — the whole configuration must be replaced, and that regenerates every iteration ID, detaching all item assignments. Both `add_iteration` and `update_iteration` snapshot every item→iteration value first and re-apply it by iteration title afterwards. The result includes `assignmentsRestored: { restored, failed }` so a partial restore is visible rather than silent.

**Parameters:**
- `projectId` (string, required): Project node ID
- `fieldId` (string, required): Iteration field ID
Expand All @@ -343,7 +393,7 @@ Add a new iteration to an existing iteration field.

### `update_iteration`

Update an existing iteration's title, start date, or duration.
Update an existing iteration's title, start date, or duration. Item assignments are preserved — see the note under [`add_iteration`](#add_iteration). Renaming is handled too: items on the renamed iteration are remapped to the new title rather than dropped.

**Parameters:**
- `projectId` (string, required): Project node ID
Expand Down Expand Up @@ -754,14 +804,17 @@ for (const issueData of issues) {
## Development

```bash
# Install dependencies
npm install
# Install dependencies (this project uses pnpm)
pnpm install

# Build
npm run build
pnpm run build

# Watch mode
npm run dev
pnpm run dev

# Run tests
pnpm test

# Test locally
node dist/index.js
Expand Down
Loading
Loading