Skip to content

feat(connect): stat-only status + on-demand content reads (US1, MCP-2829)#706

Merged
Dumbris merged 1 commit into
mainfrom
075-us1-stat-only-connect
Jun 17, 2026
Merged

feat(connect): stat-only status + on-demand content reads (US1, MCP-2829)#706
Dumbris merged 1 commit into
mainfrom
075-us1-stat-only-connect

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 17, 2026

Copy link
Copy Markdown
Member

Summary

Spec 075 US1 (MVP) — kills the macOS "wants to access data from other apps" App-Data prompt storm by making Connect's overall status determine installed-state via os.Stat metadata only, deferring config content reads to explicit per-client actions.

Related #696 · Spec: specs/075-macos-tcc-connect/ (US1, FR-001/FR-002, SC-001/SC-002) · Tasks T003–T012.

What changed

  • Content-read seam (T003): Service.readFile (default os.ReadFile) + NewServiceWithReader/setReadFile, mirroring the existing homeDir override, so tests inject reads/denials without a real OS denial.
  • Additive ClientStatus fields (T007, FR-006): access_state (json:"access_state") and remediation (json:"remediation,omitempty"). No existing field removed/repurposed.
  • GetAllStatus is now content-read-free (T008): existence via os.Stat; installed clients report access_state="unknown", connected=false. The eager findEntry call is removed.
  • GetStatus(clientID) (T009): single on-demand content read via the seam; resolves Connected + access_state ∈ {accessible,absent,malformed}. (denied is US2.)
  • Seam coverage (T010): all content reads — readOrCreate*, disconnect*, verifyJSONEntry, and the entry-finders (now byte-parsers) — route through s.read.
  • Lazy connected truth (T011): GetConnectedCount/GetConnectedIDs re-implemented to read per-client on demand.
  • CLI preserved: mcpproxy connect resolves Connected on demand (explicit user action) so the CONNECTED column is unchanged.
  • Docs (ENG-9): docs/api/rest-api.md documents the GET /api/v1/connect stat-only behavior + additive fields.

Testing (T012)

  • New TestGetAllStatus_NoContentReads (injected reader fails if called → asserts 0 content reads) and TestGetStatus_ReadsSingleClientOnDemand (asserts exactly 1 read), plus absent / malformed / unknown-client cases.
  • Migrated the three GetAllStatus().Connected assertions to the on-demand GetStatus path (behavior change is by design per the spec).
  • go test ./internal/connect/... ./internal/httpapi/... -race ✅ · golangci-lint v2 (CI config) 0 issues ✅ · build personal + -tags server ✅ · ./scripts/test-api-e2e.sh 65/65

Scope

US1 only. US2 (denied classification + remediation surfacing) and US3 (doctor check) are separate issues; the access_state/remediation fields and the seam are forward-compatible with them. A permission-denied read currently maps to access_state="unknown" (US2 refines it to denied).

Related #696

Spec 075 US1. Connect overall status (GetAllStatus) now determines each
client's installed state via os.Stat metadata only and performs ZERO config
content reads, eliminating the macOS "wants to access data from other apps"
privacy prompt storm when simply viewing status (FR-001, SC-001/SC-002).

Reading a client's config contents to detect whether mcpproxy is registered
now happens only on an explicit per-client action via the new GetStatus(id),
which resolves Connected + AccessState (FR-002).

## Changes
- Add a content-read seam: Service.readFile (default os.ReadFile) +
  NewServiceWithReader / setReadFile so tests inject reads/denials.
- ClientStatus gains additive access_state + remediation fields (FR-006).
- GetAllStatus: metadata-only existence; AccessState="unknown", Connected=false
  for installed clients (eager findEntry removed).
- GetStatus(clientID): single on-demand content read via the seam; resolves
  accessible/absent/malformed and Connected.
- Route all content reads (readOrCreate*, disconnect*, verifyJSONEntry,
  entry-finders) through the seam; entry-finders parse bytes only.
- GetConnectedCount/GetConnectedIDs re-implemented to read lazily per client.
- CLI `mcpproxy connect` resolves Connected on demand (explicit action),
  preserving the CONNECTED column.

## Testing
- New TestGetAllStatus_NoContentReads (reader asserts 0 calls),
  TestGetStatus_ReadsSingleClientOnDemand (exactly 1 read), plus absent /
  malformed / unknown-client cases.
- Migrated GetAllStatus connected-detection tests to GetStatus.
- go test ./internal/connect/... ./internal/httpapi/... -race green;
  golangci-lint v2 clean; build (personal+server) ok; API E2E 65/65 pass.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 27de6cb
Status: ✅  Deploy successful!
Preview URL: https://5ad2b565.mcpproxy-docs.pages.dev
Branch Preview URL: https://075-us1-stat-only-connect.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 61.76471% with 39 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/connect/connect.go 64.28% 32 Missing and 3 partials ⚠️
cmd/mcpproxy/connect_cmd.go 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: 075-us1-stat-only-connect

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (25 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27713068786 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@mcpproxy-gatekeeper mcpproxy-gatekeeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Claude Code review (Codex quota exhausted).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants