Skip to content

Commit 7f51f69

Browse files
Merge branch 'main' into http-static-flags-support
2 parents 0e0feeb + 95726ad commit 7f51f69

27 files changed

+837
-18
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: Insiders Feedback
3+
about: Give feedback related to a GitHub MCP Server Insiders feature
4+
title: "Insiders Feedback: "
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
Version: Insiders
11+
12+
Feature:
13+
14+
Feedback:

.github/workflows/docker-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
# https://github.com/docker/metadata-action
7171
- name: Extract Docker metadata
7272
id: meta
73-
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
73+
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
7474
with:
7575
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
7676
tags: |
@@ -106,7 +106,7 @@ jobs:
106106
# https://github.com/docker/build-push-action
107107
- name: Build and push Docker image
108108
id: build-and-push
109-
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
109+
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
110110
with:
111111
context: .
112112
push: ${{ github.event_name != 'pull_request' }}

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ The following sets of tools are available:
11191119
- `owner`: Repository owner (string, required)
11201120
- `pullNumber`: Pull request number (number, required)
11211121
- `repo`: Repository name (string, required)
1122+
- `threadId`: The node ID of the review thread (e.g., PRRT_kwDOxxx). Required for resolve_thread and unresolve_thread methods. Get thread IDs from pull_request_read with method get_review_comments. (string, optional)
11221123

11231124
- **search_pull_requests** - Search pull requests
11241125
- **Required OAuth Scopes**: `repo`
@@ -1241,9 +1242,12 @@ The following sets of tools are available:
12411242
- `author`: Author username or email address to filter commits by (string, optional)
12421243
- `owner`: Repository owner (string, required)
12431244
- `page`: Page number for pagination (min 1) (number, optional)
1245+
- `path`: Only commits containing this file path will be returned (string, optional)
12441246
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
12451247
- `repo`: Repository name (string, required)
12461248
- `sha`: Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA. (string, optional)
1249+
- `since`: Only commits after this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD) (string, optional)
1250+
- `until`: Only commits before this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD) (string, optional)
12471251

