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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,13 @@
# This is public information (visible in the OAuth authorize URL).
# Set this as a GitHub Actions variable (not a secret) for CI/CD.
VITE_GITHUB_CLIENT_ID=your_oauth_app_client_id_here

# ── MCP Server ────────────────────────────────────────────────────────────────
# Personal Access Token (PAT) or OAuth token for the MCP server.
# Required scopes: repo, read:org, notifications
# Used by: mcp/src/octokit.ts
GITHUB_TOKEN=your_github_token_here

# Port for the WebSocket relay server (MCP ↔ browser dashboard bridge).
# Default: 9876
# MCP_WS_PORT=9876
38 changes: 38 additions & 0 deletions .github/workflows/publish-mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Publish MCP Server
on:
push:
tags: ["github-tracker-mcp@*"]
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 24
registry-url: "https://registry.npmjs.org"
- run: pnpm install --frozen-lockfile
- run: pnpm --filter github-tracker-mcp run typecheck
- run: pnpm --filter github-tracker-mcp run build
- run: pnpm --filter github-tracker-mcp test
- run: cd mcp && pnpm publish --access public --no-git-checks

create-release:
runs-on: ubuntu-latest
needs: build-and-publish
permissions:
contents: write
steps:
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ github.ref_name }}" \
--repo "${{ github.repository }}" \
--title "MCP Server ${{ github.ref_name }}" \
--generate-notes \
--notes $'## Install\n```bash\nnpx github-tracker-mcp@latest\n```\nSee [npm package](https://www.npmjs.com/package/github-tracker-mcp) for full documentation.'
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
node_modules/
dist/
mcp/dist/
dist/shared/
*.tsbuildinfo
.wrangler/
.dev.vars
*.local
Expand Down
58 changes: 56 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,30 @@ pnpm run dev

The dev server starts at `http://localhost:5173`. You'll need a GitHub OAuth app client ID in `.env` (copy `.env.example` and fill in your value).

The repo uses a pnpm workspace: the root package is the SolidJS SPA; `mcp/` is a separate package (`github-tracker-mcp`) built with tsup. Running `pnpm install` at the root installs both.

To run the MCP server in standalone mode, set `GITHUB_TOKEN` before starting:

```bash
GITHUB_TOKEN=ghp_... pnpm mcp:serve
```

Fine-grained PATs need Actions (read), Contents (read), Issues (read), and Pull requests (read) permissions.

## Running checks

```bash
pnpm test # unit tests (Vitest)
pnpm test # unit tests (Vitest — root + mcp/)
pnpm test:e2e # Playwright E2E tests (chromium)
pnpm run typecheck # TypeScript validation
pnpm run typecheck # TypeScript validation (root + mcp/)
pnpm run screenshot # Capture dashboard screenshot (saves to docs/)
pnpm mcp:serve # Start the MCP server (requires GITHUB_TOKEN)
```

To test MCP tools interactively, use the MCP Inspector:

```bash
npx @modelcontextprotocol/inspector tsx mcp/src/index.ts
```

CI runs typecheck, unit tests, and E2E tests on every PR. Make sure they pass locally before pushing.
Expand Down Expand Up @@ -76,6 +93,43 @@ type(scope): description

Scope is optional. Use imperative mood: "add feature", not "adds feature" or "added feature".

## Releasing the MCP server

The MCP server (`mcp/` package) is published to npm and GitHub Releases via CI.

### First publish (manual)

The very first publish must be done locally — OIDC trusted publishing can only be configured for packages that already exist on npm.

