github-pr-context-mcp 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- github_pr_context_mcp-0.2.5/LICENSE +21 -0
- github_pr_context_mcp-0.2.5/PKG-INFO +192 -0
- github_pr_context_mcp-0.2.5/README.md +173 -0
- github_pr_context_mcp-0.2.5/analytics/__init__.py +3 -0
- github_pr_context_mcp-0.2.5/analytics/usage_metrics.py +185 -0
- github_pr_context_mcp-0.2.5/app/__init__.py +3 -0
- github_pr_context_mcp-0.2.5/app/mcp_app.py +928 -0
- github_pr_context_mcp-0.2.5/auth/__init__.py +3 -0
- github_pr_context_mcp-0.2.5/auth/gmail_identity.py +236 -0
- github_pr_context_mcp-0.2.5/entrypoints/deployed/server.py +34 -0
- github_pr_context_mcp-0.2.5/entrypoints/local/server.py +273 -0
- github_pr_context_mcp-0.2.5/fetcher/__init__.py +3 -0
- github_pr_context_mcp-0.2.5/fetcher/client.py +131 -0
- github_pr_context_mcp-0.2.5/fetcher/queries.py +67 -0
- github_pr_context_mcp-0.2.5/fetcher/transform.py +55 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/PKG-INFO +192 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/SOURCES.txt +30 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/dependency_links.txt +1 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/entry_points.txt +2 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/requires.txt +9 -0
- github_pr_context_mcp-0.2.5/github_pr_context_mcp.egg-info/top_level.txt +7 -0
- github_pr_context_mcp-0.2.5/inference/__init__.py +3 -0
- github_pr_context_mcp-0.2.5/inference/providers.py +296 -0
- github_pr_context_mcp-0.2.5/inference/review.py +175 -0
- github_pr_context_mcp-0.2.5/pyproject.toml +41 -0
- github_pr_context_mcp-0.2.5/setup.cfg +4 -0
- github_pr_context_mcp-0.2.5/storage/__init__.py +19 -0
- github_pr_context_mcp-0.2.5/storage/document_builder.py +74 -0
- github_pr_context_mcp-0.2.5/storage/encoder.py +35 -0
- github_pr_context_mcp-0.2.5/storage/vector_store.py +270 -0
- github_pr_context_mcp-0.2.5/tests/test_fixes.py +80 -0
- github_pr_context_mcp-0.2.5/tests/test_sqlite_auth.py +47 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paarth Gala
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: github-pr-context-mcp
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: GitHub PR Review Context MCP Server
|
|
5
|
+
Author: Paarth Gala
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: mcp
|
|
10
|
+
Requires-Dist: chromadb
|
|
11
|
+
Requires-Dist: sentence-transformers
|
|
12
|
+
Requires-Dist: python-dotenv
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: cerebras-cloud-sdk
|
|
15
|
+
Requires-Dist: openai
|
|
16
|
+
Requires-Dist: anthropic
|
|
17
|
+
Requires-Dist: google-generativeai
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# GitHub PR Review Context MCP
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+

|
|
26
|
+

|
|
27
|
+

|
|
28
|
+

