github-pro-mcp 0.0.3 → 0.0.4

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.
package/README.md CHANGED
@@ -7,6 +7,10 @@ GitHub Pro MCP gives Claude Code, Cursor, Copilot, opencode and any MCP host a
7
7
  complete GitHub toolkit: **34 tools** for issues, pull requests, files, projects,
8
8
  labels and organizations.
9
9
 
10
+ Read-only mode is the default. The server registers 19 read tools and keeps the
11
+ 15 write tools out of the agent's context. Set
12
+ `GITHUB_MCP_ENABLE_WRITES=true` to load the full set.
13
+
10
14
  It runs locally, connects to your host over stdio, and calls the GitHub API
11
15
  directly with your own token. There is no intermediary service.
12
16
 
@@ -36,6 +40,10 @@ API details.
36
40
 
37
41
  - **34 tools across 8 domains.** Issues, pull requests, repositories, projects,
38
42
  labels, organizations, file operations and server info.
43
+ - **Read-only by default.** 19 tools, no write surface. Enable writes explicitly.
44
+ - **Built for the common path.** The daily loop is small: read an issue, review a pull
45
+ request, change a file, update a project field. The long tail stays out of the
46
+ agent's context until you need it.
39
47
  - **Organization-aware.** `owner_list_repository` lists every repository in an
40
48
  organization.
41
49
  - **Built for real API conditions.** Retries with backoff, rate-limit handling
@@ -43,12 +51,18 @@ API details.
43
51
  nothing to tune.
44
52
  - **Guardrails.** Pin the server to one owner or repository with
45
53
  `GITHUB_OWNER` / `GITHUB_REPO`, and mismatched calls are rejected.
54
+ - **Guided token setup.** The generator builds a pre-filled fine-grained token
55
+ with a read-only or read-write preset.
46
56
  - **Any MCP host.** A standard stdio server. Works with opencode, Claude
47
57
  Desktop, Cursor, VS Code and anything else that speaks MCP.
48
58
  - **No runtime dependencies.** The npm package ships one bundled file, so
49
59
  installing it does not pull a dependency tree.
50
60
  - **GitHub Enterprise Server.** Point `GITHUB_HOST` at your GHES API.
51
61
 
62
+ If your work is mostly Actions, security scanning, notifications, discussions,
63
+ gists, governance or Copilot, use the official GitHub MCP server. GitHub Pro MCP
64
+ is built for the daily issue, pull request, file and project loop.
65
+
52
66
  ## Installation
53
67
 
54
68
  Requires Node.js 22 or newer for the npm package. The Docker image includes
@@ -80,6 +94,10 @@ docker pull anantanandgupta/github-pro-mcp
80
94
  3. Add it to your MCP host and restart the host.
81
95
  4. Verify with the `auth_get_current_login` tool.
82
96
 
97
+ By default the server is read-only and registers 19 tools. To enable issue,
98
+ pull request, file, label and project writes, set
99
+ `GITHUB_MCP_ENABLE_WRITES=true` in the host environment.
100
+
83
101
  ### Create a token
84
102
 
85
103
  Use a fine-grained personal access token. The generator builds a pre-filled
@@ -96,6 +114,9 @@ to an organization that requires approval, request it while creating the token.
96
114
  The full per-tool permission table is at
97
115
  https://mcp.anantanandgupta.net/github-pro-mcp/permissions/
98
116
 
117
+ The read-only preset pairs with the default read-only mode. The read-write
118
+ preset requires `GITHUB_MCP_ENABLE_WRITES=true` in the host environment.
119
+
99
120
  ### Connect opencode
100
121
 
101
122
  Add to your opencode config (v2 nests servers under `mcp.servers`):
@@ -108,7 +129,8 @@ Add to your opencode config (v2 nests servers under `mcp.servers`):
108
129
  "type": "local",
109
130
  "command": ["npx", "-y", "github-pro-mcp"],
