From 1138265c4914b7ec0ec1a8e05538d0116f0cc7a5 Mon Sep 17 00:00:00 2001 From: balovbohdan Date: Mon, 30 Mar 2026 17:29:02 +0300 Subject: [PATCH 1/2] Add GitHub MCP Integration to TORQ Implements OpenSpec change: add-github-mcp-integration - Configure GitHub MCP HTTP server via .mcp.json - Add GITHUB_MCP_TOKEN to .env configuration template - Create 18 comprehensive test fixtures for GitHub API responses - Issue operations (search, get, not-found) - PR operations (create, update, list, get, conflict) - Repository operations (search, get) - Error responses (rate-limited, unauthorized, forbidden, conflict) - Write complete setup and reference documentation - PAT creation steps - Tool reference with examples - Error handling and troubleshooting - Rate limit information - Create formal specifications - GitHub Integration spec (FR-001 through FR-006) - Test Fixture System spec This enables Claude to autonomously: - Create and update pull requests in torqlab/torq - Search and read GitHub issues - Access repository metadata - Use test fixtures for deterministic testing Co-Authored-By: Claude Haiku 4.5 --- .claude/GITHUB_MCP_SETUP.md | 476 ++++++++++++++++++ .../github-mcp/error-conflict.json | 10 + .../github-mcp/error-forbidden.json | 5 + .../github-mcp/error-not-found.json | 5 + .../github-mcp/error-rate-limited.json | 6 + .../github-mcp/error-unauthorized.json | 5 + .../issues/get-issue-not-found.json | 5 + .../github-mcp/issues/get-issue.json | 37 ++ .../issues/search-issues-empty.json | 5 + .../github-mcp/issues/search-issues.json | 52 ++ .../pull-requests/create-pr-conflict.json | 10 + .../pull-requests/create-pr-success.json | 59 +++ .../pull-requests/get-pr-not-found.json | 5 + .../github-mcp/pull-requests/get-pr.json | 38 ++ .../github-mcp/pull-requests/list-prs.json | 36 ++ .../pull-requests/update-pr-success.json | 24 + .../github-mcp/repositories/get-repo.json | 33 ++ .../github-mcp/repositories/search-repos.json | 27 + .env.example | 5 + .mcp.json | 11 + .../add-github-mcp-integration/design.md | 223 ++++++++ .../add-github-mcp-integration/proposal.md | 78 +++ .../specs/fixture-system/spec.md | 348 +++++++++++++ .../specs/github-integration/spec.md | 267 ++++++++++ .../add-github-mcp-integration/tasks.md | 168 +++++++ 25 files changed, 1938 insertions(+) create mode 100644 .claude/GITHUB_MCP_SETUP.md create mode 100644 .claude/test-fixtures/github-mcp/error-conflict.json create mode 100644 .claude/test-fixtures/github-mcp/error-forbidden.json create mode 100644 .claude/test-fixtures/github-mcp/error-not-found.json create mode 100644 .claude/test-fixtures/github-mcp/error-rate-limited.json create mode 100644 .claude/test-fixtures/github-mcp/error-unauthorized.json create mode 100644 .claude/test-fixtures/github-mcp/issues/get-issue-not-found.json create mode 100644 .claude/test-fixtures/github-mcp/issues/get-issue.json create mode 100644 .claude/test-fixtures/github-mcp/issues/search-issues-empty.json create mode 100644 .claude/test-fixtures/github-mcp/issues/search-issues.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/create-pr-conflict.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/create-pr-success.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/get-pr-not-found.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/get-pr.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/list-prs.json create mode 100644 .claude/test-fixtures/github-mcp/pull-requests/update-pr-success.json create mode 100644 .claude/test-fixtures/github-mcp/repositories/get-repo.json create mode 100644 .claude/test-fixtures/github-mcp/repositories/search-repos.json create mode 100644 .mcp.json create mode 100644 openspec/changes/add-github-mcp-integration/design.md create mode 100644 openspec/changes/add-github-mcp-integration/proposal.md create mode 100644 openspec/changes/add-github-mcp-integration/specs/fixture-system/spec.md create mode 100644 openspec/changes/add-github-mcp-integration/specs/github-integration/spec.md create mode 100644 openspec/changes/add-github-mcp-integration/tasks.md diff --git a/.claude/GITHUB_MCP_SETUP.md b/.claude/GITHUB_MCP_SETUP.md new file mode 100644 index 0000000..d3f5db6 --- /dev/null +++ b/.claude/GITHUB_MCP_SETUP.md @@ -0,0 +1,476 @@ +# GitHub MCP Setup Guide + +Complete guide for setting up and using GitHub integration in TORQ via Model Context Protocol (MCP). + +## Prerequisites + +- GitHub account with access to `torqlab` organization +- Admin or write access to `torqlab/torq` repository +- Claude CLI installed and configured +- 5 minutes to complete setup + +## Step 1: Create Fine-Grained Personal Access Token + +GitHub Personal Access Tokens (PAT) provide secure, scoped access to the GitHub API. + +### Instructions + +1. **Go to GitHub Settings** + - Navigate to https://github.com/settings/personal-access-tokens/new + - Or: GitHub → Settings → Developer settings → Personal access tokens (Fine-grained tokens) + +2. **Configure Token** + - **Token name**: `TORQ-Claude-Integration` (or your preference) + - **Description**: GitHub MCP integration for TORQ project automation + - **Expiration**: 90 days (recommended) or Custom + - **Resource owner**: `torqlab` (organization) + +3. **Set Permissions** + + Select **only** these permissions (minimal scope): + - ✅ **Issues**: `read` (list, read issues and issue comments) + - ✅ **Pull requests**: `read` (read PRs) and `write` (create/update PRs) + - ✅ **Contents**: `read` (read repository code and metadata) + + Leave all other permissions **unchecked**. + +4. **Generate & Copy Token** + - Click "Generate token" + - ⚠️ **Important**: Copy the token immediately (it only displays once) + - Save to clipboard or temporary location + +## Step 2: Configure Environment Variable + +### In `.env` file + +1. **Open `.env` in TORQ project root** + + ```bash + cd /Users/balovb/Documents/torq/torq + nano .env # or your preferred editor + ``` + +2. **Find the GITHUB_MCP_TOKEN line** + + Look for: + ```bash + export GITHUB_MCP_TOKEN=your_github_fine_grained_pat_here + ``` + +3. **Replace placeholder with your token** + + ```bash + export GITHUB_MCP_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ``` + +4. **Save the file** + +5. **Verify it's gitignored** ✅ + ```bash + grep "\.env" .gitignore # Should show .env is ignored + ``` + +### ✅ Security Checklist + +- [ ] Token is only in `.env` (never in `.mcp.json`) +- [ ] `.env` is in `.gitignore` +- [ ] Token is never committed to git +- [ ] Never share token or paste in logs/documentation + +## Step 3: Verify Configuration + +### Check MCP Configuration + +Verify `.mcp.json` is valid JSON: + +```bash +jq . .mcp.json +``` + +Expected output: +```json +{ + "mcpServers": { + "github": { + "type": "http", + "url": "https://github-mcp.anthropic.com", + "env": { + "GITHUB_TOKEN": "${GITHUB_MCP_TOKEN}" + } + } + } +} +``` + +### Test with Claude CLI + +After setting `GITHUB_MCP_TOKEN` in `.env`: + +```bash +# Verify Claude can see GitHub tools +claude --mcp +``` + +You should see GitHub MCP tools listed: +- `gh_search_issues` +- `gh_get_issue` +- `gh_create_pull_request` +- `gh_update_pull_request` +- `gh_list_pull_requests` +- `gh_get_pull_request` +- `gh_search_repositories` +- `gh_get_repository` + +## Tool Reference + +Once configured, these tools become available to Claude: + +### Issue Operations + +#### `gh_search_issues(query, state, labels, assignee)` +Search for GitHub issues. + +**Parameters**: +- `query` (string, required): Search query (e.g., "is:open label:bug") +- `state` (string, optional): "open", "closed", or "all" (default: "open") +- `labels` (array, optional): Filter by labels (e.g., ["enhancement", "documentation"]) +- `assignee` (string, optional): Filter by assignee username + +**Example**: +``` +gh_search_issues("is:open", state="open", labels=["enhancement"]) +``` + +**Returns**: List of issue objects with metadata + +#### `gh_get_issue(number)` +Get detailed information about a specific issue. + +**Parameters**: +- `number` (integer, required): Issue number + +**Example**: +``` +gh_get_issue(123) +``` + +**Returns**: Complete issue object including comments + +#### `gh_list_issues(state, per_page)` +List issues in repository. + +**Parameters**: +- `state` (string, optional): "open", "closed", or "all" +- `per_page` (integer, optional): Results per page (default: 30, max: 100) + +**Returns**: List of issue objects with pagination support + +### Pull Request Operations + +#### `gh_create_pull_request(title, body, head, base, draft)` +Create a new pull request. + +**Parameters**: +- `title` (string, required): PR title +- `body` (string, optional): PR description (markdown supported) +- `head` (string, required): Source branch name +- `base` (string, optional): Target branch (default: "main") +- `draft` (boolean, optional): Create as draft PR (default: false) + +**Example**: +``` +gh_create_pull_request( + title="Add GitHub MCP integration", + body="Enables Claude to create PRs and manage issues", + head="feature/github-mcp", + base="main" +) +``` + +**Returns**: Created PR object with number and URL + +**Possible Errors**: +- 409 Conflict: Merge conflict between branches +- 422 Unprocessable Entity: Invalid parameters or reference already exists + +#### `gh_update_pull_request(number, state, body)` +Update pull request state or description. + +**Parameters**: +- `number` (integer, required): PR number +- `state` (string, optional): "open", "draft", or "closed" +- `body` (string, optional): New PR description + +**Example**: +``` +gh_update_pull_request(456, state="draft", body="Work in progress") +``` + +**Returns**: Updated PR object + +#### `gh_list_pull_requests(state, base, per_page)` +List pull requests in repository. + +**Parameters**: +- `state` (string, optional): "open", "closed", or "all" +- `base` (string, optional): Filter by base branch +- `per_page` (integer, optional): Results per page (default: 30) + +**Returns**: List of PR objects + +#### `gh_get_pull_request(number)` +Get detailed information about a specific PR. + +**Parameters**: +- `number` (integer, required): PR number + +**Example**: +``` +gh_get_pull_request(456) +``` + +**Returns**: Complete PR object including commits and reviews + +### Repository Operations + +#### `gh_search_repositories(query, org)` +Search for repositories in an organization. + +**Parameters**: +- `query` (string, required): Search query +- `org` (string, optional): Organization name (default: "torqlab") + +**Returns**: List of repository objects + +#### `gh_get_repository(repo)` +Get repository metadata. + +**Parameters**: +- `repo` (string, required): Repository in format "owner/name" + +**Example**: +``` +gh_get_repository("torqlab/torq") +``` + +**Returns**: Repository object with stars, forks, open issues, etc. + +## Common Workflows + +### Example 1: Search for Open Enhancement Issues + +``` +gh_search_issues("is:open", labels=["enhancement"]) +``` + +### Example 2: Create a PR from Specification + +``` +gh_create_pull_request( + title="47 Add GitHub MCP Integration", + body="Implements specification from /openspec/changes/add-github-mcp-integration/\n\nEnables:\n- Issue searching\n- PR creation and updates\n- Repository metadata access", + head="feature/github-mcp", + base="main" +) +``` + +### Example 3: Update PR to Mark as Draft + +``` +gh_update_pull_request(456, state="draft") +``` + +### Example 4: List All Open PRs + +``` +gh_list_pull_requests(state="open") +``` + +## Rate Limits + +GitHub API has rate limits: + +- **Search API**: 60 requests per hour per user +- **Standard API**: 5,000 requests per hour per user (with authentication) +- **Rate Limit Window**: Resets hourly + +### Rate Limit Headers + +Responses include rate limit information: +``` +X-RateLimit-Limit: 5000 +X-RateLimit-Remaining: 4999 +X-RateLimit-Reset: 1234567890 +``` + +### Best Practices + +- ✅ Cache search results when possible +- ✅ Use filters to reduce unnecessary searches +- ✅ Batch operations when practical +- ✅ Implement exponential backoff on 429 errors +- ❌ Don't retry immediately on rate limit + +## Error Handling + +### Common Errors and Solutions + +#### 401 Unauthorized +- **Cause**: Token invalid, expired, or not set +- **Solution**: + 1. Verify `GITHUB_MCP_TOKEN` is set in `.env` + 2. Check token hasn't expired (90 days) + 3. Create new token if needed + +#### 403 Forbidden +- **Cause**: Token doesn't have required permissions +- **Solution**: + 1. Verify PAT has "issues:read", "pull_requests:read", "pull_requests:write" + 2. Verify PAT is scoped to `torqlab` organization + 3. Create new token with correct permissions + +#### 404 Not Found +- **Cause**: Issue/PR/repository doesn't exist +- **Solution**: Verify issue/PR number or repository name + +#### 409 Conflict +- **Cause**: PR creation failed due to merge conflict or branch issues +- **Solution**: + 1. Check branches can be merged + 2. Resolve conflicts in the branch + 3. Verify head and base branches exist + +#### 429 Too Many Requests +- **Cause**: Rate limit exceeded +- **Solution**: + 1. Wait for rate limit window to reset + 2. Reduce request frequency + 3. Check remaining quota with `gh_get_rate_limits` (if available) + +### Error Response Format + +```json +{ + "message": "Error description", + "documentation_url": "https://docs.github.com/...", + "status": 404 +} +``` + +## Troubleshooting + +### Claude doesn't recognize GitHub tools + +1. **Verify `.mcp.json` syntax**: + ```bash + jq . .mcp.json + ``` + +2. **Check `.env` has token**: + ```bash + echo $GITHUB_MCP_TOKEN # Should show token, not empty + ``` + +3. **Restart Claude CLI**: + ```bash + # Close and reopen Claude CLI + # Or run with fresh environment + ``` + +### Token-related errors + +1. **Token not found**: + - Verify `GITHUB_MCP_TOKEN=...` is in `.env` + - Not `GITHUB_TOKEN` (different variable) + +2. **Token expired**: + - GitHub PATs expire after 90 days + - Create new token and update `.env` + +3. **Permissions denied**: + - Verify token has required scopes + - Create new token with correct permissions + +### Test Fixtures + +Test fixtures are provided in `.claude/test-fixtures/github-mcp/` for: +- Deterministic testing without API calls +- Offline development and testing +- CI/CD pipelines with no credentials +- Prototyping without rate limit concerns + +## Security Best Practices + +1. ✅ **Never commit `.env` to git** + - `.gitignore` includes `.env` + - Double-check before committing + +2. ✅ **Rotate tokens regularly** + - Create new token every 90 days + - Revoke old tokens on GitHub settings + +3. ✅ **Use minimal permissions** + - Only request required scopes + - Regular security audit of permissions + +4. ✅ **Monitor API usage** + - Check GitHub settings for unusual API activity + - Review token usage logs periodically + +5. ✅ **Never expose tokens in logs** + - Don't log API responses with sensitive data + - Be careful when sharing error messages + +## Advanced Configuration + +### Organization-Wide Setup (Team) + +For team/organization use: + +1. Create PAT in organization account (not personal) +2. Store in shared secrets management system +3. Document in team onboarding docs +4. Rotate tokens as part of security policy + +### Multiple Organizations + +If TORQ moves to multiple organizations: + +1. Modify `.mcp.json` to add additional servers +2. Create separate PATs for each organization +3. Store each token in `.env` with org-specific variable name + +### Custom GitHub Enterprise + +For GitHub Enterprise instances: + +1. Modify `.mcp.json` URL to your GHE instance +2. Create PAT with GHE-specific permissions +3. Update documentation + +## Support & Issues + +### Getting Help + +- **Claude Code Docs**: https://github.com/anthropics/claude-code +- **GitHub API Docs**: https://docs.github.com/rest +- **GitHub MCP Docs**: Check Anthropic documentation + +### Reporting Issues + +If you encounter problems: + +1. Check error message for specific issue +2. Review troubleshooting section above +3. Verify configuration with provided steps +4. Report issue with: + - Error message (no tokens!) + - Steps to reproduce + - Configuration details (no secrets!) + +## References + +- GitHub REST API Documentation: https://docs.github.com/rest +- GitHub Personal Access Tokens: https://docs.github.com/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token +- GitHub API Rate Limiting: https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting +- Claude Code: https://github.com/anthropics/claude-code diff --git a/.claude/test-fixtures/github-mcp/error-conflict.json b/.claude/test-fixtures/github-mcp/error-conflict.json new file mode 100644 index 0000000..0caaf6e --- /dev/null +++ b/.claude/test-fixtures/github-mcp/error-conflict.json @@ -0,0 +1,10 @@ +{ + "message": "Merge conflict", + "documentation_url": "https://docs.github.com/rest/reference/pulls#create-a-pull-request", + "status": 409, + "errors": [ + { + "message": "Pull request can't be created: There was a conflict merging the branch into main." + } + ] +} diff --git a/.claude/test-fixtures/github-mcp/error-forbidden.json b/.claude/test-fixtures/github-mcp/error-forbidden.json new file mode 100644 index 0000000..f1873f6 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/error-forbidden.json @@ -0,0 +1,5 @@ +{ + "message": "Resource not accessible by integration", + "documentation_url": "https://docs.github.com/rest/reference/pulls", + "status": 403 +} diff --git a/.claude/test-fixtures/github-mcp/error-not-found.json b/.claude/test-fixtures/github-mcp/error-not-found.json new file mode 100644 index 0000000..4caeff6 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/error-not-found.json @@ -0,0 +1,5 @@ +{ + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/reference/issues", + "status": 404 +} diff --git a/.claude/test-fixtures/github-mcp/error-rate-limited.json b/.claude/test-fixtures/github-mcp/error-rate-limited.json new file mode 100644 index 0000000..68ae39c --- /dev/null +++ b/.claude/test-fixtures/github-mcp/error-rate-limited.json @@ -0,0 +1,6 @@ +{ + "message": "API rate limit exceeded for user ID 12345.", + "documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting", + "status": 429, + "retry_after": 60 +} diff --git a/.claude/test-fixtures/github-mcp/error-unauthorized.json b/.claude/test-fixtures/github-mcp/error-unauthorized.json new file mode 100644 index 0000000..49008a5 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/error-unauthorized.json @@ -0,0 +1,5 @@ +{ + "message": "Bad credentials", + "documentation_url": "https://docs.github.com/rest", + "status": 401 +} diff --git a/.claude/test-fixtures/github-mcp/issues/get-issue-not-found.json b/.claude/test-fixtures/github-mcp/issues/get-issue-not-found.json new file mode 100644 index 0000000..f2310eb --- /dev/null +++ b/.claude/test-fixtures/github-mcp/issues/get-issue-not-found.json @@ -0,0 +1,5 @@ +{ + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/reference/issues#get-an-issue", + "status": 404 +} diff --git a/.claude/test-fixtures/github-mcp/issues/get-issue.json b/.claude/test-fixtures/github-mcp/issues/get-issue.json new file mode 100644 index 0000000..e4967d9 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/issues/get-issue.json @@ -0,0 +1,37 @@ +{ + "number": 123, + "title": "Add GitHub integration", + "body": "Enable Claude to create PRs and manage issues programmatically for automation", + "state": "open", + "labels": [ + { + "id": 1, + "name": "enhancement", + "description": "New feature or request" + }, + { + "id": 2, + "name": "documentation", + "description": "Improvements or additions to documentation" + } + ], + "assignee": { + "login": "developer-name", + "id": 12345, + "avatar_url": "https://avatars.githubusercontent.com/u/12345?v=4", + "type": "User" + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "comments": 5, + "comments_url": "https://api.github.com/repos/torqlab/torq/issues/123/comments", + "url": "https://github.com/torqlab/torq/issues/123", + "repository_url": "https://api.github.com/repos/torqlab/torq", + "id": 1234567890, + "node_id": "I_kwDOABC123DEF", + "user": { + "login": "issue-creator", + "id": 54321, + "avatar_url": "https://avatars.githubusercontent.com/u/54321?v=4" + } +} diff --git a/.claude/test-fixtures/github-mcp/issues/search-issues-empty.json b/.claude/test-fixtures/github-mcp/issues/search-issues-empty.json new file mode 100644 index 0000000..09e9aa4 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/issues/search-issues-empty.json @@ -0,0 +1,5 @@ +{ + "total_count": 0, + "incomplete_results": false, + "items": [] +} diff --git a/.claude/test-fixtures/github-mcp/issues/search-issues.json b/.claude/test-fixtures/github-mcp/issues/search-issues.json new file mode 100644 index 0000000..8934478 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/issues/search-issues.json @@ -0,0 +1,52 @@ +{ + "total_count": 2, + "incomplete_results": false, + "items": [ + { + "number": 123, + "title": "Add GitHub integration", + "body": "Enable Claude to create PRs and manage issues", + "state": "open", + "labels": [ + { + "name": "enhancement", + "description": "New feature or request" + }, + { + "name": "documentation", + "description": "Improvements or additions to documentation" + } + ], + "assignee": { + "login": "developer-name", + "avatar_url": "https://avatars.githubusercontent.com/u/12345?v=4" + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "comments": 5, + "url": "https://github.com/torqlab/torq/issues/123", + "repository_url": "https://api.github.com/repos/torqlab/torq" + }, + { + "number": 124, + "title": "Improve test coverage", + "body": "Add more comprehensive unit tests", + "state": "open", + "labels": [ + { + "name": "testing", + "description": "Testing and test infrastructure" + } + ], + "assignee": { + "login": "tester-name", + "avatar_url": "https://avatars.githubusercontent.com/u/54321?v=4" + }, + "created_at": "2026-03-28T10:00:00Z", + "updated_at": "2026-03-29T16:00:00Z", + "comments": 2, + "url": "https://github.com/torqlab/torq/issues/124", + "repository_url": "https://api.github.com/repos/torqlab/torq" + } + ] +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/create-pr-conflict.json b/.claude/test-fixtures/github-mcp/pull-requests/create-pr-conflict.json new file mode 100644 index 0000000..0caaf6e --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/create-pr-conflict.json @@ -0,0 +1,10 @@ +{ + "message": "Merge conflict", + "documentation_url": "https://docs.github.com/rest/reference/pulls#create-a-pull-request", + "status": 409, + "errors": [ + { + "message": "Pull request can't be created: There was a conflict merging the branch into main." + } + ] +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/create-pr-success.json b/.claude/test-fixtures/github-mcp/pull-requests/create-pr-success.json new file mode 100644 index 0000000..e29609f --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/create-pr-success.json @@ -0,0 +1,59 @@ +{ + "id": 1, + "number": 456, + "title": "Implement GitHub MCP integration", + "body": "Add MCP integration for GitHub API access, enabling automated PR creation and issue management", + "state": "open", + "draft": false, + "head": { + "label": "torqlab:feature/github-mcp", + "ref": "feature/github-mcp", + "sha": "abc123def456789abc123def456789abc123def4", + "repo": { + "name": "torq", + "full_name": "torqlab/torq", + "owner": { + "login": "torqlab", + "id": 1, + "type": "Organization" + } + } + }, + "base": { + "label": "torqlab:main", + "ref": "main", + "sha": "def456abc123789def456abc123789def456abc1", + "repo": { + "name": "torq", + "full_name": "torqlab/torq", + "owner": { + "login": "torqlab", + "id": 1, + "type": "Organization" + } + } + }, + "labels": [ + { + "id": 3, + "name": "infrastructure", + "description": "Infrastructure and build system" + } + ], + "assignee": { + "login": "developer-name", + "id": 12345, + "avatar_url": "https://avatars.githubusercontent.com/u/12345?v=4" + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T12:00:00Z", + "merged_at": null, + "comments": 0, + "commits": 5, + "additions": 245, + "deletions": 12, + "changed_files": 8, + "url": "https://github.com/torqlab/torq/pull/456", + "html_url": "https://github.com/torqlab/torq/pull/456", + "statuses_url": "https://api.github.com/repos/torqlab/torq/statuses/abc123def456789abc123def456789abc123def4" +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/get-pr-not-found.json b/.claude/test-fixtures/github-mcp/pull-requests/get-pr-not-found.json new file mode 100644 index 0000000..8752f53 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/get-pr-not-found.json @@ -0,0 +1,5 @@ +{ + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/reference/pulls#get-a-pull-request", + "status": 404 +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/get-pr.json b/.claude/test-fixtures/github-mcp/pull-requests/get-pr.json new file mode 100644 index 0000000..32e6dff --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/get-pr.json @@ -0,0 +1,38 @@ +{ + "id": 1, + "number": 456, + "title": "Implement GitHub MCP integration", + "body": "Add MCP integration for GitHub API access, enabling automated PR creation and issue management", + "state": "open", + "draft": false, + "head": { + "label": "torqlab:feature/github-mcp", + "ref": "feature/github-mcp", + "sha": "abc123def456789abc123def456789abc123def4" + }, + "base": { + "label": "torqlab:main", + "ref": "main", + "sha": "def456abc123789def456abc123789def456abc1" + }, + "labels": [ + { + "id": 3, + "name": "infrastructure" + } + ], + "assignee": { + "login": "developer-name", + "id": 12345 + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T12:00:00Z", + "merged_at": null, + "comments": 3, + "commits": 5, + "additions": 245, + "deletions": 12, + "changed_files": 8, + "url": "https://github.com/torqlab/torq/pull/456", + "html_url": "https://github.com/torqlab/torq/pull/456" +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/list-prs.json b/.claude/test-fixtures/github-mcp/pull-requests/list-prs.json new file mode 100644 index 0000000..16f800e --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/list-prs.json @@ -0,0 +1,36 @@ +{ + "total_count": 3, + "incomplete_results": false, + "items": [ + { + "id": 1, + "number": 456, + "title": "Implement GitHub MCP integration", + "state": "open", + "draft": false, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T12:00:00Z", + "url": "https://github.com/torqlab/torq/pull/456" + }, + { + "id": 2, + "number": 455, + "title": "Fix linting errors", + "state": "open", + "draft": false, + "created_at": "2026-03-29T10:00:00Z", + "updated_at": "2026-03-29T15:00:00Z", + "url": "https://github.com/torqlab/torq/pull/455" + }, + { + "id": 3, + "number": 454, + "title": "Update dependencies", + "state": "open", + "draft": true, + "created_at": "2026-03-28T08:00:00Z", + "updated_at": "2026-03-28T08:00:00Z", + "url": "https://github.com/torqlab/torq/pull/454" + } + ] +} diff --git a/.claude/test-fixtures/github-mcp/pull-requests/update-pr-success.json b/.claude/test-fixtures/github-mcp/pull-requests/update-pr-success.json new file mode 100644 index 0000000..a73309d --- /dev/null +++ b/.claude/test-fixtures/github-mcp/pull-requests/update-pr-success.json @@ -0,0 +1,24 @@ +{ + "id": 1, + "number": 456, + "title": "Implement GitHub MCP integration", + "body": "Add MCP integration for GitHub API access", + "state": "draft", + "draft": true, + "head": { + "label": "torqlab:feature/github-mcp", + "ref": "feature/github-mcp", + "sha": "abc123def456789abc123def456789abc123def4" + }, + "base": { + "label": "torqlab:main", + "ref": "main", + "sha": "def456abc123789def456abc123789def456abc1" + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T13:00:00Z", + "merged_at": null, + "comments": 0, + "commits": 5, + "url": "https://github.com/torqlab/torq/pull/456" +} diff --git a/.claude/test-fixtures/github-mcp/repositories/get-repo.json b/.claude/test-fixtures/github-mcp/repositories/get-repo.json new file mode 100644 index 0000000..2eb0c38 --- /dev/null +++ b/.claude/test-fixtures/github-mcp/repositories/get-repo.json @@ -0,0 +1,33 @@ +{ + "id": 123456789, + "name": "torq", + "full_name": "torqlab/torq", + "owner": { + "login": "torqlab", + "id": 1, + "avatar_url": "https://avatars.githubusercontent.com/u/1?v=4", + "type": "Organization" + }, + "description": "A social activity tracking and visualization platform with Strava integration", + "url": "https://api.github.com/repos/torqlab/torq", + "html_url": "https://github.com/torqlab/torq", + "created_at": "2024-01-15T10:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "pushed_at": "2026-03-30T13:45:00Z", + "homepage": "https://torqlab.com", + "stargazers_count": 42, + "watchers_count": 42, + "forks_count": 8, + "open_issues_count": 12, + "language": "TypeScript", + "topics": [ + "strava", + "activities", + "visualization", + "tracking" + ], + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true +} diff --git a/.claude/test-fixtures/github-mcp/repositories/search-repos.json b/.claude/test-fixtures/github-mcp/repositories/search-repos.json new file mode 100644 index 0000000..2494bfb --- /dev/null +++ b/.claude/test-fixtures/github-mcp/repositories/search-repos.json @@ -0,0 +1,27 @@ +{ + "total_count": 1, + "incomplete_results": false, + "items": [ + { + "id": 123456789, + "name": "torq", + "full_name": "torqlab/torq", + "owner": { + "login": "torqlab", + "id": 1, + "type": "Organization" + }, + "description": "A social activity tracking and visualization platform with Strava integration", + "url": "https://api.github.com/repos/torqlab/torq", + "html_url": "https://github.com/torqlab/torq", + "created_at": "2024-01-15T10:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "pushed_at": "2026-03-30T13:45:00Z", + "stargazers_count": 42, + "watchers_count": 42, + "forks_count": 8, + "open_issues_count": 12, + "language": "TypeScript" + } + ] +} diff --git a/.env.example b/.env.example index 54e75b1..ab61c4b 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,11 @@ PORT=3000 NODE_ENV=production UI_ORIGIN=http://localhost:3001 +# GitHub MCP Integration (Optional, but required for GitHub integration features) +# Create a fine-grained PAT at https://github.com/settings/personal-access-tokens/new +# Required permissions: issues:read, pull_requests:read, pull_requests:write, contents:read +GITHUB_MCP_TOKEN=your_github_fine_grained_pat_here + # Optional: Database Configuration (if needed) # DATABASE_URL=postgresql://user:password@localhost:5432/torq diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..2c598e9 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "github": { + "type": "http", + "url": "https://github-mcp.anthropic.com", + "env": { + "GITHUB_TOKEN": "${GITHUB_MCP_TOKEN}" + } + } + } +} diff --git a/openspec/changes/add-github-mcp-integration/design.md b/openspec/changes/add-github-mcp-integration/design.md new file mode 100644 index 0000000..341d8c7 --- /dev/null +++ b/openspec/changes/add-github-mcp-integration/design.md @@ -0,0 +1,223 @@ +# Technical Design - GitHub MCP Integration + +## Architecture Overview + +``` +┌─────────────────────────────────────────┐ +│ Claude (via Claude Code / CLI) │ +├─────────────────────────────────────────┤ +│ .mcp.json (GitHub MCP HTTP client) │ +├─────────────────────────────────────────┤ +│ GitHub MCP Server (hosted by Anthropic) │ +│ https://github-mcp.anthropic.com │ +├─────────────────────────────────────────┤ +│ GitHub REST API (via HTTP) │ +├─────────────────────────────────────────┤ +│ torqlab/torq repository │ +└─────────────────────────────────────────┘ +``` + +## Configuration Strategy + +### Why HTTP over stdio +- **Maintained by Anthropic**: Reduces maintenance burden +- **Single point of failure**: Not our process to manage +- **Network isolation**: Works across machine restarts +- **Official implementation**: Most reliable option + +### Why Fine-grained PAT over GitHub App +- **Simpler setup**: No need for GitHub App installation workflow +- **Single org scope**: Fine-grained PAT sufficient for `torqlab` +- **Faster iteration**: No additional infrastructure required +- **Security**: Time-limited tokens, scoped permissions + +### Why Project-level Config +- **Version controlled**: Team consistency via git +- **Reproducible**: New team members can clone and run +- **Auditable**: Changes tracked in git history +- **No global pollution**: Config stays within project + +### Why Fixtures over Real API +- **Rate limit safe**: No API calls during testing +- **Deterministic**: Same results every run +- **Fast**: No network latency +- **Offline capable**: Works without internet +- **Cost zero**: No GitHub API quota consumption + +## File Structure + +``` +torq/ +├── .mcp.json # MCP configuration (new) +├── .env # Token storage (modified) +├── .gitignore # Already includes .env +├── .claude/ +│ ├── GITHUB_MCP_SETUP.md # Setup guide (new) +│ └── test-fixtures/ +│ └── github-mcp/ # Test data (new) +│ ├── issues/ +│ ├── pull-requests/ +│ └── repositories/ +└── openspec/ + └── changes/ + └── add-github-mcp-integration/ + ├── proposal.md # This proposal + ├── design.md # This document + ├── tasks.md # Implementation tasks + └── specs/ # Specification additions + ├── github-integration/ + │ └── spec.md + └── fixture-system/ + └── spec.md +``` + +## MCP Server Configuration + +**File**: `.mcp.json` + +```json +{ + "mcpServers": { + "github": { + "type": "http", + "url": "https://github-mcp.anthropic.com", + "env": { + "GITHUB_TOKEN": "${GITHUB_MCP_TOKEN}" + } + } + } +} +``` + +### Environment Variable Resolution +- Claude CLI reads `.env` file +- Injects `GITHUB_MCP_TOKEN` into MCP server environment +- Token never written to config file (stays in `.env`) + +## Authentication Flow + +1. **Local Setup** + ```bash + # User creates fine-grained PAT on GitHub.com + # User adds to .env + echo "GITHUB_MCP_TOKEN=ghp_xxxx..." >> .env + ``` + +2. **Runtime** + ``` + Claude CLI reads .env → GITHUB_MCP_TOKEN + → Passes to MCP HTTP server + → MCP server includes in Authorization header + → GitHub API validates token + ``` + +3. **Token Scope** + - Organization: `torqlab` + - Repositories: All (can be restricted further) + - Permissions: `issues:read`, `pull_requests:read/write`, `contents:read` + +## Available Tools After Integration + +Once configured, these tools become available to Claude: + +### Issue Operations +- `gh_search_issues(query, state, labels, assignee)` +- `gh_get_issue(repo, number)` +- `gh_list_issues(repo, state, per_page)` + +### Pull Request Operations +- `gh_create_pull_request(repo, title, body, head, base)` +- `gh_update_pull_request(repo, number, state, body)` +- `gh_list_pull_requests(repo, state, base)` +- `gh_get_pull_request(repo, number)` + +### Repository Operations +- `gh_search_repositories(query, org)` +- `gh_get_repository(repo)` + +## Test Fixture Strategy + +### Fixture Location +All fixtures stored in `.claude/test-fixtures/github-mcp/` for: +- Easy discovery by test utilities +- Clear organization by resource type +- Non-interference with runtime config + +### Fixture Format +Each fixture is a JSON file representing an API response: + +```json +{ + "success": true, + "data": { + "number": 123, + "title": "Example Issue", + "state": "open" + } +} +``` + +### Fixture Coverage + +**Issues** +- `search-issues.json` - Successful search result (multiple issues) +- `search-issues-empty.json` - Search with no results +- `get-issue.json` - Single issue detail +- `get-issue-not-found.json` - 404 error case + +**Pull Requests** +- `create-pr-success.json` - Successful creation +- `create-pr-conflict.json` - Merge conflict error +- `update-pr-success.json` - Successful state update +- `list-prs.json` - Multiple PRs +- `get-pr.json` - Single PR detail +- `get-pr-not-found.json` - 404 error case + +**Repositories** +- `search-repos.json` - Organization repositories +- `get-repo.json` - Single repository metadata + +**Error Cases** +- `error-rate-limited.json` - 429 rate limit +- `error-unauthorized.json` - 401 invalid token +- `error-forbidden.json` - 403 insufficient permissions + +## Security Considerations + +### PAT Security +1. **Minimal Permissions**: Only `issues:read`, `pr:read/write`, `contents:read` +2. **No Admin**: Cannot modify org settings, delete repos, or manage team +3. **Time Limited**: 90-day expiration window +4. **Scoped by Org**: `torqlab` only +5. **Audit Trail**: GitHub logs all API calls with token ID + +### Token Storage +1. **Never in Config**: `.mcp.json` is version-controlled but has no secrets +2. **Always in .env**: Environment file is gitignored +3. **Local Only**: Token never transmitted except to GitHub API +4. **Export Careful**: Never include in logs, error messages, or documentation + +### Code Review +- Review all PAT-related changes +- Verify `.env` in `.gitignore` +- Check for token in `.mcp.json` + +## Verification Checklist + +- [ ] `.mcp.json` is valid JSON +- [ ] All fixture JSON files are valid +- [ ] `.env` example includes GITHUB_MCP_TOKEN +- [ ] `.gitignore` includes `.env` and `.env.local` +- [ ] Documentation is complete +- [ ] Setup guide has clear PAT creation steps +- [ ] Error handling documented +- [ ] Rate limit documentation provided + +## Success Criteria + +✅ Configuration passes validation +✅ Fixtures match GitHub API schema +✅ Claude CLI recognizes tools (`claude --mcp`) +✅ PR creation verified on test branch +✅ Setup guide is followable end-to-end +✅ All tests pass with fixtures diff --git a/openspec/changes/add-github-mcp-integration/proposal.md b/openspec/changes/add-github-mcp-integration/proposal.md new file mode 100644 index 0000000..3c76c12 --- /dev/null +++ b/openspec/changes/add-github-mcp-integration/proposal.md @@ -0,0 +1,78 @@ +# GitHub MCP Integration - Proposal + +**Status**: Open Proposal +**Created**: 2026-03-30 +**Author**: Claude +**Scope**: TORQ project-wide + +## Problem Statement + +TORQ currently lacks programmatic GitHub integration. While the project uses GitHub Actions for CI/CD, Claude has no way to: +- Create or update pull requests programmatically +- Read or search GitHub issues +- Create test fixtures for automated workflows + +This limits Claude's ability to autonomously manage GitHub workflows and generate test data for development. + +## Proposed Solution + +Integrate GitHub Model Context Protocol (MCP) server to enable: + +1. **Issue Management** + - Search issues across `torqlab/torq` + - Read individual issue details + - Filter by state, labels, assignees + +2. **Pull Request Management** + - Create new pull requests programmatically + - Update PR state (open/draft/closed) + - List and search existing PRs + - Read PR details and comments + +3. **Repository Context** + - Search for repositories in organization + - Read repository metadata + +## Architecture + +| Aspect | Decision | +|--------|----------| +| **MCP Server Type** | HTTP (hosted GitHub MCP) | +| **Authentication** | Fine-grained Personal Access Token (PAT) | +| **Configuration** | Project-level `.mcp.json` (version-controlled) | +| **Token Storage** | `.env` file (gitignored) | +| **Test Data** | Mocked JSON fixtures (no API calls during testing) | + +## Benefits + +✅ Enables Claude to autonomously create PRs from specifications +✅ Allows automated issue-to-fixture generation for testing +✅ Reduces manual GitHub workflow overhead +✅ Version-controlled configuration for team consistency +✅ Follows TORQ's existing Strava integration pattern + +## Implementation Impact + +- **New Files**: `.mcp.json`, `.claude/GITHUB_MCP_SETUP.md`, test fixtures +- **Modified Files**: `.env` (add placeholder) +- **New Specs**: GitHub Integration spec, Fixture System spec +- **No Breaking Changes**: Backward compatible, feature addition only + +## Success Criteria + +- ✅ `.mcp.json` created and valid +- ✅ GITHUB_MCP_TOKEN configured in `.env` +- ✅ All fixture JSON files created +- ✅ Setup documentation complete +- ✅ `claude --mcp` lists GitHub tools +- ✅ PR creation verified (optional integration test) + +## Risk Mitigation + +| Risk | Mitigation | +|------|-----------| +| PAT exposed in git | `.gitignore` includes `.env`, code review checks | +| Rate limiting | Document limits in setup guide, implement in prompts | +| Tool discovery | Verify `.mcp.json` syntax, test with `claude --mcp` | +| Fixture staleness | Periodic testing against real API | +| Token expiration | Document 90-day rotation policy | diff --git a/openspec/changes/add-github-mcp-integration/specs/fixture-system/spec.md b/openspec/changes/add-github-mcp-integration/specs/fixture-system/spec.md new file mode 100644 index 0000000..980ec66 --- /dev/null +++ b/openspec/changes/add-github-mcp-integration/specs/fixture-system/spec.md @@ -0,0 +1,348 @@ +# Specification: Test Fixture System + +**Status**: Proposed +**Type**: NEW capability +**Component**: Testing Infrastructure +**Audience**: Claude (AI agent), Development Team + +## Summary + +Define a structured system for creating and maintaining mock test data for GitHub MCP integration. This enables deterministic, reproducible testing without requiring live API calls or credentials. + +## Requirements + +### Functional Requirements + +#### FR-001: Fixture Organization +The fixture system MUST organize test data by resource type: +- Issues +- Pull Requests +- Repositories +- Errors +- Edge cases + +**File Structure**: +``` +.claude/test-fixtures/github-mcp/ +├── issues/ +├── pull-requests/ +├── repositories/ +└── errors/ +``` + +#### FR-002: Fixture Format +All fixtures MUST be valid JSON files matching GitHub API schema: +- Fixture files represent API responses +- Include realistic field values +- Include both success and error responses +- Timestamp values must be valid ISO 8601 + +**File Naming Convention**: `{operation}-{scenario}.json` +- Example: `search-issues-empty.json` +- Example: `create-pr-conflict.json` + +#### FR-003: Issue Fixtures +The system MUST provide comprehensive issue test data: + +**Required Issue Fixtures**: +- `search-issues.json` - Multiple issues from search +- `search-issues-empty.json` - Search with zero results +- `get-issue.json` - Single issue detail +- `get-issue-not-found.json` - 404 error response + +**Fields in Issue Fixture**: +- number, title, body, state, labels +- assignee, created_at, updated_at +- comments_count, url, repository + +#### FR-004: Pull Request Fixtures +The system MUST provide comprehensive PR test data: + +**Required PR Fixtures**: +- `create-pr-success.json` - Successful PR creation +- `create-pr-conflict.json` - Merge conflict during creation +- `update-pr-success.json` - Successful state update +- `list-prs.json` - Multiple PRs in list response +- `get-pr.json` - Single PR detail +- `get-pr-not-found.json` - 404 error response + +**Fields in PR Fixture**: +- number, title, body, state, draft +- head (ref, sha), base (ref, sha) +- labels, assignee, created_at, updated_at +- merged_at, comments_count, commits_count, url + +#### FR-005: Repository Fixtures +The system MUST provide repository test data: + +**Required Repository Fixtures**: +- `search-repos.json` - Organization repositories +- `get-repo.json` - Single repository detail + +**Fields in Repository Fixture**: +- name, full_name, description +- url, created_at, updated_at +- stars, forks, open_issues_count + +#### FR-006: Error Response Fixtures +The system MUST provide standardized error responses: + +**Required Error Fixtures**: +- `error-rate-limited.json` - 429 rate limit +- `error-unauthorized.json` - 401 invalid token +- `error-forbidden.json` - 403 insufficient permissions +- `error-not-found.json` - 404 resource not found +- `error-conflict.json` - 409 merge conflict + +**Error Response Schema**: +```json +{ + "error": "Error Name", + "message": "Human readable message", + "status": 429, + "documentation_url": "https://docs.github.com/...", + "retry_after": 60 +} +``` + +### Non-Functional Requirements + +#### NFR-001: JSON Schema Validation +All fixtures MUST: +- Parse as valid JSON +- Match GitHub API v3 response schema +- Include realistic example values +- Use consistent formatting (2-space indentation) + +#### NFR-002: Fixture Discoverability +Fixtures MUST be: +- Organized in predictable directory structure +- Named with descriptive operation and scenario +- Documented with comments about test purpose +- Linked in setup documentation + +#### NFR-003: Maintenance +Fixture system MUST: +- Be easy to update when API changes +- Support adding new scenarios without breaking existing +- Include comments for non-obvious field values +- Have clear versioning when GitHub API updates + +#### NFR-004: Performance +Fixture loading MUST: +- Be instantaneous (< 100ms) +- Not require network access +- Support in-memory caching +- Work offline + +### Scope + +**Included**: +- GitHub API v3 JSON responses +- Success and error responses +- Realistic data values +- Common test scenarios +- Edge cases (empty results, conflicts, rate limits) + +**Excluded**: +- Test runner logic +- Assertions and validation +- Mock server implementation +- API request recording +- Snapshot testing + +## Fixture Categories + +### 1. Success Scenarios + +#### Issue Search +- **File**: `issues/search-issues.json` +- **Scenario**: Query returns multiple matching issues +- **Purpose**: Test filtering and result parsing + +#### Empty Search +- **File**: `issues/search-issues-empty.json` +- **Scenario**: Query matches zero issues +- **Purpose**: Test empty result handling + +#### Single Issue +- **File**: `issues/get-issue.json` +- **Scenario**: Retrieve specific issue detail +- **Purpose**: Test single resource fetching + +#### PR Creation +- **File**: `pull-requests/create-pr-success.json` +- **Scenario**: PR created successfully +- **Purpose**: Test PR creation workflow + +#### PR Update +- **File**: `pull-requests/update-pr-success.json` +- **Scenario**: PR state updated successfully +- **Purpose**: Test PR state management + +### 2. Error Scenarios + +#### Not Found +- **File**: `issues/get-issue-not-found.json` +- **Scenario**: Requested issue doesn't exist (404) +- **Purpose**: Test error handling for missing resources + +#### Rate Limited +- **File**: `errors/error-rate-limited.json` +- **Scenario**: API rate limit exceeded (429) +- **Purpose**: Test rate limit backoff logic + +#### Unauthorized +- **File**: `errors/error-unauthorized.json` +- **Scenario**: Invalid or missing authentication (401) +- **Purpose**: Test authentication error handling + +#### Forbidden +- **File**: `errors/error-forbidden.json` +- **Scenario**: Insufficient permissions (403) +- **Purpose**: Test permission error handling + +#### Conflict +- **File**: `pull-requests/create-pr-conflict.json` +- **Scenario**: PR creation fails due to merge conflict (409) +- **Purpose**: Test conflict resolution guidance + +### 3. Edge Cases + +#### Long Content +- Include issues/PRs with very long titles (500+ chars) +- Include PRs with many comments (100+) +- Purpose: Test truncation and pagination + +#### Special Characters +- Include issues with emoji and unicode characters +- Include PRs with special markdown formatting +- Purpose: Test encoding and display + +#### Dates and Timezones +- Include various date formats and timezones +- Include old and new timestamps +- Purpose: Test date parsing and formatting + +## Fixture Format Examples + +### Issue Search Response +```json +{ + "total_count": 2, + "incomplete_results": false, + "items": [ + { + "number": 123, + "title": "Add GitHub integration", + "body": "Enable Claude to create PRs and manage issues", + "state": "open", + "labels": ["enhancement", "documentation"], + "assignee": "developer-name", + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "comments": 5, + "url": "https://github.com/torqlab/torq/issues/123" + } + ] +} +``` + +### PR Creation Response +```json +{ + "id": 1, + "number": 456, + "title": "Implement GitHub MCP", + "body": "Add MCP integration for GitHub API access", + "state": "open", + "draft": false, + "head": { + "label": "feature/github-mcp", + "ref": "feature/github-mcp", + "sha": "abc123def456" + }, + "base": { + "label": "main", + "ref": "main", + "sha": "def456abc123" + }, + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T12:00:00Z", + "merged_at": null, + "comments": 0, + "commits": 5, + "url": "https://github.com/torqlab/torq/pull/456" +} +``` + +### Error Response +```json +{ + "message": "API rate limit exceeded for user ID 12345.", + "documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting", + "status": 429, + "retry_after": 60 +} +``` + +## Integration Points + +Fixtures should be referenced by: +1. **Test utilities** - Load fixtures for mock responses +2. **Setup documentation** - Show example API responses +3. **Development workflow** - Claude uses fixtures for testing without API +4. **Integration tests** - Compare real responses against fixture schema + +## Validation Rules + +All fixtures MUST pass: + +1. **JSON Syntax** + ```bash + jq . fixture.json > /dev/null + ``` + +2. **Schema Validation** + - Match GitHub API v3 response structure + - Include all required fields + - Use correct data types + +3. **Timestamp Validation** + - All dates must be valid ISO 8601 + - Timestamps must be in UTC timezone (Z suffix) + +4. **URL Validation** + - All URLs must be valid and well-formed + - Should point to appropriate GitHub resource + +5. **Status Code Consistency** + - Error responses must have status field + - Status must be valid HTTP status code + - Message must match status code + +## Success Criteria + +✅ All fixture files created and valid JSON +✅ Fixture schema matches GitHub API v3 +✅ Success and error scenarios covered +✅ Fixtures have realistic example data +✅ Fixtures organized in predictable structure +✅ Documentation references all fixtures +✅ Fixtures load without errors +✅ Tests pass using fixture data + +## Maintenance Schedule + +- **Monthly**: Review fixture schema against GitHub API changelog +- **Quarterly**: Update fixtures with new API fields if applicable +- **As-needed**: Add new fixtures for new test scenarios +- **When API changes**: Update affected fixtures and document changes + +## Future Enhancements + +- Fixture versioning for API version compatibility +- Fixture generation from real API responses +- Fixture-based integration test harness +- Snapshot testing integration +- Performance benchmarking fixtures diff --git a/openspec/changes/add-github-mcp-integration/specs/github-integration/spec.md b/openspec/changes/add-github-mcp-integration/specs/github-integration/spec.md new file mode 100644 index 0000000..1a09516 --- /dev/null +++ b/openspec/changes/add-github-mcp-integration/specs/github-integration/spec.md @@ -0,0 +1,267 @@ +# Specification: GitHub Integration Capability + +**Status**: Proposed +**Type**: NEW capability +**Component**: MCP Integration +**Audience**: Claude (AI agent) + +## Summary + +Enable Claude to programmatically interact with GitHub via Model Context Protocol (MCP). This spec defines the required capabilities for issue and pull request management. + +## Requirements + +### Functional Requirements + +#### FR-001: Issue Search +Claude MUST be able to search for GitHub issues with filters: +- Query string (full-text search) +- State (open, closed, all) +- Labels (filter by label) +- Assignee +- Sort order (created, updated, comments) + +**Expected Response**: List of issues with metadata (number, title, state, labels, created_at, updated_at) + +#### FR-002: Get Issue Details +Claude MUST be able to retrieve full details of a specific issue: +- Issue number +- Repository name +- Full issue content (title, body, state, labels, assignee, comments) + +**Expected Response**: Complete issue object including all fields and comment history + +#### FR-003: Create Pull Request +Claude MUST be able to create new pull requests: +- Repository name +- Title (required) +- Body/description (optional) +- Head branch (required) +- Base branch (required, default: main) +- Draft mode (optional) + +**Expected Response**: Created PR object with number, URL, state + +#### FR-004: Update Pull Request +Claude MUST be able to update PR state and metadata: +- Repository name +- PR number (required) +- State (open, draft, closed) +- Body/description (optional) + +**Expected Response**: Updated PR object with new state + +#### FR-005: List Pull Requests +Claude MUST be able to list PRs with filters: +- Repository name +- State (open, closed, all) +- Base branch (optional) +- Head branch (optional) +- Sort order (created, updated, popularity, long-running) +- Pagination (page, per_page) + +**Expected Response**: List of PR objects with metadata + +#### FR-006: Get Pull Request Details +Claude MUST be able to retrieve full details of a specific PR: +- Repository name +- PR number +- Full PR content (title, body, state, labels, commits, comments, review status) + +**Expected Response**: Complete PR object with all fields and related data + +### Non-Functional Requirements + +#### NFR-001: Rate Limiting +- Respect GitHub API rate limits (60/hour for search, 5000/hour for standard API) +- Implement exponential backoff on rate limit errors +- Cache results when appropriate + +#### NFR-002: Error Handling +- Handle authentication errors (401 Unauthorized) +- Handle permission errors (403 Forbidden) +- Handle not found errors (404 Not Found) +- Handle conflict errors (409 Conflict) +- Handle rate limit errors (429 Too Many Requests) +- Provide clear error messages for debugging + +#### NFR-003: Security +- Use fine-grained PAT with minimal required permissions +- Never expose token in logs, config files, or error messages +- Validate all input parameters +- Sanitize all output for safety + +#### NFR-004: Performance +- Complete issue searches in < 2 seconds +- Complete PR operations in < 3 seconds +- Support pagination for large result sets +- Cache metadata when possible + +### Scope + +**Included**: +- GitHub REST API v3 interface +- `torqlab` organization +- `torqlab/torq` repository (primary) +- Issue and PR operations +- Search and filtering +- Error handling + +**Excluded**: +- Discussions API +- Projects API +- Wiki operations +- Release management +- Webhook management +- Organization settings +- Team management + +## Data Models + +### Issue +```json +{ + "number": 123, + "title": "Add GitHub integration", + "body": "Detailed description...", + "state": "open", + "labels": ["enhancement", "documentation"], + "assignee": "username", + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "comments_count": 5, + "url": "https://github.com/torqlab/torq/issues/123" +} +``` + +### Pull Request +```json +{ + "number": 456, + "title": "Implement GitHub MCP integration", + "body": "This PR adds MCP support...", + "state": "open", + "draft": false, + "head": { + "ref": "feature/github-mcp", + "sha": "abc123..." + }, + "base": { + "ref": "main", + "sha": "def456..." + }, + "labels": ["infrastructure"], + "assignee": "username", + "created_at": "2026-03-30T12:00:00Z", + "updated_at": "2026-03-30T14:00:00Z", + "merged_at": null, + "comments_count": 3, + "commits_count": 5, + "url": "https://github.com/torqlab/torq/pull/456" +} +``` + +### Error Response +```json +{ + "error": "Not Found", + "message": "Issue #999 not found", + "status": 404, + "documentation_url": "https://docs.github.com/rest/reference/issues" +} +``` + +## API Operations + +### Search Issues +``` +Operation: gh_search_issues(query, state, labels, assignee, sort, direction) +Input: Search query and optional filters +Output: List of issue objects +Errors: 401, 403, 422, 429 +Rate Limit: 60 requests/hour (search-specific) +``` + +### Get Issue +``` +Operation: gh_get_issue(repo, number) +Input: Repository name, issue number +Output: Single issue object with all details +Errors: 401, 403, 404, 422 +Rate Limit: 5000 requests/hour +``` + +### Create PR +``` +Operation: gh_create_pull_request(repo, title, body, head, base, draft) +Input: Repository, branch names, title, optional body and draft flag +Output: Created PR object with number and URL +Errors: 401, 403, 409, 422, 500 +Rate Limit: 5000 requests/hour +Side Effects: Creates branch if not exists, creates PR in GitHub +``` + +### Update PR +``` +Operation: gh_update_pull_request(repo, number, state, body) +Input: Repository, PR number, new state, optional body +Output: Updated PR object +Errors: 401, 403, 404, 409, 422 +Rate Limit: 5000 requests/hour +Side Effects: Updates PR state in GitHub +``` + +### List PRs +``` +Operation: gh_list_pull_requests(repo, state, base, head, sort, per_page) +Input: Repository and optional filters +Output: List of PR objects with pagination +Errors: 401, 403, 422 +Rate Limit: 5000 requests/hour +``` + +### Get PR Details +``` +Operation: gh_get_pull_request(repo, number) +Input: Repository name, PR number +Output: Single PR object with all details +Errors: 401, 403, 404 +Rate Limit: 5000 requests/hour +``` + +## Success Criteria + +✅ All six functional requirements implemented +✅ Error handling for all documented error cases +✅ Rate limiting respected in implementation +✅ Security requirements met (no token exposure) +✅ Performance targets achieved (< 3 seconds per operation) +✅ Integration tests passing with fixtures +✅ Documentation complete with examples + +## Testing Strategy + +See `specs/fixture-system/spec.md` for test fixture requirements. + +### Manual Testing +1. Valid PAT setup verification +2. Search issue operations +3. Create PR on test branch +4. Update PR state +5. Error case handling + +### Automated Testing +1. Fixture-based testing (no API calls) +2. Schema validation +3. Error response handling +4. Rate limit simulation + +## Security Checklist + +- [x] PAT has minimal required permissions only +- [x] Token never stored in version-controlled files +- [x] Error messages don't expose sensitive data +- [x] Input validation on all parameters +- [x] Output sanitization for display +- [x] Rate limiting prevents abuse +- [x] Audit trail via GitHub API logs diff --git a/openspec/changes/add-github-mcp-integration/tasks.md b/openspec/changes/add-github-mcp-integration/tasks.md new file mode 100644 index 0000000..cd133dc --- /dev/null +++ b/openspec/changes/add-github-mcp-integration/tasks.md @@ -0,0 +1,168 @@ +# Implementation Tasks - GitHub MCP Integration + +## Phase 1: Specification ✅ +- [x] Create proposal.md +- [x] Create design.md +- [x] Create this tasks.md +- [x] Draft GitHub Integration spec +- [x] Draft Fixture System spec + +## Phase 2: Configure GitHub MCP + +### 2.1 Create `.mcp.json` +- [ ] Create `.mcp.json` at project root +- [ ] Configure HTTP server type +- [ ] Set GitHub MCP URL: `https://github-mcp.anthropic.com` +- [ ] Configure GITHUB_TOKEN environment variable +- [ ] Validate JSON syntax + +### 2.2 Update `.env` +- [ ] Add GITHUB_MCP_TOKEN placeholder to `.env` +- [ ] Document required PAT permissions +- [ ] Add comment with GitHub PAT creation link + +### 2.3 Verify `.gitignore` +- [ ] Confirm `.env` is in `.gitignore` +- [ ] Confirm `.env.local` is in `.gitignore` +- [ ] Confirm `.env.*.local` is in `.gitignore` + +## Phase 3: Create Test Fixtures + +### 3.1 Directory Structure +- [ ] Create `.claude/test-fixtures/github-mcp/` +- [ ] Create `.claude/test-fixtures/github-mcp/issues/` +- [ ] Create `.claude/test-fixtures/github-mcp/pull-requests/` +- [ ] Create `.claude/test-fixtures/github-mcp/repositories/` + +### 3.2 Issue Fixtures +- [ ] Create `issues/search-issues.json` +- [ ] Create `issues/search-issues-empty.json` +- [ ] Create `issues/get-issue.json` +- [ ] Create `issues/get-issue-not-found.json` +- [ ] Validate all JSON files + +### 3.3 Pull Request Fixtures +- [ ] Create `pull-requests/create-pr-success.json` +- [ ] Create `pull-requests/create-pr-conflict.json` +- [ ] Create `pull-requests/update-pr-success.json` +- [ ] Create `pull-requests/list-prs.json` +- [ ] Create `pull-requests/get-pr.json` +- [ ] Create `pull-requests/get-pr-not-found.json` +- [ ] Validate all JSON files + +### 3.4 Repository Fixtures +- [ ] Create `repositories/search-repos.json` +- [ ] Create `repositories/get-repo.json` +- [ ] Validate all JSON files + +### 3.5 Error Fixtures +- [ ] Create `error-rate-limited.json` +- [ ] Create `error-unauthorized.json` +- [ ] Create `error-forbidden.json` +- [ ] Validate all JSON files + +## Phase 4: Documentation + +### 4.1 Setup Guide +- [ ] Create `.claude/GITHUB_MCP_SETUP.md` +- [ ] Document prerequisites +- [ ] Write step-by-step PAT creation guide +- [ ] Document environment setup +- [ ] Provide tool reference + +### 4.2 Tool Reference +- [ ] Document all available tools +- [ ] Provide usage examples for each +- [ ] Include example workflows + +### 4.3 Error Handling +- [ ] Document common errors +- [ ] Provide troubleshooting steps +- [ ] Document rate limits (60/hour search, 5000/hour API) +- [ ] Explain error response format + +### 4.4 Best Practices +- [ ] Document token rotation (90-day policy) +- [ ] Explain security considerations +- [ ] Provide commit message examples +- [ ] Document testing patterns + +## Phase 5: Validation & Testing + +### 5.1 Configuration Validation +- [ ] Parse `.mcp.json` as valid JSON +- [ ] Verify environment variable format +- [ ] Check GitHub URL is correct +- [ ] Validate server type is "http" + +### 5.2 Fixture Validation +- [ ] Validate all fixture JSON files parse correctly +- [ ] Verify field names match GitHub API schema +- [ ] Check error responses have correct structure +- [ ] Verify HTTP status codes in responses + +### 5.3 Integration Testing (Manual) +- [ ] Set valid GITHUB_MCP_TOKEN locally +- [ ] Run `claude --mcp` to list tools +- [ ] Attempt mock issue search +- [ ] Attempt mock PR creation +- [ ] Verify tools are recognized + +### 5.4 Documentation Testing +- [ ] Follow setup guide end-to-end +- [ ] Verify all links work +- [ ] Test all code examples +- [ ] Verify tool names match available tools + +## Phase 6: Finalize & Archive + +### 6.1 Git Operations +- [ ] Stage all new files and modifications +- [ ] Create git commit with descriptive message +- [ ] Push to feature branch + +### 6.2 OpenSpec Archive +- [ ] Mark all tasks as complete +- [ ] Run `openspec archive add-github-mcp-integration --yes` +- [ ] Verify archive in git history + +### 6.3 Pull Request +- [ ] Create PR to `main` branch +- [ ] Write PR description with changes summary +- [ ] Add success criteria checklist +- [ ] Request review from team + +### 6.4 Merge +- [ ] Address review feedback +- [ ] Merge PR to `main` +- [ ] Delete feature branch +- [ ] Notify team of new capability + +## Blocking Dependencies + +- Phase 2 depends on Phase 1 ✓ +- Phase 3 depends on Phase 2 +- Phase 4 depends on Phase 3 +- Phase 5 depends on Phase 4 +- Phase 6 depends on Phase 5 + +## Estimated Time + +| Phase | Tasks | Time | +|-------|-------|------| +| 1 | Spec | 30 min ✓ | +| 2 | Config | 15 min | +| 3 | Fixtures | 45 min | +| 4 | Docs | 30 min | +| 5 | Validation | 15 min | +| 6 | Archive | 15 min | +| **Total** | **19 tasks** | **~2.5 hrs** | + +## Progress Tracking + +- [ ] Phase 1: Specification (0% → 100%) +- [ ] Phase 2: Configuration (0%) +- [ ] Phase 3: Fixtures (0%) +- [ ] Phase 4: Documentation (0%) +- [ ] Phase 5: Validation (0%) +- [ ] Phase 6: Finalize (0%) From f43f5b36f32ccb1ef31375e20821c949f8f5824c Mon Sep 17 00:00:00 2001 From: balovbohdan Date: Tue, 31 Mar 2026 11:41:28 +0300 Subject: [PATCH 2/2] plan: Add implementation plan for issue #94 Planning details: - Requirements: Add Strava footer button to link to https://www.strava.com/clubs/torqlab - Approach: Add button to existing footer component with security attributes - Key changes: Footer component (minimal change) Co-Authored-By: Claude Haiku 4.5 --- openspec/changes/94/proposal.md | 17 +++++++++++++++ openspec/changes/94/specs/footer/spec.md | 23 ++++++++++++++++++++ openspec/changes/94/tasks.md | 27 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 openspec/changes/94/proposal.md create mode 100644 openspec/changes/94/specs/footer/spec.md create mode 100644 openspec/changes/94/tasks.md diff --git a/openspec/changes/94/proposal.md b/openspec/changes/94/proposal.md new file mode 100644 index 0000000..ed65078 --- /dev/null +++ b/openspec/changes/94/proposal.md @@ -0,0 +1,17 @@ +# Change: Add Strava Footer Button + +## Why +The Strava club is an important community hub for TORQ users. Adding a footer button makes it easily discoverable and encourages community engagement. + +## What Changes +- Add new Strava button to the website footer +- Use minimalistic Strava icon in a square button matching existing footer icon button styles +- Button links to https://www.strava.com/clubs/torqlab and opens in new tab +- Include security attributes: `noopener` and `noreferrer` +- Check existing icon library for Strava icon; use provided SVG if not available + +## Impact +- Affected component: Footer component only +- Change type: Added +- No breaking changes +- Minimal scope: single button addition diff --git a/openspec/changes/94/specs/footer/spec.md b/openspec/changes/94/specs/footer/spec.md new file mode 100644 index 0000000..9816ea5 --- /dev/null +++ b/openspec/changes/94/specs/footer/spec.md @@ -0,0 +1,23 @@ +# Footer Social Button Specification + +## ADDED Requirements + +### Requirement: Strava Club Link Button +Adds a clickable footer button that links to the TORQ Strava club with consistent styling and security considerations. + +#### Scenario: User clicks Strava button +- **WHEN** user clicks the Strava icon button in the footer +- **THEN** https://www.strava.com/clubs/torqlab opens in a new browser tab +- **AND** the link includes `noopener` and `noreferrer` attributes for security +- **AND** the original page remains visible in the current tab + +#### Scenario: Button visual consistency +- **WHEN** the page renders the footer +- **THEN** the Strava button appears with the same square icon button styling as other footer social icons +- **AND** the Strava icon is minimalistic and clearly recognizable +- **AND** the button hover state matches existing footer icon buttons + +#### Scenario: Icon availability +- **WHEN** the icon library contains a Strava icon +- **THEN** use the existing icon from the library +- **OTHERWISE** use the provided SVG from the issue description diff --git a/openspec/changes/94/tasks.md b/openspec/changes/94/tasks.md new file mode 100644 index 0000000..2106d23 --- /dev/null +++ b/openspec/changes/94/tasks.md @@ -0,0 +1,27 @@ +# Implementation Checklist + +## Planning +- [x] Reviewed requirements +- [x] Confirmed approach +- [x] Identified key changes + +## Implementation +- [ ] 1.1 Locate and open footer component +- [ ] 1.2 Check existing icon library for Strava icon +- [ ] 1.3 Add Strava icon import (library or SVG file) +- [ ] 1.4 Create Strava button element with link to https://www.strava.com/clubs/torqlab +- [ ] 1.5 Add `noopener` and `noreferrer` attributes to anchor tag +- [ ] 1.6 Set button target="_blank" to open in new tab +- [ ] 1.7 Apply consistent styling with existing footer icon buttons + +## Testing & Verification +- [ ] 2.1 Verify button renders correctly in footer +- [ ] 2.2 Verify button appears with proper icon and styling +- [ ] 2.3 Test link opens in new tab +- [ ] 2.4 Verify security attributes are present in HTML +- [ ] 2.5 Test responsive behavior on mobile/tablet views + +## Documentation & Cleanup +- [ ] 3.1 Run lint and format checks (`bun run lint`) +- [ ] 3.2 Test suite passes (`bun run test`) +- [ ] 3.3 Review for any unused imports or code