12481252
- **list_releases** - List releases
12491253
- **Required OAuth Scopes**: `repo`
@@ -1536,6 +1540,34 @@ set the following environment variable:
15361540
export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description"
15371541
```
15381542

1543+
### Overriding Server Name and Title
1544+
1545+
The same override mechanism can be used to customize the MCP server's `name` and
1546+
`title` fields in the initialization response. This is useful when running
1547+
multiple GitHub MCP Server instances (e.g., one for github.com and one for
1548+
GitHub Enterprise Server) so that agents can distinguish between them.
1549+
1550+
| Key | Environment Variable | Default |
1551+
|-----|---------------------|---------|
1552+
| `SERVER_NAME` | `GITHUB_MCP_SERVER_NAME` | `github-mcp-server` |
1553+
| `SERVER_TITLE` | `GITHUB_MCP_SERVER_TITLE` | `GitHub MCP Server` |
1554+
1555+
For example, to configure a server instance for GitHub Enterprise Server:
1556+
1557+
```json
1558+
{
1559+
"SERVER_NAME": "ghes-mcp-server",
1560+
"SERVER_TITLE": "GHES MCP Server"
1561+
}
1562+
```
1563+
1564+
Or using environment variables:
1565+
1566+
```sh
1567+
export GITHUB_MCP_SERVER_NAME="ghes-mcp-server"
1568+
export GITHUB_MCP_SERVER_TITLE="GHES MCP Server"
1569+
```
1570+
15391571
## Library Usage
15401572

15411573
The exported Go API of this module should currently be considered unstable, and subject to breaking changes. In the future, we may offer stability; please file an issue if there is a use case where this would be valuable.

docs/server-configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ We currently support the following ways in which the GitHub MCP Server can be co
1515
| Lockdown Mode | `X-MCP-Lockdown` header | `--lockdown-mode` flag or `GITHUB_LOCKDOWN_MODE` env var |
1616
| Insiders Mode | `X-MCP-Insiders` header or `/insiders` URL | `--insiders` flag or `GITHUB_INSIDERS` env var |
1717
| Scope Filtering | Always enabled | Always enabled |
18+
| Server Name/Title | Not available | `GITHUB_MCP_SERVER_NAME` / `GITHUB_MCP_SERVER_TITLE` env vars or `github-mcp-server-config.json` |
1819

1920
> **Default behavior:** If you don't specify any configuration, the server uses the **default toolsets**: `context`, `issues`, `pull_requests`, `repos`, `users`.
2021

internal/ghmcp/server.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/github/github-mcp-server/pkg/inventory"
1919
"github.com/github/github-mcp-server/pkg/lockdown"
2020
mcplog "github.com/github/github-mcp-server/pkg/log"
21+
"github.com/github/github-mcp-server/pkg/observability"
22+
"github.com/github/github-mcp-server/pkg/observability/metrics"
2123
"github.com/github/github-mcp-server/pkg/raw"
2224
"github.com/github/github-mcp-server/pkg/scopes"
2325
"github.com/github/github-mcp-server/pkg/translations"
@@ -116,6 +118,10 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se
116118
featureChecker := createFeatureChecker(cfg.EnabledFeatures)
117119

118120
// Create dependencies for tool handlers
121+
obs, err := observability.NewExporters(cfg.Logger, metrics.NewNoopMetrics())
122+
if err != nil {
123+
return nil, fmt.Errorf("failed to create observability exporters: %w", err)
124+
}
119125
deps := github.NewBaseDeps(
120126
clients.rest,
121127
clients.gql,
@@ -128,6 +134,7 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se
128134
},
129135
cfg.ContentWindowSize,
130136
featureChecker,
137+
obs,
131138
)
132139
// Build and register the tool/resource/prompt inventory
133140
inventoryBuilder := github.NewInventory(cfg.Translator).

pkg/github/__toolsnaps__/list_commits.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
"minimum": 1,
2020
"type": "number"
2121
},
22+
"path": {
23+
"description": "Only commits containing this file path will be returned",
24+
"type": "string"
25+
},
2226
"perPage": {
2327
"description": "Results per page for pagination (min 1, max 100)",
2428
"maximum": 100,
@@ -32,6 +36,14 @@
3236
"sha": {
3337
"description": "Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA.",
3438
"type": "string"
39+
},
40+
"since": {
41+
"description": "Only commits after this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD)",
42+
"type": "string"
43+
},
44+
"until": {
45+
"description": "Only commits before this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD)",
46+
"type": "string"
3547
}
3648
},
3749
"required": [

pkg/github/__toolsnaps__/pull_request_review_write.snap

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"annotations": {
33
"title": "Write operations (create, submit, delete) on pull request reviews."
44
},
5-
"description": "Create and/or submit, delete review of a pull request.\n\nAvailable methods:\n- create: Create a new review of a pull request. If \"event\" parameter is provided, the review is submitted. If \"event\" is omitted, a pending review is created.\n- submit_pending: Submit an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request. The \"body\" and \"event\" parameters are used when submitting the review.\n- delete_pending: Delete an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request.\n",
5+
"description": "Create and/or submit, delete review of a pull request.\n\nAvailable methods:\n- create: Create a new review of a pull request. If \"event\" parameter is provided, the review is submitted. If \"event\" is omitted, a pending review is created.\n- submit_pending: Submit an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request. The \"body\" and \"event\" parameters are used when submitting the review.\n- delete_pending: Delete an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request.\n- resolve_thread: Resolve a review thread. Requires only \"threadId\" parameter with the thread's node ID (e.g., PRRT_kwDOxxx). The owner, repo, and pullNumber parameters are not used for this method. Resolving an already-resolved thread is a no-op.\n- unresolve_thread: Unresolve a previously resolved review thread. Requires only \"threadId\" parameter. The owner, repo, and pullNumber parameters are not used for this method. Unresolving an already-unresolved thread is a no-op.\n",
66
"inputSchema": {
77
"properties": {
88
"body": {
@@ -27,7 +27,9 @@
2727
"enum": [
2828
"create",
2929
"submit_pending",
30-
"delete_pending"
30+
"delete_pending",
31+
"resolve_thread",
32+
"unresolve_thread"
3133
],
3234
"type": "string"
3335
},
@@ -42,6 +44,10 @@
4244
"repo": {
4345
"description": "Repository name",
4446
"type": "string"
47+
},
48+
"threadId": {
49+
"description": "The node ID of the review thread (e.g., PRRT_kwDOxxx). Required for resolve_thread and unresolve_thread methods. Get thread IDs from pull_request_read with method get_review_comments.",
50+
"type": "string"
4551
}
4652
},
4753
"required": [

pkg/github/__toolsnaps__/push_files.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"files": {
1313
"description": "Array of file objects to push, each object with path (string) and content (string)",
1414
"items": {
15+
"additionalProperties": false,
1516
"properties": {
1617
"content": {
1718
"description": "file content",

pkg/github/context_tools_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ func Test_GetMe(t *testing.T) {
9696
t.Run(tc.name, func(t *testing.T) {
9797
var deps ToolDependencies
9898
if tc.clientErr != "" {
99-
deps = stubDeps{clientFn: stubClientFnErr(tc.clientErr)}
99+
deps = stubDeps{clientFn: stubClientFnErr(tc.clientErr), obsv: stubExporters()}
100100
} else {
101-
deps = BaseDeps{Client: github.NewClient(tc.mockedClient)}
101+
obs := stubExporters()
102+
deps = BaseDeps{Client: github.NewClient(tc.mockedClient), Obsv: obs}
102103
}
103104
handler := serverTool.Handler(deps)
104105

@@ -304,7 +305,7 @@ func Test_GetTeams(t *testing.T) {
304305
{
305306
name: "getting client fails",
306307
makeDeps: func() ToolDependencies {
307-
return stubDeps{clientFn: stubClientFnErr("expected test error")}
308+
return stubDeps{clientFn: stubClientFnErr("expected test error"), obsv: stubExporters()}
308309
},
309310
requestArgs: map[string]any{},
310311
expectToolError: true,
@@ -315,6 +316,7 @@ func Test_GetTeams(t *testing.T) {
315316
makeDeps: func() ToolDependencies {
316317
return BaseDeps{
317318
Client: github.NewClient(httpClientUserFails()),
319+
Obsv: stubExporters(),
318320
}
319321
},
320322
requestArgs: map[string]any{},
@@ -327,6 +329,7 @@ func Test_GetTeams(t *testing.T) {
327329
return stubDeps{
328330
clientFn: stubClientFnFromHTTP(httpClientWithUser()),
329331
gqlClientFn: stubGQLClientFnErr("GraphQL client error"),
332+
obsv: stubExporters(),
330333
}
331334
},
332335
requestArgs: map[string]any{},
@@ -469,7 +472,7 @@ func Test_GetTeamMembers(t *testing.T) {
469472
},
470473
{
471474
name: "getting GraphQL client fails",
472-
deps: stubDeps{gqlClientFn: stubGQLClientFnErr("GraphQL client error")},
475+
deps: stubDeps{gqlClientFn: stubGQLClientFnErr("GraphQL client error"), obsv: stubExporters()},
473476
requestArgs: map[string]any{
474477
"org": "testorg",
475478
"team_slug": "testteam",

pkg/github/dependencies.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"log/slog"
78
"net/http"
89
"os"
910

1011
ghcontext "github.com/github/github-mcp-server/pkg/context"
1112
"github.com/github/github-mcp-server/pkg/http/transport"
1213
"github.com/github/github-mcp-server/pkg/inventory"
1314
"github.com/github/github-mcp-server/pkg/lockdown"
15+
"github.com/github/github-mcp-server/pkg/observability"
16+
"github.com/github/github-mcp-server/pkg/observability/metrics"
1417
"github.com/github/github-mcp-server/pkg/raw"
1518
"github.com/github/github-mcp-server/pkg/scopes"
1619
"github.com/github/github-mcp-server/pkg/translations"
@@ -94,6 +97,14 @@ type ToolDependencies interface {
9497

9598
// IsFeatureEnabled checks if a feature flag is enabled.
9699
IsFeatureEnabled(ctx context.Context, flagName string) bool
100+
101+
// Logger returns the structured logger, optionally enriched with
102+
// request-scoped data from ctx. Integrators provide their own slog.Handler
103+
// to control where logs are sent.
104+
Logger(ctx context.Context) *slog.Logger
105+
106+
// Metrics returns the metrics client
107+
Metrics(ctx context.Context) metrics.Metrics
97108
}
98109

99110
// BaseDeps is the standard implementation of ToolDependencies for the local server.
@@ -113,6 +124,9 @@ type BaseDeps struct {
113124

114125
// Feature flag checker for runtime checks
115126
featureChecker inventory.FeatureFlagChecker
127+
128+
// Observability exporters (includes logger)
129+
Obsv observability.Exporters
116130
}
117131

118132
// Compile-time assertion to verify that BaseDeps implements the ToolDependencies interface.
@@ -128,6 +142,7 @@ func NewBaseDeps(
128142
flags FeatureFlags,
129143
contentWindowSize int,
130144
featureChecker inventory.FeatureFlagChecker,
145+
obsv observability.Exporters,
131146
) *BaseDeps {
132147
return &BaseDeps{
133148
Client: client,
@@ -138,6 +153,7 @@ func NewBaseDeps(
138153
Flags: flags,
139154
ContentWindowSize: contentWindowSize,
140155
featureChecker: featureChecker,
156+
Obsv: obsv,
141157
}
142158
}
143159

@@ -170,6 +186,16 @@ func (d BaseDeps) GetFlags(_ context.Context) FeatureFlags { return d.Flags }
170186
// GetContentWindowSize implements ToolDependencies.
171187
func (d BaseDeps) GetContentWindowSize() int { return d.ContentWindowSize }
172188

189+
// Logger implements ToolDependencies.
190+
func (d BaseDeps) Logger(_ context.Context) *slog.Logger {
191+
return d.Obsv.Logger()
192+
}
193+
194+
// Metrics implements ToolDependencies.
195+
func (d BaseDeps) Metrics(ctx context.Context) metrics.Metrics {
196+
return d.Obsv.Metrics(ctx)
197+
}
198+
173199
// IsFeatureEnabled checks if a feature flag is enabled.
174200
// Returns false if the feature checker is nil, flag name is empty, or an error occurs.
175201
// This allows tools to conditionally change behavior based on feature flags.
@@ -247,6 +273,9 @@ type RequestDeps struct {
247273

248274
// Feature flag checker for runtime checks
249275
featureChecker inventory.FeatureFlagChecker
276+
277+
// Observability exporters (includes logger)
278+
obsv observability.Exporters
250279
}
251280

252281
// NewRequestDeps creates a RequestDeps with the provided clients and configuration.
@@ -258,6 +287,7 @@ func NewRequestDeps(
258287
t translations.TranslationHelperFunc,
259288
contentWindowSize int,
260289
featureChecker inventory.FeatureFlagChecker,
290+
obsv observability.Exporters,
261291
) *RequestDeps {
262292
return &RequestDeps{
263293
apiHosts: apiHosts,
@@ -267,6 +297,7 @@ func NewRequestDeps(
267297
T: t,
268298
ContentWindowSize: contentWindowSize,
269299
featureChecker: featureChecker,
300+
obsv: obsv,
270301
}
271302
}
272303

@@ -374,6 +405,16 @@ func (d *RequestDeps) GetFlags(ctx context.Context) FeatureFlags {
374405
// GetContentWindowSize implements ToolDependencies.
375406
func (d *RequestDeps) GetContentWindowSize() int { return d.ContentWindowSize }
376407

408+
// Logger implements ToolDependencies.
409+
func (d *RequestDeps) Logger(_ context.Context) *slog.Logger {
410+
return d.obsv.Logger()
411+
}
412+
413+
// Metrics implements ToolDependencies.
414+
func (d *RequestDeps) Metrics(ctx context.Context) metrics.Metrics {
415+
return d.obsv.Metrics(ctx)
416+
}
417+
377418
// IsFeatureEnabled checks if a feature flag is enabled.
378419
func (d *RequestDeps) IsFeatureEnabled(ctx context.Context, flagName string) bool {
379420
if d.featureChecker == nil || flagName == "" {

0 commit comments

Comments
 (0)