110
131
  "environment": {
111
- "GITHUB_PERSONAL_ACCESS_TOKEN": "{env:GITHUB_TOKEN}"
132
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "{env:GITHUB_TOKEN}",
133
+ "GITHUB_MCP_ENABLE_WRITES": "true"
112
134
  }
113
135
  }
114
136
  }
@@ -117,7 +139,8 @@ Add to your opencode config (v2 nests servers under `mcp.servers`):
117
139
  ```
118
140
 
119
141
  `{env:GITHUB_TOKEN}` reads that variable from the environment opencode starts
120
- in. Use a literal token instead if you prefer.
142
+ in. Use a literal token instead if you prefer. Remove
143
+ `GITHUB_MCP_ENABLE_WRITES` to stay read-only.
121
144
 
122
145
  ### Connect Claude Desktop
123
146
 
@@ -133,7 +156,8 @@ Edit `claude_desktop_config.json` and restart Claude Desktop:
133
156
  "command": "npx",
134
157
  "args": ["-y", "github-pro-mcp"],
135
158
  "env": {
136
- "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_..."
159
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_...",
160
+ "GITHUB_MCP_ENABLE_WRITES": "true"
137
161
  }
138
162
  }
139
163
  }
@@ -156,9 +180,10 @@ https://mcp.anantanandgupta.net/github-pro-mcp/install/
156
180
  "servers": {
157
181
  "github_pro": {
158
182
  "type": "local",
159
- "command": ["docker", "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "anantanandgupta/github-pro-mcp"],
183
+ "command": ["docker", "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_MCP_ENABLE_WRITES", "anantanandgupta/github-pro-mcp"],
160
184
  "environment": {
161
- "GITHUB_PERSONAL_ACCESS_TOKEN": "{env:GITHUB_TOKEN}"
185
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "{env:GITHUB_TOKEN}",
186
+ "GITHUB_MCP_ENABLE_WRITES": "true"
162
187
  }
163
188
  }
164
189
  }
@@ -183,14 +208,16 @@ The same `docker run` command works as the server command in other hosts.
183
208
  | `GITHUB_HOST` | GitHub Enterprise Server API, for example `https://github.example.com/api/v3` |
184
209
  | `GITHUB_OWNER` | Pin the server to a single owner and reject mismatched calls |
185
210
  | `GITHUB_REPO` | Pin the server to a single repository and reject mismatched calls |
211
+ | `GITHUB_MCP_ENABLE_WRITES` | Set to `true`, `1`, `yes` or `on` to register the 15 write tools. Default `false` |
186
212
 
187
213
  Retries, rate limiting and logging have sensible defaults. You do not need to
188
214
  tune them for normal use.
189
215
 
190
216
  ## Tools
191
217
 
192
- 34 tools across 8 domains. Tool names appear in your host's tool list after the
193
- server starts.
218
+ 34 tools across 8 domains. Read-only mode registers 19 of them; the other 15
219
+ are write tools. Tool names appear in your host's tool list after the server
220
+ starts.
194
221
 
195
222
  | Domain | Tools | Count |
196
223
  |--------|-------|-------|
@@ -207,6 +234,9 @@ server starts.
207
234
  Task-based examples and the prompts they respond to are at
208
235
  https://mcp.anantanandgupta.net/github-pro-mcp/tools/
209
236
 
237
+ A measured comparison with the official GitHub MCP server is at
238
+ https://mcp.anantanandgupta.net/github-pro-mcp/compare/
239
+
210
240
  ## Troubleshooting
211
241
 
212
242
  | Symptom | Likely cause |
@@ -217,6 +247,7 @@ https://mcp.anantanandgupta.net/github-pro-mcp/tools/
217
247
  | `429` | Rate limited. The server retries; wait if it persists |
218
248
  | No tools appear in the host | The host was not restarted, or the server failed to start |
219
249
  | Server exits immediately | `GITHUB_PERSONAL_ACCESS_TOKEN` is not set in the host's environment |
250
+ | Write tool not in the list | `GITHUB_MCP_ENABLE_WRITES` is not set to `true` |
220
251
 
221
252
  ## Support
222
253
 
