* fix(mcp-apps): defer _meta.ui strip to per-request RegisterTools
The MCP Apps `_meta.ui` strip lived in `Builder.Build()`, which calls
`checkFeatureFlag(context.Background())`. The HTTP feature checker
(`createHTTPFeatureChecker`) reads insiders mode from the request
context — a background context never has it set, so the FF reported
MCP Apps off and the strip ran eagerly at server startup. Per-request
inventory factories then served pre-stripped tools regardless of
whether the request actually arrived on the `/insiders` route.
Symptom: `github/github-mcp-server-remote` returns 0 tools with
`_meta.ui` over HTTP `/insiders`, despite the source unconditionally
setting it on `get_me`, `issue_write`, and `create_pull_request`.
VS Code only renders MCP App UIs because of its persistent tool cache
from earlier deploys. Reproducible locally with
`cmd/github-mcp-server http --insiders` plus a vanilla curl tools/list.
Fix: drop the strip from `Build()`. Apply it in `RegisterTools(ctx,…)`
where the per-request context is in scope and the HTTP feature checker
can correctly detect insiders mode (or the remote checker can correctly
read user identity for Statsig flag lookup).
The same root cause affects `github/github-mcp-server-remote` — its
`featureflags.NewComposedFeatureFlagChecker` reads
`requestctx.User(ctx)`, which background context lacks, so the
`remote_mcp_ui_apps` Statsig flag always returned false. The fix here
covers both downstreams since `RegisterTools` is the single entry
point for tool registration.
Stdio mode is unaffected: it uses a closure-captured insiders mode
flag (`createFeatureChecker`) that does not depend on context, and the
per-request strip in `RegisterTools` produces the same outcome.
Verified end-to-end against the deployed remote tool definitions:
HTTP /insiders → 3 tools with _meta.ui (was 0)
HTTP / → 0 tools with _meta.ui (correct)
stdio --insiders → 3 tools with _meta.ui (unchanged)
stdio → 0 tools with _meta.ui (correct)
Adds:
- pkg/http: TestInsidersRoutePreservesUIMeta — pins the regression
- pkg/inventory: updates the existing strip tests to use the new
RegisterTools-as-strip-site contract via a captureRegisteredTools
helper
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* style: gofmt handler_test.go and registry_test.go
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* lint: address revive (context-as-argument) and unused checkFeatureFlag
- Reorder captureRegisteredTools params to put context.Context first
- Remove dead Builder.checkFeatureFlag (was only called by Build's
former MCP Apps strip, now done in RegisterTools via the Inventory
receiver's checkFeatureFlag instead)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GoReleaser Release / release (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
* Fix set_issue_fields mutation: use correct inline fragments for IssueFieldValue union
The mutation response struct used a single inline fragment
'... on IssueFieldDateValue' with a 'Name' field that doesn't exist
on that type (only IssueFieldSingleSelectValue has 'name'). This
caused GraphQL validation to fail with:
Field 'name' doesn't exist on type 'IssueFieldDateValue'
Since GraphQL validates the entire document (including response
selection sets) before executing any operation, the mutation never
fired at all — no fields were ever set regardless of input.
Fix by adding correct inline fragments for all four union types:
- IssueFieldTextValue (value)
- IssueFieldSingleSelectValue (name)
- IssueFieldDateValue (value)
- IssueFieldNumberValue (value)
* Update test mock to match corrected inline fragments
* Update handler_test.go formatting
Add NormalizeContentType middleware that strips optional parameters
(e.g. charset=utf-8) from application/json Content-Type headers before
the request reaches the Go SDK's StreamableHTTP handler, which performs
strict string matching.
Per RFC 8259, the charset parameter is redundant for JSON but must be
accepted per HTTP semantics.
Fixes#2333
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rebase PR #2282 onto main (post-#2332) and unify feature flag
allowlists into a single source of truth.
- Add MCPAppsFeatureFlag, AllowedFeatureFlags, InsidersFeatureFlags,
and ResolveFeatureFlags in feature_flags.go
- AllowedFeatureFlags includes all user-controllable flags (MCP Apps +
granular), InsidersFeatureFlags only includes MCPAppsFeatureFlag
- HeaderAllowedFeatureFlags() now delegates to AllowedFeatureFlags
- Builder uses feature checker instead of insidersMode bool
- Remove InsidersOnly field from ServerTool and WithInsidersMode from
Builder
- HTTP feature checker uses ResolveFeatureFlags for per-request
resolution with insiders expansion
- Tool handlers check MCPAppsFeatureFlag via IsFeatureEnabled instead
of InsidersMode
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The HTTP server now respects the same static CLI flags as the stdio
server: --toolsets, --tools, --exclude-tools, --read-only,
--dynamic-toolsets, and --insiders.
A static inventory is built once at startup from these flags, producing
a pre-filtered tool/resource/prompt universe. Per-request headers
(X-MCP-Toolsets, X-MCP-Tools, etc.) can only narrow within these
bounds, never expand beyond them. When no static flags are set, the
existing behavior is preserved — headers have full access to all
toolsets.
Fixes#2156
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of new CLI flags (--server-name, --server-title), reuse the
existing string override mechanism that already supports tool title/
description overrides throughout the codebase.
Users can now configure the server name and title via:
- GITHUB_MCP_SERVER_NAME / GITHUB_MCP_SERVER_TITLE env vars
- "SERVER_NAME" / "SERVER_TITLE" keys in github-mcp-server-config.json
This is consistent with how all other user-visible strings are
overridden (e.g. GITHUB_MCP_TOOL_GET_ME_USER_TITLE). No new struct
fields or CLI flags are needed.
Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
Allows users running multiple GitHub MCP Server instances (e.g., for
github.com and GitHub Enterprise Server) to override the server name and
title in the MCP initialization response.
- Add --server-name / GITHUB_SERVER_NAME flag+env to override name
- Add --server-title / GITHUB_SERVER_TITLE flag+env to override title
- Defaults remain "github-mcp-server" and "GitHub MCP Server"
- Applies to both stdio and HTTP server modes
- Add tests for default and custom name/title
Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
CodeQL / Analyze (actions) (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
* Move scope storage into its own context key, separately from token info.
This allows us to provide scopes seperately in the remote server, where
we have scopes before we do the auth.
* Skip token extraction if token info already exists in context.
This is to avoid redundant token extraction in remote setup where token info may have already been extracted earlier in the request lifecycle.
* Check for existing scopes in context before fetching from GitHub API in scope challenge middleware
* Return error type for unknown tools in inventory builder and handle it in HTTP handler
Create a shared mcp.SchemaCache in RunHTTPServer and pass it through
to each per-request MCP Server via ServerOptions. This avoids repeated
JSON schema reflection and resolution when a new Server is created for
every request in stateless mode, matching the pattern used by the
remote server.