|
|
29
|
+
[](LICENSE)
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
**Production-grade context layer for AI code review, grounded in your repository's real pull request history.**
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
> Tracking unique users across **uvx**, **pipx**, and **local** sources. (Render hosting upcoming)
|
|
36
|
+
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
GitHub PR Review Context MCP gives AI assistants institutional review memory.
|
|
44
|
+
|
|
45
|
+
Instead of generic feedback, reviews are informed by historical reviewer comments, recurring quality patterns, and repository-specific standards from your own PR history.
|
|
46
|
+
|
|
47
|
+
### Core Value
|
|
48
|
+
|
|
49
|
+
- Improves review consistency across teams and repositories.
|
|
50
|
+
- Reduces repeated reviewer feedback on known issues.
|
|
51
|
+
- Integrates with any MCP-compatible client and multiple LLM providers.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🛠️ Usage Modes: Solo vs. Team
|
|
56
|
+
|
|
57
|
+
This MCP server is built to scale from a single machine to an entire engineering organization.
|
|
58
|
+
|
|
59
|
+
### 👤 Solo Developer (Local Mode)
|
|
60
|
+
**Best for:** Privacy, local-first control, and zero hosting costs.
|
|
61
|
+
- **How it works:** Run via `uvx`, `pipx`, or a local git clone.
|
|
62
|
+
- **Storage:** ChromaDB stays on your local machine.
|
|
63
|
+
- **Security:** Your GitHub Token and LLM keys never leave your device.
|
|
64
|
+
- **Setup:** See [Quick Start](docs/quickstart.md#🚀-zero-setup-uvx--pipx--npx).
|
|
65
|
+
|
|
66
|
+
### 🤝 Team Collaboration (Hosted Mode - UPCOMING)
|
|
67
|
+
**Best for:** Scaling team-wide PR standards and centralized infra.
|
|
68
|
+
- **How it works:** One deployment on Render (Coming Soon) shared by the whole team.
|
|
69
|
+
- **Isolation:** Strict **Gmail-based namespace isolation** (driven by SQLite). User A's indexed data is mathematically invisible to User B.
|
|
70
|
+
- **Economics:** Pooled LLM credits and a single shared indexing server.
|
|
71
|
+
- **Setup:** See [Deployment Guide](docs/integrations/deployed.md).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### 🌟 Zero-Friction Setup (Upcoming)
|
|
76
|
+
If your team has Hosted this MCP on Render, you do **NOT** need to `git clone` or install anything. You just drop a snippet into your IDE:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
"github-pr-context": {
|
|
80
|
+
"type": "sse",
|
|
81
|
+
"url": "https://YOUR-RENDER-URL.onrender.com/mcp",
|
|
82
|
+
"headers": {
|
|
83
|
+
"Authorization": "Bearer YOUR_TOKEN"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
*That's it.* If your IDE supports native MCP SSE connections, you are immediately connected to the secure Render deployment. No setup friction, no tools required.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Key Capabilities
|
|
92
|
+
|
|
93
|
+
| Capability | What It Delivers |
|
|
94
|
+
|---|---|
|
|
95
|
+
| Historical review retrieval | Semantic search across prior PR comments and review summaries |
|
|
96
|
+
| Context-aware AI review | Feedback grounded in repository-specific review behavior |
|
|
97
|
+
| Grounded code generation | Generate new code based on past commits, comments, and style |
|
|
98
|
+
| **Team rules generation** | **Auto-generate .cursorrules / CLAUDE.md from repo history** |
|
|
99
|
+
| Smart repository readiness | Auto-detect indexed state and index on demand |
|
|
100
|
+
| Flexible storage modes | Permanent (disk) and temporary (in-memory) indexing options |
|
|
101
|
+
| Portable inference layer | Switch LLM providers using environment configuration only |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Demo
|
|
106
|
+
|
|
107
|
+

|
|
108
|
+
|
|
109
|
+
Example workflow:
|
|
110
|
+
- Ask the assistant to review a diff using repository history.
|
|
111
|
+
- The server retrieves similar past review context.
|
|
112
|
+
- The model returns grounded feedback aligned to team expectations.
|
|
113
|
+
|
|
114
|
+
## Usage Analytics
|
|
115
|
+
|
|
116
|
+
To help us understand adoption, the MCP server collects privacy-first, anonymous telemetry on deployments. Future hosted deployments will expose HTTP endpoints (`/stats` and `/ping`) that publicly display the **number of unique users**.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🧰 Core Tools Reference
|
|
121
|
+
|
|
122
|
+
The server exposes 12 core tools for IDE agents and developers. For a deep dive on when to use each, see the [**Tool Strategy Guide**](docs/tools_strategy.md).
|
|
123
|
+
|
|
124
|
+
| Tool | Action |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `ensure_repo_ready` | Index a repo and ensure it's ready for queries |
|
|
127
|
+
| `generate_repo_rules` | **Synthesize .cursorrules / CLAUDE.md from PR history** |
|
|
128
|
+
| `generate_code_from_history`| Write code grounded in past commits & team style |
|
|
129
|
+
| `review_code_with_history` | Perform AI review grounded in team review memory |
|
|
130
|
+
| `get_team_review_patterns` | Summarize recurring team standards (e.g. "no magic numbers") |
|
|
131
|
+
| `semantic_search_reviews` | Search past PR comments by meaning, not just keywords |
|
|
132
|
+
| `set_active_repo` | Switch between multiple indexed repositories |
|
|
133
|
+
| `list_indexed_repos` | View all repos currently in local/temporary storage |
|
|
134
|
+
| `delete_repo_index` | Free up disk space by clearing repository indices |
|
|
135
|
+
| `get_index_stats` | Verify if a repo index is complete (doc count) |
|
|
136
|
+
| `update_settings` | Update tokens/LLM keys (Hosted mode only) |
|
|
137
|
+
| `get_usage_stats` | View adoption metrics and unique user counts |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
Detailed guides are split into focused pages:
|
|
144
|
+
|
|
145
|
+
- [Quick Start and Usage](docs/quickstart.md)
|
|
146
|
+
- [LLM Configuration](docs/llm-configuration.md)
|
|
147
|
+
- [Integrations](docs/integrations/index.md)
|
|
148
|
+
- [Architecture and Tools](docs/architecture.md)
|
|
149
|
+
- [Pipeline Deep Dive](docs/pipeline.md)
|
|
150
|
+
- [Configuration Guide (Change Tokens/Settings)](docs/guides/configuration.md)
|
|
151
|
+
- [Roadmap](docs/roadmap.md)
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Quick Links
|
|
156
|
+
|
|
157
|
+
- Access setup: [GitHub Token Guide](docs/GUIDE_GITHUB_TOKEN.md)
|
|
158
|
+
- Client connection: [Integrations](docs/integrations/index.md)
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 📣 Community & Feedback
|
|
163
|
+
|
|
164
|
+
We want to hear from you—whether you are a solo developer or a team at a large company!
|
|
165
|
+
|
|
166
|
+
### 👤 For Individuals
|
|
167
|
+
- **Feedback**: Please open an issue or start a discussion if you have ideas or encounter bugs.
|
|
168
|
+
- **Show your support**: If this tool saves you time, give it a **Star ⭐**! It helps others find the project.
|
|
169
|
+
|
|
170
|
+
### 🏢 For Corporate & Teams
|
|
171
|
+
- **Usage**: Is your team using this MCP server? Join our "Adopters" list by opening a PR to add your team's name.
|
|
172
|
+
- **Corporate Feedback**: Open an issue with the `corporate-usage` label to tell us how this has improved your PR review workflow.
|
|
173
|
+
- **Custom Integration**: Need help deploying this to your private cloud? Reach out via GitHub Discussions.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 📜 Documentation & Guides
|
|
178
|
+
|
|
179
|
+
- **Strategy & Best Practices**: [Tool Strategy & Selection Guide](docs/tools_strategy.md)
|
|
180
|
+
- **Architecture**: [Architecture and Tools](docs/architecture.md)
|
|
181
|
+
- **Pipeline**: [Pipeline Deep Dive](docs/pipeline.md)
|
|
182
|
+
- **Usage**: [Quick Start and Usage](docs/quickstart.md)
|
|
183
|
+
|
|
184
|
+
## 🛠️ Troubleshooting
|
|
185
|
+
|
|
186
|
+
- **"command not found"**: Use absolute paths in your configuration. Run `github-pr-context-mcp config` to get your exact path.
|
|
187
|
+
- **"PermissionError: [WinError 32]"**: The binary is locked by a running process. Close Claude/Cursor, run `taskkill /F /IM github-pr-context-mcp.exe`, then retry the upgrade.
|
|
188
|
+
- **Rate Limit Errors**: Ensure your `GITHUB_TOKEN` is valid and has `repo` scope.
|
|
189
|
+
|
|
190
|
+
## ⚖️ License
|
|
191
|
+
|
|
192
|
+
MIT
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# GitHub PR Review Context MCP
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
[](LICENSE)
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
**Production-grade context layer for AI code review, grounded in your repository's real pull request history.**
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
> Tracking unique users across **uvx**, **pipx**, and **local** sources. (Render hosting upcoming)
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
GitHub PR Review Context MCP gives AI assistants institutional review memory.
|
|
25
|
+
|
|
26
|
+
Instead of generic feedback, reviews are informed by historical reviewer comments, recurring quality patterns, and repository-specific standards from your own PR history.
|
|
27
|
+
|
|
28
|
+
### Core Value
|
|
29
|
+
|
|
30
|
+
- Improves review consistency across teams and repositories.
|
|
31
|
+
- Reduces repeated reviewer feedback on known issues.
|
|
32
|
+
- Integrates with any MCP-compatible client and multiple LLM providers.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 🛠️ Usage Modes: Solo vs. Team
|
|
37
|
+
|
|
38
|
+
This MCP server is built to scale from a single machine to an entire engineering organization.
|
|
39
|
+
|
|
40
|
+
### 👤 Solo Developer (Local Mode)
|
|
41
|
+
**Best for:** Privacy, local-first control, and zero hosting costs.
|
|
42
|
+
- **How it works:** Run via `uvx`, `pipx`, or a local git clone.
|
|
43
|
+
- **Storage:** ChromaDB stays on your local machine.
|
|
44
|
+
- **Security:** Your GitHub Token and LLM keys never leave your device.
|
|
45
|
+
- **Setup:** See [Quick Start](docs/quickstart.md#🚀-zero-setup-uvx--pipx--npx).
|
|
46
|
+
|
|
47
|
+
### 🤝 Team Collaboration (Hosted Mode - UPCOMING)
|
|
48
|
+
**Best for:** Scaling team-wide PR standards and centralized infra.
|
|
49
|
+
- **How it works:** One deployment on Render (Coming Soon) shared by the whole team.
|
|
50
|
+
- **Isolation:** Strict **Gmail-based namespace isolation** (driven by SQLite). User A's indexed data is mathematically invisible to User B.
|
|
51
|
+
- **Economics:** Pooled LLM credits and a single shared indexing server.
|
|
52
|
+
- **Setup:** See [Deployment Guide](docs/integrations/deployed.md).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### 🌟 Zero-Friction Setup (Upcoming)
|
|
57
|
+
If your team has Hosted this MCP on Render, you do **NOT** need to `git clone` or install anything. You just drop a snippet into your IDE:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
"github-pr-context": {
|
|
61
|
+
"type": "sse",
|
|
62
|
+
"url": "https://YOUR-RENDER-URL.onrender.com/mcp",
|
|
63
|
+
"headers": {
|
|
64
|
+
"Authorization": "Bearer YOUR_TOKEN"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
*That's it.* If your IDE supports native MCP SSE connections, you are immediately connected to the secure Render deployment. No setup friction, no tools required.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Key Capabilities
|
|
73
|
+
|
|
74
|
+
| Capability | What It Delivers |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Historical review retrieval | Semantic search across prior PR comments and review summaries |
|
|
77
|
+
| Context-aware AI review | Feedback grounded in repository-specific review behavior |
|
|
78
|
+
| Grounded code generation | Generate new code based on past commits, comments, and style |
|
|
79
|
+
| **Team rules generation** | **Auto-generate .cursorrules / CLAUDE.md from repo history** |
|
|
80
|
+
| Smart repository readiness | Auto-detect indexed state and index on demand |
|
|
81
|
+
| Flexible storage modes | Permanent (disk) and temporary (in-memory) indexing options |
|
|
82
|
+
| Portable inference layer | Switch LLM providers using environment configuration only |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Demo
|
|
87
|
+
|
|
88
|
+

|
|
89
|
+
|
|
90
|
+
Example workflow:
|
|
91
|
+
- Ask the assistant to review a diff using repository history.
|
|
92
|
+
- The server retrieves similar past review context.
|
|
93
|
+
- The model returns grounded feedback aligned to team expectations.
|
|
94
|
+
|
|
95
|
+
## Usage Analytics
|
|
96
|
+
|
|
97
|
+
To help us understand adoption, the MCP server collects privacy-first, anonymous telemetry on deployments. Future hosted deployments will expose HTTP endpoints (`/stats` and `/ping`) that publicly display the **number of unique users**.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🧰 Core Tools Reference
|
|
102
|
+
|
|
103
|
+
The server exposes 12 core tools for IDE agents and developers. For a deep dive on when to use each, see the [**Tool Strategy Guide**](docs/tools_strategy.md).
|
|
104
|
+
|
|
105
|
+
| Tool | Action |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `ensure_repo_ready` | Index a repo and ensure it's ready for queries |
|
|
108
|
+
| `generate_repo_rules` | **Synthesize .cursorrules / CLAUDE.md from PR history** |
|
|
109
|
+
| `generate_code_from_history`| Write code grounded in past commits & team style |
|
|
110
|
+
| `review_code_with_history` | Perform AI review grounded in team review memory |
|
|
111
|
+
| `get_team_review_patterns` | Summarize recurring team standards (e.g. "no magic numbers") |
|
|
112
|
+
| `semantic_search_reviews` | Search past PR comments by meaning, not just keywords |
|
|
113
|
+
| `set_active_repo` | Switch between multiple indexed repositories |
|
|
114
|
+
| `list_indexed_repos` | View all repos currently in local/temporary storage |
|
|
115
|
+
| `delete_repo_index` | Free up disk space by clearing repository indices |
|
|
116
|
+
| `get_index_stats` | Verify if a repo index is complete (doc count) |
|
|
117
|
+
| `update_settings` | Update tokens/LLM keys (Hosted mode only) |
|
|
118
|
+
| `get_usage_stats` | View adoption metrics and unique user counts |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
Detailed guides are split into focused pages:
|
|
125
|
+
|
|
126
|
+
- [Quick Start and Usage](docs/quickstart.md)
|
|
127
|
+
- [LLM Configuration](docs/llm-configuration.md)
|
|
128
|
+
- [Integrations](docs/integrations/index.md)
|
|
129
|
+
- [Architecture and Tools](docs/architecture.md)
|
|
130
|
+
- [Pipeline Deep Dive](docs/pipeline.md)
|
|
131
|
+
- [Configuration Guide (Change Tokens/Settings)](docs/guides/configuration.md)
|
|
132
|
+
- [Roadmap](docs/roadmap.md)
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Quick Links
|
|
137
|
+
|
|
138
|
+
- Access setup: [GitHub Token Guide](docs/GUIDE_GITHUB_TOKEN.md)
|
|
139
|
+
- Client connection: [Integrations](docs/integrations/index.md)
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 📣 Community & Feedback
|
|
144
|
+
|
|
145
|
+
We want to hear from you—whether you are a solo developer or a team at a large company!
|
|
146
|
+
|
|
147
|
+
### 👤 For Individuals
|
|
148
|
+
- **Feedback**: Please open an issue or start a discussion if you have ideas or encounter bugs.
|
|
149
|
+
- **Show your support**: If this tool saves you time, give it a **Star ⭐**! It helps others find the project.
|
|
150
|
+
|
|
151
|
+
### 🏢 For Corporate & Teams
|
|
152
|
+
- **Usage**: Is your team using this MCP server? Join our "Adopters" list by opening a PR to add your team's name.
|
|
153
|
+
- **Corporate Feedback**: Open an issue with the `corporate-usage` label to tell us how this has improved your PR review workflow.
|
|
154
|
+
- **Custom Integration**: Need help deploying this to your private cloud? Reach out via GitHub Discussions.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 📜 Documentation & Guides
|
|
159
|
+
|
|
160
|
+
- **Strategy & Best Practices**: [Tool Strategy & Selection Guide](docs/tools_strategy.md)
|
|
161
|
+
- **Architecture**: [Architecture and Tools](docs/architecture.md)
|
|
162
|
+
- **Pipeline**: [Pipeline Deep Dive](docs/pipeline.md)
|
|
163
|
+
- **Usage**: [Quick Start and Usage](docs/quickstart.md)
|
|
164
|
+
|
|
165
|
+
## 🛠️ Troubleshooting
|
|
166
|
+
|
|
167
|
+
- **"command not found"**: Use absolute paths in your configuration. Run `github-pr-context-mcp config` to get your exact path.
|
|
168
|
+
- **"PermissionError: [WinError 32]"**: The binary is locked by a running process. Close Claude/Cursor, run `taskkill /F /IM github-pr-context-mcp.exe`, then retry the upgrade.
|
|
169
|
+
- **Rate Limit Errors**: Ensure your `GITHUB_TOKEN` is valid and has `repo` scope.
|
|
170
|
+
|
|
171
|
+
## ⚖️ License
|
|
172
|
+
|
|
173
|
+
MIT
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import sqlite3
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
def _utc_now() -> datetime:
|
|
10
|
+
return datetime.now(timezone.utc)
|
|
11
|
+
|
|
12
|
+
class UsageMetricsStore:
|
|
13
|
+
"""Persist and summarize anonymous usage metrics for hosted MCP deployments backed by thread-safe SQLite."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, file_path: str):
|
|
16
|
+
# Swap existing json suffixes to .db without breaking integrations
|
|
17
|
+
p = Path(file_path)
|
|
18
|
+
if p.suffix == '.json':
|
|
19
|
+
self._path = p.with_suffix('.db')
|
|
20
|
+
else:
|
|
21
|
+
self._path = p
|
|
22
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
self._init_db()
|
|
24
|
+
|
|
25
|
+
def _get_conn(self) -> sqlite3.Connection:
|
|
26
|
+
# isolation_level=None enables autocommit for simple operations
|
|
27
|
+
# check_same_thread=False allows sharing across async workers
|
|
28
|
+
return sqlite3.connect(str(self._path), isolation_level=None, check_same_thread=False)
|
|
29
|
+
|
|
30
|
+
def _init_db(self):
|
|
31
|
+
with self._get_conn() as conn:
|
|
32
|
+
conn.execute('''
|
|
33
|
+
CREATE TABLE IF NOT EXISTS global_stats (
|
|
34
|
+
key TEXT PRIMARY KEY,
|
|
35
|
+
value INTEGER DEFAULT 0
|
|
36
|
+
)
|
|
37
|
+
''')
|
|
38
|
+
conn.execute('''
|
|
39
|
+
CREATE TABLE IF NOT EXISTS system_config (
|
|
40
|
+
key TEXT PRIMARY KEY,
|
|
41
|
+
value TEXT
|
|
42
|
+
)
|
|
43
|
+
''')
|
|
44
|
+
conn.execute("INSERT OR IGNORE INTO system_config (key, value) VALUES ('tracked_since', ?)", (_utc_now().isoformat(),))
|
|
45
|
+
|
|
46
|
+
conn.execute('''
|
|
47
|
+
CREATE TABLE IF NOT EXISTS tools (
|
|
48
|
+
name TEXT PRIMARY KEY,
|
|
49
|
+
calls INTEGER DEFAULT 0
|
|
50
|
+
)
|
|
51
|
+
''')
|
|
52
|
+
conn.execute('''
|
|
53
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
54
|
+
user_hash TEXT PRIMARY KEY,
|
|
55
|
+
first_seen TEXT,
|
|
56
|
+
last_seen TEXT,
|
|
57
|
+
events INTEGER DEFAULT 0
|
|
58
|
+
)
|
|
59
|
+
''')
|
|
60
|
+
conn.execute('''
|
|
61
|
+
CREATE TABLE IF NOT EXISTS daily_tool_calls (
|
|
62
|
+
day TEXT PRIMARY KEY,
|
|
63
|
+
calls INTEGER DEFAULT 0
|
|
64
|
+
)
|
|
65
|
+
''')
|
|
66
|
+
conn.execute('''
|
|
67
|
+
CREATE TABLE IF NOT EXISTS daily_users (
|
|
68
|
+
day TEXT,
|
|
69
|
+
user_hash TEXT,
|
|
70
|
+
calls INTEGER DEFAULT 0,
|
|
71
|
+
PRIMARY KEY (day, user_hash)
|
|
72
|
+
)
|
|
73
|
+
''')
|
|
74
|
+
conn.execute('''
|
|
75
|
+
CREATE TABLE IF NOT EXISTS pings (
|
|
76
|
+
mode TEXT,
|
|
77
|
+
user_hash TEXT,
|
|
78
|
+
first_seen TEXT,
|
|
79
|
+
last_seen TEXT,
|
|
80
|
+
count INTEGER DEFAULT 0,
|
|
81
|
+
PRIMARY KEY (mode, user_hash)
|
|
82
|
+
)
|
|
83
|
+
''')
|
|
84
|
+
conn.execute('''
|
|
85
|
+
CREATE TABLE IF NOT EXISTS github_daily_clones (
|
|
86
|
+
timestamp TEXT PRIMARY KEY,
|
|
87
|
+
count INTEGER DEFAULT 0,
|
|
88
|
+
uniques INTEGER DEFAULT 0
|
|
89
|
+
)
|
|
90
|
+
''')
|
|
91
|
+
|
|
92
|
+
def _hash_user(self, user_id: str) -> str:
|
|
93
|
+
return hashlib.sha256(user_id.encode("utf-8")).hexdigest()[:16]
|
|
94
|
+
|
|
95
|
+
def record_event(self, user_id: str, tool_name: str) -> None:
|
|
96
|
+
user_hash = self._hash_user(user_id)
|
|
97
|
+
now_str = _utc_now().isoformat()
|
|
98
|
+
day = _utc_now().date().isoformat()
|
|
99
|
+
|
|
100
|
+
with self._get_conn() as conn:
|
|
101
|
+
conn.execute("INSERT INTO global_stats (key, value) VALUES ('total_tool_calls', 1) ON CONFLICT(key) DO UPDATE SET value = value + 1")
|
|
102
|
+
conn.execute("INSERT INTO tools (name, calls) VALUES (?, 1) ON CONFLICT(name) DO UPDATE SET calls = calls + 1", (tool_name,))
|
|
103
|
+
conn.execute("INSERT INTO users (user_hash, first_seen, last_seen, events) VALUES (?, ?, ?, 1) ON CONFLICT(user_hash) DO UPDATE SET last_seen = ?, events = events + 1", (user_hash, now_str, now_str, now_str))
|
|
104
|
+
conn.execute("INSERT INTO daily_tool_calls (day, calls) VALUES (?, 1) ON CONFLICT(day) DO UPDATE SET calls = calls + 1", (day,))
|
|
105
|
+
conn.execute("INSERT INTO daily_users (day, user_hash, calls) VALUES (?, ?, 1) ON CONFLICT(day, user_hash) DO UPDATE SET calls = calls + 1", (day, user_hash))
|
|
106
|
+
|
|
107
|
+
def record_ping(self, anonymous_id: str, mode: str) -> None:
|
|
108
|
+
ALLOWED_MODES = {"render", "uvx", "pipx", "local", "unknown"}
|
|
109
|
+
safe_mode = mode if mode in ALLOWED_MODES else "unknown"
|
|
110
|
+
user_hash = self._hash_user(anonymous_id)
|
|
111
|
+
now_str = _utc_now().isoformat()
|
|
112
|
+
|
|
113
|
+
with self._get_conn() as conn:
|
|
114
|
+
conn.execute("INSERT INTO pings (mode, user_hash, first_seen, last_seen, count) VALUES (?, ?, ?, ?, 1) ON CONFLICT(mode, user_hash) DO UPDATE SET last_seen = ?, count = count + 1", (safe_mode, user_hash, now_str, now_str, now_str))
|
|
115
|
+
|
|
116
|
+
def update_github_clones(self, clones_data: list[dict]) -> None:
|
|
117
|
+
"""Update the persistent record of daily github clones fetched from the traffic API."""
|
|
118
|
+
with self._get_conn() as conn:
|
|
119
|
+
for day in clones_data:
|
|
120
|
+
ts = day.get("timestamp")
|
|
121
|
+
count = day.get("count", 0)
|
|
122
|
+
uniques = day.get("uniques", 0)
|
|
123
|
+
if ts:
|
|
124
|
+
conn.execute(
|
|
125
|
+
"INSERT INTO github_daily_clones (timestamp, count, uniques) VALUES (?, ?, ?) "
|
|
126
|
+
"ON CONFLICT(timestamp) DO UPDATE SET count = MAX(count, ?), uniques = MAX(uniques, ?)",
|
|
127
|
+
(ts, count, uniques, count, uniques)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def update_github_downloads(self, count: int) -> None:
|
|
131
|
+
"""Update the persistent record of GitHub release downloads."""
|
|
132
|
+
with self._get_conn() as conn:
|
|
133
|
+
conn.execute("INSERT INTO global_stats (key, value) VALUES ('github_downloads', ?) ON CONFLICT(key) DO UPDATE SET value = ?", (count, count))
|
|
134
|
+
|
|
135
|
+
def summary(self, last_days: int = 30) -> dict[str, Any]:
|
|
136
|
+
last_days = max(1, min(last_days, 365))
|
|
137
|
+
|
|
138
|
+
with self._get_conn() as conn:
|
|
139
|
+
tracked_since = conn.execute("SELECT value FROM system_config WHERE key = 'tracked_since'").fetchone()
|
|
140
|
+
tracked_since_val = tracked_since[0] if tracked_since else None
|
|
141
|
+
|
|
142
|
+
total_calls = conn.execute("SELECT value FROM global_stats WHERE key = 'total_tool_calls'").fetchone()
|
|
143
|
+
total_calls_val = total_calls[0] if total_calls else 0
|
|
144
|
+
|
|
145
|
+
tools_cursor = conn.execute("SELECT name, calls FROM tools ORDER BY calls DESC")
|
|
146
|
+
top_tools = [{"tool": row[0], "calls": row[1]} for row in tools_cursor]
|
|
147
|
+
|
|
148
|
+
daily_series = []
|
|
149
|
+
days_cursor = conn.execute("SELECT day, calls FROM daily_tool_calls ORDER BY day DESC LIMIT ?", (last_days,))
|
|
150
|
+
days_data = days_cursor.fetchall()
|
|
151
|
+
|
|
152
|
+
for day, calls in reversed(days_data):
|
|
153
|
+
users_count = conn.execute("SELECT COUNT(DISTINCT user_hash) FROM daily_users WHERE day = ?", (day,)).fetchone()[0]
|
|
154
|
+
daily_series.append({
|
|
155
|
+
"date": day,
|
|
156
|
+
"tool_calls": calls,
|
|
157
|
+
"unique_users": users_count
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
users_by_mode: dict[str, int] = {}
|
|
161
|
+
modes_cursor = conn.execute("SELECT mode, COUNT(DISTINCT user_hash) FROM pings GROUP BY mode")
|
|
162
|
+
for mode, count in modes_cursor:
|
|
163
|
+
users_by_mode[mode] = count
|
|
164
|
+
|
|
165
|
+
total_auth_users = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
|
|
166
|
+
if "render" not in users_by_mode or users_by_mode["render"] < total_auth_users:
|
|
167
|
+
users_by_mode["render"] = total_auth_users
|
|
168
|
+
|
|
169
|
+
github_clones = conn.execute("SELECT SUM(uniques) FROM github_daily_clones").fetchone()
|
|
170
|
+
github_clones_val = github_clones[0] if github_clones and github_clones[0] else 0
|
|
171
|
+
|
|
172
|
+
github_downloads = conn.execute("SELECT value FROM global_stats WHERE key = 'github_downloads'").fetchone()
|
|
173
|
+
github_downloads_val = github_downloads[0] if github_downloads else 0
|
|
174
|
+
|
|
175
|
+
total_ping_users = conn.execute("SELECT COUNT(DISTINCT user_hash) FROM pings").fetchone()[0]
|
|
176
|
+
total_unique = total_ping_users + total_auth_users + github_clones_val + github_downloads_val
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
"tracked_since": tracked_since_val,
|
|
180
|
+
"total_tool_calls": total_calls_val,
|
|
181
|
+
"total_unique_users": total_unique,
|
|
182
|
+
"users_by_mode": users_by_mode,
|
|
183
|
+
"top_tools": top_tools,
|
|
184
|
+
"daily": daily_series,
|
|
185
|
+
}
|