1. Create an npm account at [npmjs.com](https://www.npmjs.com/signup) if you don't have one
2. Log in locally: `npm login`
3. Build and publish:
```bash
cd mcp
pnpm run build
pnpm publish --access public
```

### Trusted publishing setup (one-time, after first publish)

CI publishes via npm OIDC trusted publishing — no tokens or secrets needed.

1. Go to **npmjs.com > github-tracker-mcp > Settings > Trusted Publishers**
2. Add a trusted publisher:
- **Owner:** `gordon-code`
- **Repository:** `github-tracker`
- **Workflow filename:** `publish-mcp.yml`

### Cutting a release

```bash
cd mcp
pnpm version patch # or minor / major
git push upstream main --follow-tags
```

`pnpm version` bumps `mcp/package.json`, commits, and creates a `github-tracker-mcp@X.Y.Z` tag. Pushing that tag to upstream triggers CI, which typechecks, builds, tests, publishes to npm, and creates a GitHub release.

## Pull requests

All PRs target `main` on `gordon-code/github-tracker`. Keep PRs focused — one feature or fix per PR makes review faster and reverts cleaner.
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Conditional requests using `If-None-Match` headers — GitHub doesn't count 304

```
src/
shared/ # Browser-agnostic types, schemas, format utils shared with MCP server
app/
components/
dashboard/ # DashboardPage, IssuesTab, PullRequestsTab, ActionsTab,
Expand All @@ -105,9 +106,9 @@ src/
# LoadingSpinner, SkeletonRows, ToastContainer, NotificationDrawer,
# RepoLockControls, UserAvatarBadge, ExpandCollapseButtons,
# RepoGitHubLink, ChevronIcon, ExternalLinkIcon, Tooltip/InfoTooltip
lib/ # 14 modules: format, errors, notifications, oauth, pat, url,
lib/ # 15 modules: format, errors, notifications, oauth, pat, url,
# flashDetection, grouping, reorderHighlight, collections,
# emoji, label-colors, sentry, github-emoji-map.json
# emoji, label-colors, sentry, mcp-relay, github-emoji-map.json
pages/ # LoginPage, OAuthCallback, PrivacyPage
services/
api.ts # GitHub API methods — issues, PRs, workflow runs, user validation,
Expand All @@ -121,8 +122,11 @@ src/
view.ts # View state (tabs, sorting, filters, ignored items, locked repos)
worker/
index.ts # OAuth token exchange endpoint, CORS, security headers
tests/ # unit/component tests across 70 test files
e2e/ # 15 E2E tests across 3 spec files
mcp/
src/ # MCP server: tools, resources, WebSocket relay, Octokit fallback
tests/ # MCP server unit + integration tests
tests/ # SPA unit/component tests
e2e/ # Playwright E2E tests
```

## Development
Expand All @@ -145,6 +149,10 @@ OAuth tokens are stored in `localStorage` under an app-specific key — this is

See [DEPLOY.md](./DEPLOY.md) for Cloudflare, OAuth App, and CI/CD setup.

## MCP Server

An optional MCP (Model Context Protocol) server lets AI clients like Claude Code and Cursor query your dashboard data — open PRs, issues, failing CI — without leaving the editor. See the [MCP server README](mcp/README.md) for setup, available tools, and configuration.

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).
56 changes: 56 additions & 0 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ GitHub Tracker is a dashboard that aggregates open issues, pull requests, and Gi
- [Notifications](#notifications)
- [Tracked Items](#tracked-items)
- [Repo Pinning](#repo-pinning)
- [MCP Server Integration](#mcp-server-integration)
- [Settings Reference](#settings-reference)
- [Troubleshooting](#troubleshooting)

Expand Down Expand Up @@ -359,6 +360,47 @@ Pin state is per-tab — a repo can be pinned on the Issues tab but not the Pull

---

## MCP Server Integration

The MCP (Model Context Protocol) server lets AI clients like Claude Code and Cursor query your dashboard data — open PRs, issues, failing CI — without leaving the editor.

MCP access is fully opt-in. Nothing is exposed unless you explicitly run the standalone server or enable the WebSocket relay in Settings.

### Standalone mode

Run the MCP server with a GitHub token for direct API access:

```bash
GITHUB_TOKEN=ghp_... npx github-tracker-mcp
```

This works without the dashboard open. The server fetches data directly from GitHub using the token. See the [MCP server README](https://github.com/gordon-code/github-tracker/tree/main/mcp) for Claude Code configuration and the full tool reference.

### WebSocket relay mode

For richer data without extra API calls, connect the MCP server to the running dashboard:

1. Open **Settings > MCP Server Relay**
2. Toggle **Enable relay** on
3. The status indicator shows "Connected" when the MCP server is running and linked

When connected, the MCP server receives live dashboard data over a local WebSocket connection (`ws://127.0.0.1:9876`). This provides the same enriched data you see in the dashboard — GraphQL-sourced review decisions, check statuses, and reviewer lists — without consuming additional API quota.

The relay falls back to direct GitHub API calls automatically when the dashboard is closed. Set `GITHUB_TOKEN` even when using the relay as a safety net — without it, all tool calls fail if the relay disconnects.

### Available tools

| Tool | What it returns |
|------|----------------|
| `get_dashboard_summary` | Counts: open PRs, open issues, failing CI, PRs needing review, approved but unmerged |
| `get_open_prs` | Open PRs with CI status, review decision, size, reviewers |
| `get_open_issues` | Open issues across tracked repos |
| `get_failing_actions` | In-progress or recently failed workflow runs |
| `get_pr_details` | Full details for a specific PR |
| `get_rate_limit` | Current GitHub API quota |

---

## Settings Reference

Settings are saved automatically to `localStorage` and persist across sessions. All settings can be exported as a JSON file via **Settings > Data > Export**.
Expand All @@ -382,6 +424,8 @@ Settings are saved automatically to `localStorage` and persist across sessions.
| Remember last tab | On | Return to the last active tab on revisit. |
| Enable tracked items | Off | Show the Tracked tab for pinning issues and PRs to a personal TODO list. |
| API Usage | — | Displays per-source API call counts, pool labels (Core/GraphQL), and last-called timestamps for the current rate limit window. Counts auto-reset when the rate limit window expires. Use "Reset counts" to clear manually. |
| MCP relay enabled | Off | Allow a local MCP server to receive live dashboard data over WebSocket. |
| MCP relay port | 9876 | Port for the WebSocket relay connection. Must match the MCP server's `MCP_WS_PORT`. |

### View State Settings

Expand Down Expand Up @@ -430,6 +474,18 @@ When a tab has been hidden for more than 2 minutes, a catch-up fetch fires autom

Go to **Settings > Repositories > Manage Repositories**, find the repo, and deselect it. If it was in the monitored list, it will be removed from monitoring automatically.

**MCP relay shows "Connecting..." but never connects.**

- Verify the MCP server is running (`GITHUB_TOKEN=ghp_... npx github-tracker-mcp` or `pnpm mcp:serve`)
- Check that the port in Settings matches the MCP server's port (default: 9876)
- The MCP server binds to `127.0.0.1` only — it must run on the same machine as your browser

**MCP tools return empty or stale data.**

- If the dashboard is open with the relay enabled, the MCP server uses live dashboard data. Navigate to the Dashboard tab to trigger a data load.
- If the dashboard is closed, the MCP server falls back to direct API calls using `GITHUB_TOKEN`. REST search lacks check status and review decision data, so PR filters like `failing` and `approved` may return empty results. Use the relay for full filter accuracy.
- The relay snapshot updates on each full refresh (every 5 minutes by default). Hot poll updates are not forwarded to the relay.

**How do I sign out or reset everything?**

- **Sign out**: Settings > Data > Sign out. This clears your auth token and returns you to the login page. Your config is preserved.
Expand Down
68 changes: 68 additions & 0 deletions mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# github-tracker-mcp

MCP server for [GitHub Tracker](https://github.com/gordon-code/github-tracker) — exposes dashboard data (open PRs, issues, failing CI) to AI clients like Claude Code and Cursor.

## Install

```bash
# Run without installing
npx github-tracker-mcp

# Or install globally
npm install -g github-tracker-mcp
```

## Configuration

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `GITHUB_TOKEN` | No | — | Classic PAT with `repo` and `read:org` scopes (recommended), or fine-grained PAT with Actions (read), Contents (read), Issues (read), and Pull requests (read) permissions. Fine-grained PATs skip scope validation at startup. |
| `MCP_WS_PORT` | No | `9876` | WebSocket relay port for receiving live data from the dashboard SPA. |

`GITHUB_TOKEN` is required for standalone (direct API) mode. In relay mode the server receives data from the dashboard and works without a token. If you set `GITHUB_TOKEN` alongside the relay, the server uses it as a fallback when the relay disconnects.

## Claude Code setup

```bash
claude mcp add --transport stdio --env GITHUB_TOKEN=ghp_... github-tracker -- npx -y github-tracker-mcp@latest
```

Or add `--scope project` to store in `.mcp.json` (shared with git — don't include real tokens in committed files).

## Available tools

| Tool | Description | Parameters |
|------|-------------|------------|
| `get_dashboard_summary` | Aggregated counts of open PRs, issues, failing CI, PRs needing review, approved but unmerged | `scope?` (involves_me\|all, default: involves_me) |
| `get_open_prs` | Open PRs with check status and review decision | `repo?`, `status?` (all\|needs_review\|failing\|approved\|draft) |
| `get_open_issues` | Open issues across tracked repos | `repo?` |
| `get_failing_actions` | In-progress or recently failed workflow runs | `repo?` |
| `get_pr_details` | Detailed info about a specific PR | `repo`, `number` |
| `get_rate_limit` | Current GitHub API rate limit status | — |

`repo` parameters use `owner/repo` format (e.g., `octocat/hello-world`).

## Resources

- `tracker://config` — current dashboard configuration (selected repos, tracked users)
- `tracker://repos` — list of tracked repositories

## WebSocket relay

Enable the WebSocket relay in the dashboard's Settings page to let the MCP server receive live data directly from the SPA. When connected, the server prefers relay data and falls back to direct GitHub API calls. This reduces API usage and gives the AI client the same enriched data visible in the dashboard without separate polling.

The relay listens on `ws://127.0.0.1:9876` by default. Override with `MCP_WS_PORT`.

### Direct API mode limitations

Without the relay, the MCP server uses REST search which lacks some GraphQL-sourced fields. This affects:

- `get_open_prs` — `status=failing` and `status=approved` filters return empty results (REST search lacks check status and review decision data). `status=needs_review` works correctly via the `review-requested:` search qualifier.
- `get_dashboard_summary` — `approvedUnmergedCount` is always 0; `scope` parameter works as expected
- `get_dashboard_summary` — when the relay IS connected, `scope` is ignored (the relay always reflects the dashboard's current data set)

For full filter accuracy for `failing` and `approved` statuses, use the WebSocket relay.

## Full documentation

See the [GitHub Tracker repository](https://github.com/gordon-code/github-tracker) for deployment, contributing, and architecture details.
44 changes: 44 additions & 0 deletions mcp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "github-tracker-mcp",
"version": "0.1.0",
"description": "MCP server for GitHub Tracker — serves dashboard data to AI clients",
"type": "module",
"bin": {
"github-tracker-mcp": "./dist/index.js"
},
"files": ["dist", "README.md"],
"scripts": {
"build": "tsup",
"typecheck": "tsc --build",
"dev": "tsx src/index.ts",
"start": "node dist/index.js",
"test": "vitest run",
"prepublishOnly": "pnpm run build"
},
"keywords": ["mcp", "github", "dashboard", "model-context-protocol"],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/gordon-code/github-tracker.git",
"directory": "mcp"
},
"engines": {
"node": ">=22"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
"ws": "^8.17.0",
"zod": "4.3.6"
},
"devDependencies": {
"@octokit/core": "7.0.6",
"@octokit/plugin-paginate-rest": "14.0.0",
"@octokit/plugin-retry": "8.1.0",
"@octokit/plugin-throttling": "11.0.3",
"@types/ws": "^8.0.0",
"tsup": "^8.0.0",
"tsx": "^4.0.0",
"typescript": "5.9.3",
"vitest": "^4.0.0"
}
}
Loading