spike: context-scoped MCP server modes (repo / pull-request / project)#2693
Draft
SamMorrowDrums wants to merge 2 commits into
Draft
spike: context-scoped MCP server modes (repo / pull-request / project)#2693SamMorrowDrums wants to merge 2 commits into
SamMorrowDrums wants to merge 2 commits into
Conversation
Prototype a binding layer that pins the MCP server to a single GitHub
context so it presents a bespoke, purpose-built tool surface rather than
a reduced copy of the full server.
A new `pkg/binding` package transforms the tool universe for one of three
scopes — a repository, a pull request, or a ProjectsV2 project. For each
admitted tool it:
- removes the context-identifying params (owner, repo, pullNumber, ...)
from the advertised input schema and injects the fixed values at call
time;
- narrows the `method` enum to the operations the scope supports, pruning
disallowed values from the schema (not just rejecting them at runtime);
- rewrites the tool description so the surface reads as bespoke;
- enforces the boundary in the handler: caller-supplied fixed/rejected
params are refused, denied methods are blocked, and scoped search
queries that could escape the bound context (cross-context qualifiers
or boolean grouping) are rejected.
Membership is an explicit per-mode manifest (fail-closed): a new server
tool is invisible to a scoped surface until it is deliberately admitted.
Wiring: `NewScopedInventory` pre-transforms the universe before the
existing inventory filter pipeline (read-only, feature flags, PAT scopes
still apply); `--repository` / `--pull-request` / `--project` stdio flags
select the scope; the scoped server advertises a bespoke title and
instructions.
Validation: adversarial + singleton-safety unit tests, fail-closed
manifest coverage, and per-surface toolsnaps under
`pkg/binding/__toolsnaps__/{repo,pull_request,project}/` so tool changes
must be re-wired into every surface. The mcp-diff config generator gains
scoped stdio entries so the diff workflow tracks these surfaces too.
Deferred: HTTP scoped roots/middleware, scoped resources + prompts, and a
combined multi-project mode.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scoped tool descriptions previously used generic relative phrasing
("this repository" / "this pull request" / "this project") that never
named the bound resource. Render each manifest description as a
text/template against the bound Context so the advertised surface names
the concrete resource it operates on (e.g. octocat/Hello-World#42),
reinforcing the bespoke, purpose-built feel.
- Add RepoRef/PullRef/ProjectRef helpers to Context.
- Render Description templates in bindTool (missingkey=error, fail loud
on parse/exec error; non-template descriptions pass through unchanged).
- Rewrite repo, pull_request, and project manifest descriptions to name
the resource via RepoRef/PullRef/ProjectRef.
- Regenerate per-surface toolsnaps; add tests locking rendered output
and malformed-template failure.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A spike / prototype (draft, not for merge) exploring context-scoped MCP server modes. The operator binds the server once to a single GitHub context — a repository, a pull request, or a ProjectsV2 project — and the server then presents a bespoke, purpose-built tool surface rather than a reduced copy of the full GitHub server.
The guiding principle is interface-first: a scoped server should look like it was designed for that one context, not like the general server with parameters stripped out.
MCP Server Diff(stdio) is red on this PRThis is expected for the introducing PR and not a head-side bug. The diff action runs each config against both the PR tree and a baseline checkout of
main. The four scoped configs (scope-repository,scope-repository+read-only,scope-pull-request,scope-project) pass the new--repository/--pull-request/--projectflags, which don't exist onmainyet, so the baseline server exits immediately →MCP error -32000: Connection closed.The head server starts cleanly on all four (verified locally: 16 / 10 / 9 / 3 tools with bespoke titles), and the surfaces are locked by per-surface toolsnaps. The diff check resolves automatically once the flags are on
main(i.e. there's a baseline to diff against). The configs are intentionally left wired into the matrix so the surfaces are tracked from then on.How it works
New
pkg/bindingpackage transforms the tool universe for the selected scope. For each admitted tool it:owner,repo,pullNumber,owner_type,project_number) from the advertised input schema and injects the fixed values server-side at call time.methodenum to the operations the scope supports, pruning disallowed values from the advertised schema (not merely rejecting them at runtime) so the schema is an honest description of the surface.Membership is an explicit per-mode manifest (fail-closed): a newly added server tool is invisible to a scoped surface until it is deliberately admitted.
Surfaces (default flags)
--repository owner/repo--pull-request owner/repo#N--project owner_type/owner/NWiring
NewScopedInventorypre-transforms the universe before the existing inventory filter pipeline — read-only, feature flags, and PAT-scope filters still apply on top.--repository/--pull-request/--projectselect the scope.Validation
repo:query qualifier, or a denied method are all rejected; the package-level tool singletons are never mutated).pkg/binding/__toolsnaps__/{repo,pull_request,project}/lock the advertised API per surface, so any tool change must be re-wired into every surface to keep snapshots green.script/lintclean, fullscript/testgreen.Design notes / decisions
add_project_itemaccepts anyitem_owner/item_repobecause ProjectsV2 projects legitimately aggregate issues/PRs from across GitHub. The bound context is the project; cross-repo item references are in-scope by design.{owner,repo}-bound repository reads (get_file_contents,get_commit) for reviewer context — still single-repo confined.Deferred (out of scope for this spike)
/repos/{owner}/{repo},/repos/{owner}/{repo}/pulls/{n},/projects/{owner_type}/{owner}/{n}) + header-based scope config (which would also give the scoped configs an http-headers diff path).🤖 Spike generated with assistance from Copilot. Not intended for merge as-is — opening to review the approach.