@@ -26705,6 +26705,18 @@ function formatRepo(r) {
26705
26705
  };
26706
26706
  }
26707
26707
 
26708
+ // src/utils/annotations.ts
26709
+ var READ_ONLY = {
26710
+ readOnlyHint: true,
26711
+ idempotentHint: true,
26712
+ openWorldHint: true
26713
+ };
26714
+ var WRITE = {
26715
+ readOnlyHint: false,
26716
+ idempotentHint: false,
26717
+ openWorldHint: true
26718
+ };
26719
+
26708
26720
  // src/tools/repos.ts
26709
26721
  function toBase64(content) {
26710
26722
  return Buffer.from(content, "utf-8").toString("base64");
@@ -26713,6 +26725,7 @@ function registerAuthGetCurrentLogin(server2) {
26713
26725
  server2.registerTool(
26714
26726
  "auth_get_current_login",
26715
26727
  {
26728
+ annotations: READ_ONLY,
26716
26729
  description: "Get the authenticated GitHub user's profile. Use this to verify your GitHub token and identity.",
26717
26730
  inputSchema: object2({})
26718
26731
  },
@@ -26740,6 +26753,7 @@ function registerFileGetContent(server2) {
26740
26753
  server2.registerTool(
26741
26754
  "file_get_content",
26742
26755
  {
26756
+ annotations: READ_ONLY,
26743
26757
  description: "Get the contents of a file from a GitHub repository. Returns the decoded file content as text.",
26744
26758
  inputSchema: OwnerRepoSchema.extend({
26745
26759
  path: string2().min(1).describe("File path within the repository (e.g., 'README.md' or 'src/index.ts')"),
@@ -26776,6 +26790,7 @@ function registerProfileGet(server2) {
26776
26790
  server2.registerTool(
26777
26791
  "profile_get",
26778
26792
  {
26793
+ annotations: READ_ONLY,
26779
26794
  description: "Get profile info for any GitHub owner (user or organization). Useful for checking if an owner is an organization before calling organization-only tools like owner_list_issue_field.",
26780
26795
  inputSchema: object2({
26781
26796
  owner: string2().min(1).describe("Repository owner (user or organization)")
@@ -26808,6 +26823,7 @@ function registerOwnerListRepository(server2) {
26808
26823
  server2.registerTool(
26809
26824
  "owner_list_repository",
26810
26825
  {
26826
+ annotations: READ_ONLY,
26811
26827
  description: "List all repositories for a given owner. For an organization, returns every repository in the organization. For a user, returns the repositories the token can access. The 'type' filter applies to user owners only. Unlike search_repositories, results are unranked.",
26812
26828
  inputSchema: object2({
26813
26829
  owner: string2().min(1).describe("Repository owner (user or organization)"),
@@ -26844,6 +26860,7 @@ function registerFileCreateUpdate(server2) {
26844
26860
  server2.registerTool(
26845
26861
  "file_create_update",
26846
26862
  {
26863
+ annotations: WRITE,
26847
26864
  description: "Create or update a single file in a GitHub repository. Content is base64-encoded automatically. Provide sha when updating an existing file (obtain sha from the previous file_create_update response).",
26848
26865
  inputSchema: OwnerRepoSchema.extend({
26849
26866
  path: string2().min(1).describe("Path where to create/update the file"),
@@ -26890,6 +26907,7 @@ function registerCodeSearch(server2) {
26890
26907
  server2.registerTool(
26891
26908
  "code_search",
26892
26909
  {
26910
+ annotations: READ_ONLY,
26893
26911
  description: "Search for code across GitHub repositories. Supports GitHub search qualifiers like 'repo:...', 'language:...', 'path:...'.",
26894
26912
  inputSchema: object2({
26895
26913
  query: string2().min(1).max(1e3).describe("Search query with GitHub qualifiers, e.g., 'function repo:owner/repo language:python'"),
@@ -26922,6 +26940,7 @@ function registerFileDelete(server2) {
26922
26940
  server2.registerTool(
26923
26941
  "file_delete",
26924
26942
  {
26943
+ annotations: WRITE,
26925
26944
  description: "Delete a file from a GitHub repository. Requires the SHA of the file (obtain from file_get_content).",
26926
26945
  inputSchema: OwnerRepoSchema.extend({
26927
26946
  path: string2().min(1).describe("Path to the file to delete"),
@@ -26963,6 +26982,7 @@ function registerRepositorySearch(server2) {
26963
26982
  server2.registerTool(
26964
26983
  "repository_search",
26965
26984
  {
26985
+ annotations: READ_ONLY,
26966
26986
  description: "Search for GitHub repositories by query. Supports GitHub search qualifiers like 'org:...', 'language:...', 'stars:>100'.",
26967
26987
  inputSchema: object2({
26968
26988
  query: string2().min(1).max(1e3).describe("Search query with GitHub qualifiers, e.g., 'org:microsoft language:python'"),
@@ -26995,6 +27015,7 @@ function registerOrganizationSearch(server2) {
26995
27015
  server2.registerTool(
26996
27016
  "organization_search",
26997
27017
  {
27018
+ annotations: READ_ONLY,
26998
27019
  description: "Search for organizations on GitHub by query. Supports qualifiers like 'location:...', 'repos:>...'.",
26999
27020
  inputSchema: object2({
27000
27021
  query: string2().min(1).max(1e3).describe("Search query, e.g., 'microsoft' or 'location:seattle repos:>5'"),
@@ -27029,6 +27050,7 @@ function registerOwnerListIssueType(server2) {
27029
27050
  server2.registerTool(
27030
27051
  "owner_list_issue_type",
27031
27052
  {
27053
+ annotations: READ_ONLY,
27032
27054
  description: "List issue types for an organization (e.g., Bug, Feature, Enhancement). Owner must be an organization.",
27033
27055
  inputSchema: object2({
27034
27056
  owner: string2().min(1).describe("Organization owner")
@@ -27053,6 +27075,7 @@ function registerOwnerListOrganization(server2) {
27053
27075
  server2.registerTool(
27054
27076
  "owner_list_organization",
27055
27077
  {
27078
+ annotations: READ_ONLY,
27056
27079
  description: "List all organizations that a user account belongs to. Provide a username, not an organization name.",
27057
27080
  inputSchema: object2({
27058
27081
  owner: string2().min(1).describe("Username to list organizations for")
@@ -27086,6 +27109,7 @@ function registerPullRequestRead(server2) {
27086
27109
  server2.registerTool(
27087
27110
  "pull_request_read",
27088
27111
  {
27112
+ annotations: READ_ONLY,
27089
27113
  description: "Get details of a pull request. Optionally include commits and reviews. Each include_* flag makes additional API calls.",
27090
27114
  inputSchema: OwnerRepoSchema.extend({
27091
27115
  pull_number: PullNumberSchema,
@@ -27148,6 +27172,7 @@ function registerPullRequestCreate(server2) {
27148
27172
  server2.registerTool(
27149
27173
  "pull_request_create",
27150
27174
  {
27175
+ annotations: WRITE,
27151
27176
  description: "Create a new pull request in a GitHub repository.",
27152
27177
  inputSchema: OwnerRepoSchema.extend({
27153
27178
  title: string2().min(1).describe("Pull request title"),
@@ -27183,6 +27208,7 @@ function registerPullRequestMerge(server2) {
27183
27208
  server2.registerTool(
27184
27209
  "pull_request_merge",
27185
27210
  {
27211
+ annotations: WRITE,
27186
27212
  description: "Merge a pull request in a GitHub repository.",
27187
27213
  inputSchema: OwnerRepoSchema.extend({
27188
27214
  pull_number: PullNumberSchema,
@@ -27216,6 +27242,7 @@ function registerIssueRead(server2) {
27216
27242
  server2.registerTool(
27217
27243
  "issue_read",
27218
27244
  {
27245
+ annotations: READ_ONLY,
27219
27246
  description: "Get details of a specific issue. Optionally include comments, labels, field values, and sub-issues. Each include_* flag makes additional API calls.",
27220
27247
  inputSchema: OwnerRepoSchema.extend({
27221
27248
  issue_number: IssueNumberSchema,
@@ -27306,6 +27333,7 @@ function registerIssueList(server2) {
27306
27333
  server2.registerTool(
27307
27334
  "issue_list",
27308
27335
  {
27336
+ annotations: READ_ONLY,
27309
27337
  description: "List issues in a repository. Optionally include org-level field values (Priority, Effort) and/or project board field values (Status, Application). Supports field-based filtering with filter_fields (AND logic).",
27310
27338
  inputSchema: OwnerRepoSchema.extend({
27311
27339
  state: _enum(["open", "closed", "all"]).optional().describe("Filter by issue state"),
@@ -27506,6 +27534,7 @@ function registerIssueSearch(server2) {
27506
27534
  server2.registerTool(
27507
27535
  "issue_search",
27508
27536
  {
27537
+ annotations: READ_ONLY,
27509
27538
  description: "Search for issues in GitHub repositories using GitHub's search syntax.",
27510
27539
  inputSchema: object2({
27511
27540
  query: string2().min(1).max(1e3).describe("Search query using GitHub issues search syntax, e.g., 'label:bug repo:owner/repo'"),
@@ -27545,6 +27574,7 @@ function registerIssueAddComment(server2) {
27545
27574
  server2.registerTool(
27546
27575
  "issue_add_comment",
27547
27576
  {
27577
+ annotations: WRITE,
27548
27578
  description: "Add a comment to an existing issue in a GitHub repository.",
27549
27579
  inputSchema: OwnerRepoSchema.extend({
27550
27580
  issue_number: IssueNumberSchema,
@@ -27574,6 +27604,7 @@ function registerSubIssueWrite(server2) {
27574
27604
  server2.registerTool(
27575
27605
  "sub_issue_write",
27576
27606
  {
27607
+ annotations: WRITE,
27577
27608
  description: "Add or remove a sub-issue from a parent issue in a GitHub repository. Method 'add' adds sub_issue_number as a sub-issue of issue_number. Method 'remove' removes the sub-issue relationship.",
27578
27609
  inputSchema: OwnerRepoSchema.extend({
27579
27610
  method: _enum(["add", "remove"]).describe("Operation: add or remove a sub-issue"),
@@ -27645,6 +27676,7 @@ function registerIssueCreate(server2) {
27645
27676
  server2.registerTool(
27646
27677
  "issue_create",
27647
27678
  {
27679
+ annotations: WRITE,
27648
27680
  description: "Create a new issue in a GitHub repository.",
27649
27681
  inputSchema: OwnerRepoSchema.extend({
27650
27682
  title: string2().min(1).describe("Issue title"),
@@ -27678,6 +27710,7 @@ function registerIssueClose(server2) {
27678
27710
  server2.registerTool(
27679
27711
  "issue_close",
27680
27712
  {
27713
+ annotations: WRITE,
27681
27714
  description: "Close an issue in a GitHub repository.",
27682
27715
  inputSchema: OwnerRepoSchema.extend({
27683
27716
  issue_number: IssueNumberSchema,
@@ -27719,6 +27752,7 @@ function registerIssueReopen(server2) {
27719
27752
  server2.registerTool(
27720
27753
  "issue_reopen",
27721
27754
  {
27755
+ annotations: WRITE,
27722
27756
  description: "Reopen a closed issue in a GitHub repository.",
27723
27757
  inputSchema: OwnerRepoSchema.extend({
27724
27758
  issue_number: IssueNumberSchema
@@ -27746,6 +27780,7 @@ function registerIssueUpdate(server2) {
27746
27780
  server2.registerTool(
27747
27781
  "issue_update",
27748
27782
  {
27783
+ annotations: WRITE,
27749
27784
  description: "Update an issue's title, body, or state in a GitHub repository.",
27750
27785
  inputSchema: OwnerRepoSchema.extend({
27751
27786
  issue_number: IssueNumberSchema,
@@ -27786,6 +27821,7 @@ function registerIssueAddLabel(server2) {
27786
27821
  server2.registerTool(
27787
27822
  "issue_add_label",
27788
27823
  {
27824
+ annotations: WRITE,
27789
27825
  description: "Add labels to an issue in a GitHub repository.",
27790
27826
  inputSchema: OwnerRepoSchema.extend({
27791
27827
  issue_number: IssueNumberSchema,
@@ -27814,6 +27850,7 @@ function registerIssueRemoveLabel(server2) {
27814
27850
  server2.registerTool(
27815
27851
  "issue_remove_label",
27816
27852
  {
27853
+ annotations: WRITE,
27817
27854
  description: "Remove labels from an issue in a GitHub repository.",
27818
27855
  inputSchema: OwnerRepoSchema.extend({
27819
27856
  issue_number: IssueNumberSchema,
@@ -27855,6 +27892,7 @@ function registerOwnerListIssueField(server2) {
27855
27892
  server2.registerTool(
27856
27893
  "owner_list_issue_field",
27857
27894
  {
27895
+ annotations: READ_ONLY,
27858
27896
  description: "List all issue field definitions for an organization (e.g., Priority, Effort, Start date, Target date). Owner must be an organization.",
27859
27897
  inputSchema: object2({
27860
27898
  owner: string2().min(1).describe("Organization owner")
@@ -27879,6 +27917,7 @@ function registerIssueGetFieldValue(server2) {
27879
27917
  server2.registerTool(
27880
27918
  "issue_get_field_value",
27881
27919
  {
27920
+ annotations: READ_ONLY,
27882
27921
  description: "Get the current field values (Priority, Effort, dates, etc.) assigned to an issue.",
27883
27922
  inputSchema: OwnerRepoSchema.extend({
27884
27923
  issue_number: IssueNumberSchema
@@ -27902,6 +27941,7 @@ function registerIssueSetFieldValue(server2) {
27902
27941
  server2.registerTool(
27903
27942
  "issue_set_field_value",
27904
27943
  {
27944
+ annotations: WRITE,
27905
27945
  description: "Set field values (Priority, Effort, dates, etc.) on an issue. Accepts either field_id (numeric ID) or field_name (e.g. 'Priority'). For single-select fields, value can be the option name (e.g. 'Medium') or option ID. For dates, use ISO 8601 (e.g. '2026-06-15'). Pass null to clear a field.",
27906
27946
  inputSchema: OwnerRepoSchema.extend({
27907
27947
  issue_number: IssueNumberSchema,
@@ -27969,6 +28009,7 @@ function registerLabelList(server2) {
27969
28009
  server2.registerTool(
27970
28010
  "label_list",
27971
28011
  {
28012
+ annotations: READ_ONLY,
27972
28013
  description: "List labels in a GitHub repository.",
27973
28014
  inputSchema: OwnerRepoSchema.extend({
27974
28015
  page: PaginationSchema.shape.page,
@@ -27999,6 +28040,7 @@ function registerLabelWrite(server2) {
27999
28040
  server2.registerTool(
28000
28041
  "label_write",
28001
28042
  {
28043
+ annotations: WRITE,
28002
28044
  description: "Create, update, or delete a label in a GitHub repository. Method 'create' requires name and color. Method 'update' requires name (current name) and optional color/description/new_name. Method 'delete' requires name only.",
28003
28045
  inputSchema: OwnerRepoSchema.extend({
28004
28046
  method: _enum(["create", "update", "delete"]).describe("Action to perform on the label"),
@@ -28055,6 +28097,7 @@ function registerOwnerListProject(server2) {
28055
28097
  server2.registerTool(
28056
28098
  "owner_list_project",
28057
28099
  {
28100
+ annotations: READ_ONLY,
28058
28101
  description: "List GitHub Projects (v2) for an owner. Works for both organizations and individual users.",
28059
28102
  inputSchema: object2({
28060
28103
  owner: string2().min(1).describe("Organization or user login"),
@@ -28157,6 +28200,7 @@ function registerProjectsGet(server2) {
28157
28200
  server2.registerTool(
28158
28201
  "projects_get",
28159
28202
  {
28203
+ annotations: READ_ONLY,
28160
28204
  description: "Get a single GitHub Project (v2) by its GraphQL node ID with optional fields, items, and status updates. Each include_* flag makes additional API calls.",
28161
28205
  inputSchema: object2({
28162
28206
  project_id: string2().min(1).describe("GraphQL node ID of the project (obtain from owner_list_project)"),
@@ -28306,6 +28350,7 @@ function registerProjectsWrite(server2) {
28306
28350
  server2.registerTool(
28307
28351
  "projects_write",
28308
28352
  {
28353
+ annotations: WRITE,
28309
28354
  description: "Manage items in a GitHub Project (v2): add, update field values, delete items, or create status updates. Methods: add_item (requires content_id), update_item (requires item_id + field_values; field_values each need field_id, value, and value_type), delete_item (requires item_id), create_status_update (requires body; optional status/start_date/target_date). Note: update_item sends field values one at a time \u2014 if one fails, earlier updates are already committed. Check partial: true in the response.",
28310
28355
  inputSchema: object2({
28311
28356
  project_id: string2().min(1).describe("GraphQL node ID of the project"),
@@ -28500,6 +28545,7 @@ function registerServerInfo(server2, meta) {
28500
28545
  server2.registerTool(
28501
28546
  "server_info",
28502
28547
  {
28548
+ annotations: READ_ONLY,
28503
28549
  description: "Get information about this MCP server, including version, support links, and runtime configuration.",
28504
28550
  inputSchema: object2({})
28505
28551
  },
@@ -28507,6 +28553,8 @@ function registerServerInfo(server2, meta) {
28507
28553
  const runtime = {
28508
28554
  node_version: process.version,
28509
28555
  platform: process.platform,
28556
+ read_only: !meta.writesEnabled,
28557
+ writes_enabled: meta.writesEnabled,
28510
28558
  pinned_owner: process.env.GITHUB_OWNER ?? null,
28511
28559
  pinned_repo: process.env.GITHUB_REPO ?? null,
28512
28560
  log_level: process.env.GITHUB_LOG_LEVEL ?? "error",
@@ -28541,6 +28589,12 @@ function registerServerInfo(server2, meta) {
28541
28589
  // src/index.ts
28542
28590
  var require2 = createRequire(import.meta.url);
28543
28591
  var pkg = require2("../package.json");
28592
+ function readBool(name, fallback) {
28593
+ const raw = process.env[name];
28594
+ if (raw === void 0) return fallback;
28595
+ return ["1", "true", "yes", "on"].includes(raw.toLowerCase());
28596
+ }
28597
+ var writesEnabled = readBool("GITHUB_MCP_ENABLE_WRITES", false);
28544
28598
  var server = new McpServer(
28545
28599
  { name: pkg.name, version: pkg.version },
28546
28600
  { capabilities: { tools: {} } }
@@ -28554,46 +28608,52 @@ var serverMeta = {
28554
28608
  repository: pkg.repository?.url ?? "",
28555
28609
  homepage: pkg.homepage ?? "",
28556
28610
  bug_tracker: pkg.bugs?.url ?? "",
28557
- bin: pkg.bin ?? {}
28611
+ bin: pkg.bin ?? {},
28612
+ writesEnabled
28558
28613
  };
28559
28614
  registerAuthGetCurrentLogin(server);
28560
28615
  registerFileGetContent(server);
28561
- registerFileDelete(server);
28562
28616
  registerProfileGet(server);
28563
28617
  registerOwnerListRepository(server);
28564
- registerFileCreateUpdate(server);
28565
28618
  registerCodeSearch(server);
28566
28619
  registerRepositorySearch(server);
28567
28620
  registerOrganizationSearch(server);
28568
28621
  registerOwnerListIssueType(server);
28569
28622
  registerOwnerListOrganization(server);
28570
28623
  registerPullRequestRead(server);
28571
- registerPullRequestCreate(server);
28572
- registerPullRequestMerge(server);
28573
28624
  registerIssueRead(server);
28574
28625
  registerIssueList(server);
28575
28626
  registerIssueSearch(server);
28576
- registerIssueAddComment(server);
28577
- registerSubIssueWrite(server);
28578
- registerIssueCreate(server);
28579
- registerIssueClose(server);
28580
- registerIssueReopen(server);
28581
- registerIssueUpdate(server);
28582
- registerIssueAddLabel(server);
28583
- registerIssueRemoveLabel(server);
28584
28627
  registerOwnerListIssueField(server);
28585
28628
  registerIssueGetFieldValue(server);
28586
- registerIssueSetFieldValue(server);
28587
28629
  registerLabelList(server);
28588
- registerLabelWrite(server);
28589
28630
  registerOwnerListProject(server);
28590
28631
  registerProjectsGet(server);
28591
- registerProjectsWrite(server);
28632
+ if (writesEnabled) {
28633
+ registerFileCreateUpdate(server);
28634
+ registerFileDelete(server);
28635
+ registerPullRequestCreate(server);
28636
+ registerPullRequestMerge(server);
28637
+ registerIssueAddComment(server);
28638
+ registerSubIssueWrite(server);
28639
+ registerIssueCreate(server);
28640
+ registerIssueClose(server);
28641
+ registerIssueReopen(server);
28642
+ registerIssueUpdate(server);
28643
+ registerIssueAddLabel(server);
28644
+ registerIssueRemoveLabel(server);
28645
+ registerIssueSetFieldValue(server);
28646
+ registerLabelWrite(server);
28647
+ registerProjectsWrite(server);
28648
+ }
28592
28649
  registerServerInfo(server, serverMeta);
28593
28650
  async function main() {
28594
28651
  const transport = new StdioServerTransport();
28595
28652
  await server.connect(transport);
28596
- console.error("github-pro-mcp running on stdio");
28653
+ const toolCount = Object.keys(server._registeredTools ?? {}).length;
28654
+ console.error(
28655
+ `github-pro-mcp running on stdio (${writesEnabled ? "writes enabled" : "read-only"}, ${toolCount} tools)`
28656
+ );
28597
28657
  }
28598
28658
  main().catch((err) => {
28599
28659
  console.error("Fatal error:", err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-pro-mcp",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "MCP server providing GitHub API access with hybrid REST + GraphQL support",
5
5
  "main": "dist-package/index.js",
6
6
  "bin": {
@@ -29,7 +29,8 @@
29
29
  "test:consistency": "node --test --test-global-setup='src/__tests__/setup.mjs' 'src/__tests__/consistency.test.mjs'",
30
30
  "test:docker": "node --test 'test/docker/**/*.test.mjs'",
31
31
  "test:npm": "node --test 'test/integration/**/*.test.mjs'",
32
- "test:all": "npm test && npm run test:docker && npm run test:npm",
32
+ "test:all": "npm test && npm run test:readonly && npm run test:docker && npm run test:npm",
33
+ "test:readonly": "node --test 'test/readonly.test.mjs'",
33
34
  "test:coverage": "node --experimental-test-coverage --test --test-global-setup='src/__tests__/setup.mjs' 'src/__tests__/**/*.test.mjs'",
34
35
  "test:inspector": "npx @modelcontextprotocol/inspector node dist/index.js",
35
36
  "prepack": "npm run build && npm run bundle",
@@ -55,7 +56,7 @@
55
56
  "bugs": {
56
57
  "url": "https://github.com/anantanandgupta/mcp-servers/issues"
57
58
  },
58
- "homepage": "https://github.com/anantanandgupta/mcp-servers#readme",
59
+ "homepage": "https://mcp.anantanandgupta.net/github-pro-mcp/",
59
60
  "type": "module",
60
61
  "engines": {
61
62
  "node": ">=22.0.0"