feat(mcp): add custom comment minimize tool#45
Conversation
This comment has been minimized.
This comment has been minimized.
chaodu-agent
left a comment
There was a problem hiding this comment.
Important
CHANGES REQUESTED
Consolidated review: #45 (comment)
GitHub event: COMMENT - self-review delivery only; this is not an approval.
|
Important CHANGES REQUESTED What This PR DoesThis PR adds the first ghpool-owned MCP operation, How It WorksThe proxy applies authentication, the agent tool allowlist, write classification, repository policy, credential routing, in-flight limits, and fail-closed audit before local dispatch. The handler verifies the current GitHub App identity and the target comment's repository, then calls Findings
Finding Details🟡 F1: Preserve the complete SSE envelope and fail closedThe current parser retains only the last accumulated data payload, and the serializer emits Requested change: Parse the SSE event sequence, modify only the JSON-RPC 🟡 F2: Bound custom discovery resourcesThe custom discovery branch runs before the write in-flight guard and performs buffering, JSON parsing, and serialization. Multiple parallel requests multiply those allocations, and checking the cap after Requested change: Add a bounded per-agent or global semaphore/aggregate budget, enforce the cap before appending or split safely, and add concurrent-request and oversized-chunk tests while preserving streaming for non-custom traffic. 🟡 F3: Add handler-level GraphQL coverageHelper tests cover schema creation, allowlist filtering, headers, and SSE assembly, but not the local handler's argument failures, classifier rejection, ownership failure, repository mismatch Requested change: Add mock GraphQL integration tests for the success path and representative fail-closed paths, including repository mismatch and invalid arguments. 🟡 F4: Share the SSE outcome parser
Requested change: Move the parser to a shared module or make 🟡 F5: Define the GraphQL endpoint boundaryThe endpoint is a source-level constant rather than a configured GitHub API base. If github.com-only deployment is intentional, document that constraint; otherwise derive the endpoint from configuration and add coverage for the selected base. Requested change: Make the deployment scope explicit or add a configurable endpoint without weakening credential scoping. 🟡 F6: Bound the end-to-end timeoutThe two GraphQL requests each receive the full POST timeout, while the in-flight guard spans the entire local handler. A single end-to-end deadline or shorter verification budget would prevent stalled ownership checks from consuming all write slots for an excessive interval. Requested change: Apply one total deadline across verification and mutation, or use a materially shorter verification timeout, and test slot release after timeout. 🟡 F7: Keep the MVP boundary maintainableThe narrow MVP framing and explicit invariants are useful, and a general registry is not required for this one tool. However, if another ghpool-owned tool is added, the current duplicated interception, discovery, and dispatch paths should be consolidated before expanding the surface. Requested change: Track a follow-up to introduce a small registry/dispatch abstraction only when a second custom tool is accepted; do not broaden this PR speculatively. Addressing Review Feedback
Baseline Check
Validation
What's Good (🟢)
5. Three Reasons We Might Not Need This PR
|
chaodu-agent
left a comment
There was a problem hiding this comment.
Important
CHANGES REQUESTED
Consolidated review: #45 (comment)
GitHub event: COMMENT - self-review delivery only; this is not an approval.
| } | ||
| let encoded = serde_json::to_string(&json).ok()?; | ||
| if is_sse { | ||
| Some(format!("event: message\ndata: {}\n\n", encoded).into_bytes()) |
There was a problem hiding this comment.
🟡 F1 - Preserve the complete SSE envelope and fail closed
This serializer emits one synthetic event and drops preceding/following events plus fields such as id, retry, comments, and extension fields. The parse-failure and overflow paths can also forward an unfiltered catalog.
Requested change: Parse the event sequence, modify only the matching tools/list response, preserve all other events and metadata, and fail closed when filtering cannot be completed. Add multi-event, unmatched-ID, malformed, and overflow tests.
| .get("content-type") | ||
| .and_then(|v| v.to_str().ok()) | ||
| .map(str::to_string); | ||
| match buffer_body(resp, MAX_BODY_BYTES).await { |
There was a problem hiding this comment.
🟡 F2 - Bound custom discovery resources
This custom discovery branch buffers, parses, and serializes responses before the write in-flight guard. Parallel requests multiply allocations, and the cap is checked after appending a chunk.
Requested change: Add a bounded semaphore or aggregate budget, enforce the cap before appending or split safely, and add concurrent and oversized-chunk tests.
| ); | ||
| } | ||
|
|
||
| let verify_payload = serde_json::json!({ |
There was a problem hiding this comment.
🟡 F3 - Add handler-level GraphQL coverage
The security-sensitive handler now performs argument, ownership, and repository checks, but the test suite does not exercise the verify-plus-mutate success path or representative fail-closed responses.
Requested change: Add mock GraphQL integration tests for success, repository mismatch, ownership/GraphQL errors, invalid arguments, and classifier rejection.
| /// ghpool-owned MCP tools are namespaced so they cannot collide with tools | ||
| /// exposed by GitHub's hosted MCP server. | ||
| const MINIMIZE_COMMENT_TOOL: &str = "ghpool_review_minimize_comment"; | ||
| const GITHUB_GRAPHQL_URL: &str = "https://api.github.com/graphql"; |
There was a problem hiding this comment.
🟡 F5 - Define the GraphQL endpoint boundary
The custom operation is tied to the github.com GraphQL endpoint while the proxy has configurable upstream behavior. GHES/private deployments cannot use this operation without code changes.
Requested change: Make github.com-only scope explicit, or derive the endpoint from configuration and test the selected base.
| .header("user-agent", concat!("ghpool/", env!("CARGO_PKG_VERSION"))) | ||
| .header("content-type", "application/json") | ||
| .json(payload) | ||
| .timeout(std::time::Duration::from_secs(POST_TIMEOUT_SECS)) |
There was a problem hiding this comment.
🟡 F6 - Bound the end-to-end timeout
Verification and mutation each receive the full POST timeout while the in-flight guard spans the local handler. Two stalled requests can therefore hold one write slot for nearly twice the configured timeout.
Requested change: Apply one total deadline across both requests or use a shorter verification budget, and add a timeout test proving slot release.
…aphQL testable
The ownership gate compared GraphQL viewer.login to the comment
author.login with strict equality. Empirically (verified against
api.github.com with a real installation token): viewer for an App
installation token is '<app-slug>[bot]' while Bot-authored comments
carry the bare '<app-slug>' author login — the check could never
pass for the App's own comments, i.e. the only comments the tool is
allowed to minimize. The feature was unusable and untested because
the GraphQL endpoint was a hard-coded const.
- normalize_actor() strips the '[bot]' suffix on both sides of the
comparison
- execute_graphql/handle_minimize_comment take the GraphQL URL as a
parameter (production callsite passes the api.github.com const)
- handler-level tests against a mock GraphQL server:
- App-bot-authored comment succeeds (viewer 'oab-ghpool[bot]' vs
author 'oab-ghpool' — the exact live API shape), mutation payload
asserted
- human-authored comment refused, zero mutations
- node in a different repository than the policy-checked owner/repo
refused (403), zero mutations
- GraphQL soft errors (HTTP 200 + errors[]) fail closed; unknown
classifier rejected before any network call
- drop the contradictory required+default classifier in the tool
schema (classifier is required)
Tests: ghpool 112 passed, clippy -D warnings clean.
|
Important CHANGES REQUESTED What This PR DoesAdds the first ghpool-owned MCP tool, Findings
Fixes (e43fb9c)
Verified
5️⃣ Three Reasons We Might Not Need This PR
The slice is appropriately narrow. With the identity fix and handler coverage in place, this is ready. |
Summary
Partially addresses #44 by adding the first ghpool-owned review operation:
ghpool_review_minimize_comment.What changed
tools/listonly when an authenticated agent explicitly allowlists it.minimizeCommentGraphQL mutation with the existing scoped MCP credential.ghpool_*tools out of the upstreamX-MCP-Toolsheader.Security and compatibility
Validation
git diff --checkpassed.cargo/rustc; GitHub CI is expected to runcargo check,cargo clippy -- -D warnings, andcargo test.This PR intentionally keeps the first slice narrow; pending operations from #44 (restore, pending-review deletion/submission, and commit status) can follow the same custom-tool contract.