Repository navigation
feat(scope): exact raw-name tool identity on every dispatch path (Spec 105 PR A, FR-009) - #1279
Merged
Merged
Conversation
…p map Phase 0 evidence (gap-map.md: 54 gaps adversarially verified against b39800a; prior-diff-assessment.md) and Phase 1 design: nine PRs in a fixed merge order, one effective-scope predicate, identity stamped at production, single refusal constructor per surface, two-fixture differential oracle. research.md records the eleven open decisions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fied gap Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…h/contracts/tasks 14 findings, all verified against spec.md and applied: D3 admins keep today's retrieve path (SC-005), D4 unresolved identity refuses every caller (Edge Case spec.md:107), D5 caller-kind-first per FR-001, D6 score neutrality asserted not assumed with exhaustive as the only fallback, D7 terminal strip filter + protocol-level proof, D8 reuse the existing server field (no admin record change), D10 four operations + merge-base CI gate, D12 whole-envelope refusal parity, D13 only tier leaves the call-time filter, D14 standalone tests before H1; FR-003 URL precedence, FR-005 approval-aware usage ranking, FR-006 unstamped prompts withheld for everyone, FR-009 tables at spec shape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ifacts Round 2 (20 items: 6 stale propagations from round 1, 6 new). Applied: D3 follows spec.md:113 and names the profiled-admin fallback in SC-005; D5 named in SC-005 and data-model order fixed; D4 stale fail-open text removed from plan/contract; D13 tier-first over callability inside one call-time filter; D8 boundary-aware fields parsing (left-to-right first complete JSON object) + producer audit; D9 image-name cleanup fallback covered; D10 merge-base harness supplied from head, missing baseline fails; D15 stages the FR-009 direct table across A and G; T065 env var is a directory; T084 compares the same requested name across fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ifacts Retained-effect assertion mode + fixtures (spec.md:114,161) and the operator-published-content positive controls (spec.md:116) assigned; usage ranking requires population membership AND approval (stale approved record fixture); golden gate narrowed to the frozen files so H1's latency fixtures can land. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ifacts Historical subject-conflict log records (foreign container names, ID-only container records, mis-routed callback records) withheld from scoped readers per FR-007; usage-ranking membership rule and the golden-only gate propagated into plan.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ifacts Container log records need canonical subject evidence (container_owner from the label) — sanitised names collide (a/b ≡ a-b), so every pre-upgrade container record is withheld from scoped readers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d fixtures T001-T003: scope_fixture_test.go (agentCtx/adminCtx/startCountingUpstream), spec.md amendments (6 items from the plan round). T004-T010: 21 tests, 16 red on HEAD by assertion (ns:erase inherits erase's approval, index docID collapse, exact-name record deleted on rediscovery, unresolved identity granted destructive tier, no-record classified ready), 5 regression pins (54-cell retrieve table, direct/nested tables, exact- match permissions, quarantine-disabled control). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… (FR-009) Producers key approval records and index docIDs by the exact raw name; lookupToolApproval: exact record wins, legacy collapsed record only restricts, discovered-but-unrecorded tool under an active quarantine gate is pending; unresolved identity on a known server refused for every caller (D4); pinned pre-105 tests inverted. WIP: critique fixes follow in the next commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…napshot, keep full_tool_name raw, drop the index wipe, carry legacy blocks F1 direct evaluator Discovered=true; F2 unresolved refusal only for a hydrated snapshot; F3 full_tool_name stays the raw name (admin BM25 scores byte-identical to main, pinned); F4 remove the global index rebuild + schema bump (differential self-heals, migration test); F5 legacy collapsed restriction carried onto the new exact record; F6 drop the raw-name alias clause; F7 distinct no-record body; F8 direct reader test; F9 docs + RELEASE_NOTICE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dentityKeyed stamp, discovery marker, exact describe gates R1 adopt a legacy lock onto a toggle-synthesized exact record; R2 carry only a real user block from unstamped siblings (IdentityKeyed on every post-105 write); R3 ToolsDiscovered marker so connected-but-undiscovered servers refuse unresolved names; R4 describe/count gates use the exact pair; R5 distinct no-record body on describe, distinct block reason, nested check without AuthContext. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tityKeyed backfill, disconnected pending, discovery window body Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ce, restricting orphans wait, toggle-minted records pending, supervisor marker cleared, bulk toggle raw names Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…output-schema backfill; restricting legacy records stay consultable across operator writes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s the legacy-consult seam; race-free supervisor marker test Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ration-alias diff keys, per-identity legacy consult, atomic stamp sweep, connection-generation discovery marker P1 schema-only change can no longer auto-revert (also a latent main bug); P2 an old collapsed docID whose raw name is a suffix of a new namespaced key is an alias, not a removal; P3 restricting collapsed records lend their lock per namespaced identity until the operator acts on them; P4 stamp sweep reads and writes inside one bbolt Update; R1 preflight check mode recommends rediscovery for no-record tools; I1 empty rediscovery never certifies a retained tool set; I2 discovery results carry the connection generation and are dropped when stale; I3 the identity gate keys on the live client, not StateView.Connected; I4 reconcile invalidates ToolsDiscovered whenever it observes a disconnect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying mcpproxy-docs with
|
| Latest commit: |
f60c688
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://827b1c47.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://105-agent-scope-hardening.mcpproxy-docs.pages.dev |
…concile in the discovery-marker test Reconcile reads Connected from the adapter, whose ConnectServer the earlier passes dispatched asynchronously; on slow CI runners the goroutine had not run yet and reconcile correctly treated the server as disconnected, clearing the marker the test expected to survive. All four failing CI jobs were this one test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Contributor
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 34945538835 --repo smart-mcp-proxy/mcpproxy-go
|
…witch ApplySecurityConfig on the routing-mode-refresh goroutine wrote engine.deepScanEnabled/deepScanScanners while StartScan read them under e.mu in resolveScanners — a pre-existing data race the Integration Tests job caught on this branch (TestE2E_DirectModeLegacyAliasesFollow- SerializationMode). Writer and out-of-section reader now go through e.mu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… identity-checked describe/preflight, live-client connection epoch C1 stampConsultedLegacySibling uses the conditional in-tx stamp; C2 describe_tool and preflight/check mode reject a known-server unresolved identity like dispatch (search listing untouched); C3 the managed client carries a connection epoch that discovery captures and both identity checks compare against, so a stale generation never passes when the disconnect/reconnect events are delayed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…overed identity; epoch-pinned upstream dispatch D1 a disconnected server keeps its not-connected / reconnect_on_use verdict (the S3 clause protected nothing: no gated path reconnects); D2 the validated connection epoch travels into the managed-client call and is rechecked under the client lock right before the transport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…certified identity; release notice matches the disconnected verdict Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e Connected snapshot for the identity gate Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…vel verdicts derive from one persisted read Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…spatch gate's persisted record Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…de so a replacement client never reuses one Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… from the sandbox preflight through dispatch Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dumbris
marked this pull request as ready for review
September 15, 2026 08:13
This was referenced Sep 16, 2026
Dumbris
added a commit
that referenced
this pull request
Sep 20, 2026
…main (#1324) CHANGELOG.md's Unreleased section was missing entries for 9 recent behavior-changing commits, most notably the Spec 105 scope-hardening security fixes (#1283/#1284/#1285) and Spec 107 SSO features (#1293/#1296). roadmap.yaml had drifted stale in both directions for two in-flight specs: - Spec 105 (agent-scope-hardening): 4 merged PRs (A #1279, B #1282, D #1283, E #1284) still showed status: todo, and PR H0 (#1285) had no roadmap task entry at all. Flipped to done; genuinely outstanding work (PR C/F/G/H1) is untouched. - Spec 107 (sso): PR-D (#1296) is merged but the epic and its task still read in_review. Verified all 87 files PR-D touched exist on disk (internal/audit/*, docs/features/audit-log.md, config, docs, frontend) and that verification.md records a completed multi-round cross-model review and a passing SC-009 benchmark, then ticked the 20 checkbox-lag tasks (T096-T115, T118-T119) in tasks.md and flipped both the task and epic status to done. Regenerated ROADMAP.md via scripts/gen-roadmap.py.
Dumbris
added a commit
that referenced
this pull request
Sep 21, 2026
…ng holds (Spec 105 PR H1) (#1332) Spec 105 PR H1 — `scope-regression-suite` part 2 (FR-011/013/014, SC-007, gaps FR01x-G4..G7). Closes tasks T107-T116. This is the FINAL phase of the nine-PR agent-scope-hardening epic (A, B, D, E, H0, C, F, G already merged). ## Changes - **Two-fixture differential oracle** (`internal/server/scope_differential_test.go`, new): `newScopeFixture`/`runScopeScenario`/`normalizeScopeResponse` per contracts/differential-oracle.md, plus `TestScopeCoverage_EveryUserStoryScenario` registering every US1.1-1.8/US2.1-2.6/US3.1-3.4 scenario by id — some directly via the general three-server harness, most by re-running each prior PR's own dedicated differential fixture (plan.md: "H1 re-registers them by User Story id"). - **Pinned-reversal grep guard** (`internal/server/scope_pinned_reversal_guard_test.go`, new): `TestScopePinnedReversalsStayInverted` proves none of the four pre-105 insecure assertions gap-map.md's FR01x-G7 entry names have crept back to their original form, traced to their exact pre/post-105 diffs. - **Retained-effect fixtures** (`internal/server/scope_retained_effects_test.go`, new): real reproductions of 5 of 7 SC-001-excluded effects (shared-limiter contention, cross-server scan admission via the actual shadowing.cross_server impersonation-clone mechanism, prompt-name collision, global prompt cap, direct display-name collision), 1 via a companion package (`internal/upstream/manager_prompts_deadline_test.go`, new — shared vs independent prompt-refresh deadline, two discriminating regimes), 1 cited cross-package (log rotation). - **HTTP credential matrix** (`internal/server/scope_http_matrix_test.go`, new): real minted tokens over real loopback HTTP through mcpAuthMiddleware, covering every FR-014 applicability-matrix row at least once plus a content-level (raw wire-text) non-disclosure proof for retrieve_tools. - **Latency harness finalized** (`internal/server/scope_latency_test.go`): all four FR-011 operations (retrieve_tools, read_cache, prompts/list, tools/list) with real workloads, plus `.github/workflows/scope-latency.yml` (new) and `cmd/scope-latency-compare` (new, stdlib-only) for the merge-base p95 regression gate. - **Bug fix found via this suite**: `internal/logs.ReadUpstreamServerLogTail`/ `ReadUpstreamServerLogTailAttributed` dereferenced a nil `*config.LogConfig` — fixed with a TDD red/green pair (`internal/logs/nil_log_config_test.go`). - Docs and roadmap sync: `docs/features/agent-tokens.md` invariant wording finalized; `roadmap.yaml`'s `scope-refusal-shapes` task (stale `todo` despite PR G #1328 already merged) flipped to `done`. `scope-regression-suite` and the epic-level status are deliberately left for the maintainer to flip on merge. ## Testing - `go build`/`go vet` (bare + `-tags server`): clean - Full `-race` suite across internal/server, internal/upstream, internal/cache, internal/logs, cmd/scope-latency-compare: green - `golangci-lint --new-from-rev=origin/main` (bare + `--build-tags server`): 0 new issues - `./scripts/test-api-e2e.sh`: 70/70 passed - Cross-model review: opencode terra/sol/astra confirmed quota-exhausted; fell back to `codex exec --model gpt-5.6-sol`. 4 rounds, every finding verified genuine before fixing (non-vacuous confirmed by temporarily reverting the underlying guard and observing red). `VERDICT: clean` (round 4) — full history in tasks.md T116. Related #1223 #1224 #1225 #1226 #1227 #1279 #1282 #1283 #1284 #1285 #1325 #1326 #1328
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.
Summary
Spec 105 PR A — roadmap task
scope-target-identity-producers(FR-009). Every approval record, search-index entry and policy gate is now keyed by the exact raw tool name its server reports (ns:eraseisns:erase, nevererase), on all three dispatch paths (call_tool_*,/mcp/alldirect, nestedcode_execution), preflight anddescribe_tool. An unresolved or stale identity on a connected, discovered server is refused before any upstream call — for every caller, administrators included (spec Edge Case, research D4). A discovered tool with no approval record under an active quarantine gate is pending, never ready.Also lands the Spec 105 planning artifacts (plan / research / data-model / contracts / tasks / gap-map, 6 astra plan-review rounds → CLEAN) and the six spec-text amendments from that round.
Gaps closed (gap-map §1)
FR009-G1
ns:eraseinheritederase's approval · G2 index docID collapse + exact-name record deleted on rediscovery · G3 direct/preflight readers used the exact key only · G4 unresolved identity granted the destructive tier · G5 no-record classified ready · G6 acceptance tables (retrieve 54 cells + direct + nested) · G7 docs (exact-match permissions).What changed
internal/runtime/tool_quarantine.go,lifecycle.go):checkToolApprovalsandapplyDifferentialToolUpdatekey byRawName; an old collapsed docID whose raw name is a suffix of a new namespaced key is a migration alias, not a removal;IdentityKeyedstamp on every post-105 write; legacy collapsed records lend a user block / pending / changed lock to the namespaced tool once per identity until the operator acts on them; never-baselined (toggle-synthesized) exact records adopt the legacy lock or baseline only against a matching contract; the changed-record revert predicate compares the whole contract (closes a latentmainbug: a schema-only rug-pull auto-approved on the second pass).internal/server/tool_gate.go,mcp_direct_callability.go,preflight_glue.go,internal/preflight): exact record wins; no record for a discovered tool under an active gate ⇒ pending; the direct catalog is the direct discovery snapshot; preflight check mode recommends rediscovery for record-less tools.internal/server/mcp.go,mcp_code_execution.go,jsruntime):toolIdentity{ServerKnown, SnapshotHydrated, DiscoveryDone, Found}; refusal fires only for a connected, discovered server (live client, not a stale StateView flag);ToolsDiscoveredis per connection generation (stamped by discovery incl. zero-tool results, cleared on disconnect and by reconcile, stale generations dropped at publish).internal/index/bleve.go): docID =server:rawName;full_tool_namestays the raw name so admin BM25 scores are byte-identical tomain(pinned byTestBleveIndex_RawShapedExactNameScoresUnchanged); identity derived fromhit.IDon read; no global index rebuild — the differential self-heals pre-upgrade docs.docs/features/agent-tokens.md(Target tool tier),docs/features/security-quarantine.md(Namespaced tool names),.github/RELEASE_NOTICE.md(one-time pending after upgrade).Tests inverted (never deleted)
TestLookupToolPermission_UnresolvedMetadataIsDestructive→…UndiscoveredOnKnownServerIsUnresolved;TestCallToolRead_UndiscoveredTool_RequiresDestructiveTier→…RefusedForEveryCaller;TestToolGate_StaleExactApprovalCannotShadowCollapsedChange→…RugPullOnExactRecord_StaleLegacyApprovalCannotShadowIt; merge-reader cells inTestToolGate_LegacyCollapsedApprovalRecord_StillGates,…MergedApprovalRecordsKeepIndependentLocks,TestLookupToolApproval_ReadsBothKeysFromOneSnapshot;TestSetToolEnabled_CreatesRecordWhenMissing(active gate ⇒ pending). Admin controls kept green.SC-005 admin changes (all named exceptions)
Config-denied / unapproved
a:ns:eraserefused for admins;eraseandns:erasedistinct index entries; unresolved identity on a connected discovered server refused; container-housekeeping records unchanged in this PR. Fourretrieve_full_*goldens andtoolslist_goldensbyte-identical.Verification
go test -raceon runtime/index/preflight/jsruntime/config/upstream/storage/security,internal/server(CI skip regex),-tags serverserveredition — green../scripts/test-api-e2e.sh— 65/65.go vetclean; goldens unchanged.golangci-lintv2 could not run locally (binary built with go1.25 refuses the go 1.26 target) — relying on CI.github-copilot/gpt-6-astra, four file-named chunks: index CLEAN; producers 4, identity gate 4, readers 1 (all confirmed; one was a latentmainbug: a schema-only rug-pull auto-approved on the second pass).gpt-5.6-sol(Copilot monthly quota exhausted after round 1; switched on maintainer direction). Producers CLEAN at r3, readers CLEAN at r4, identity gate CLEAN at r10. Rounds 3/5/6/7/9 were one defect class — a cache-derived identity refusal pre-empting an authoritative server-level verdict during StateView lag — closed structurally: one persisted read per dispatch (gate → identity → live certification → epoch-pinned upstream call), a live client overrules the snapshot's connection flag, and process-wide connection epochs.TestSupervisor_ToolsDiscoveredMarkerreading the mock before an async connect on slow runners (fixed test-side), plus a pre-existing scannerSetDeepScandata race the Integration job caught (fixed with the engine lock); ahdiutil: Resource busyDMG flake was re-run.Follow-ups / Spec 105 gaps
POST /api/v1/tool-calls/{id}/replaydispatches with no tool gate (pre-existing, REST is outside Spec 105's stated scope) — recorded in gap-map §8.PUT /api/v1/profiles/active) bounds no MCP session today — noted for the scope-management spec.🤖 Generated with Claude Code