diff --git a/docs/current-status-card.md b/docs/current-status-card.md index 2bf1672..bb74c69 100644 --- a/docs/current-status-card.md +++ b/docs/current-status-card.md @@ -14,21 +14,22 @@ Remote base before local roadmap commits: `b2d839e Merge branch 'codex/n15-repor ## Current completed state - Go MCP foundation exists: `cmd/isphere-mcp`, `internal/mcpserver`, `internal/helperclient`, and `internal/tools` build a stdio MCP server. -- The current MCP surface exposes only four read-only helper tools: +- The current MCP surface exposes five read-oriented tools: - `win_helper_version` - `win_helper_self_check` - `win_helper_scan_windows` - `win_helper_dump_uia` + - `isphere_receive_messages` - C# `ISphereWinHelper` exists under `native/ISphereWinHelper` and uses the `isphere.helper.v1` stdin/stdout JSON contract. -- `scripts/verify-win-helper.ps1` and `scripts/verify-go-mcp.ps1` provide repeatable local checks for the helper and first-phase Go MCP server. +- `scripts/verify-win-helper.ps1` and `scripts/verify-go-mcp.ps1` provide repeatable local checks for the helper, five-tool MCP surface, and empty-source `isphere_receive_messages` smoke call. - N12-pre and N12R documents define safe offline evidence intake and internal-sandbox live UIA capture procedures. - N13/N14/N15 produced selector catalog/report validation infrastructure, but those are supporting validation assets only. ## What cannot be claimed externally -- The project has not yet implemented business MCP tools for contact search, group search, message receiving, message sending, file receiving, or file sending. +- The project has not yet implemented business MCP tools for contact search, group search, message sending, file receiving, or file sending; `isphere_receive_messages` is registered but still lacks a production loader/configuration path. - The project has not completed production iSphere integration. -- The project has not proven a stable real message source for the full business workflow. +- The project has not proven a stable real message source for the full business workflow beyond decrypted PacketReader log evidence and synthetic/redacted tests. - The project has not implemented real message sending or real file upload. - N12-pre offline evidence is not N12 pass. - N13/N14/N15 selector/report work is pre-business evidence, not finished contact/message/file capability. @@ -38,8 +39,8 @@ Remote base before local roadmap commits: `b2d839e Merge branch 'codex/n15-repor - The repository has a Go MCP + C# WinHelper foundation for controlled Windows-side read-only observation. - The helper protocol boundary is explicit and keeps Windows/UIA work separate from the Go MCP service layer. - The current code can support future MCP business tools by routing low-level Windows/UIA fallback work through a bounded helper layer. -- The project has repeatable verification scripts for the first-phase helper/MCP foundation. -- The project now has a corrected business contract and plan for core MCP communication tools. +- The project has repeatable verification scripts for the helper/MCP foundation and the current five-tool surface. +- The project now has a corrected business contract and plan for core MCP communication tools, plus a registered first read tool for receiving messages from a future configured source. ## N13/N14/N15 positioning @@ -47,7 +48,7 @@ N13/N14/N15 are pre-business validation results. They can help identify UI eleme ## Current loop -Current loop: `Stage C / Loop C1 - log decryptor and PacketReader parser`. +Current loop: `Stage C / Loop C4 - operator verification and docs`. Plan file: `docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md`. @@ -58,18 +59,17 @@ Loop rule: 3. Verify, commit exact paths, then update the next loop based on actual results. 4. Continue automatically while the next step is clear; ask the user only when blocked or when business direction changes. -Current loop output: +Current loop output so far: -1. `internal/isphere/logcodec.DecryptLine` for DES/CBC/PKCS7 Base64 log lines. -2. `internal/isphere/packetlog.ParseMessageLog` for redacted `PacketReader.ProcessPacket` XMPP `` stanzas. -3. Passing unit tests with synthetic/redacted fixtures only. -4. Updated Loop C2 plan before source abstraction starts. +1. C1: `internal/isphere/logcodec.DecryptLine` and `internal/isphere/packetlog.ParseMessageLog` for redacted PacketReader message logs. +2. C2: `internal/isphere.EncryptedPacketLogSource.ReceiveMessages` source abstraction with synthetic/redacted encrypted fixtures. +3. C3: `isphere_receive_messages` MCP tool registration and server tool list update. +4. C4: updating repeatable operator verification and docs for the five-tool surface. ## Next business mainline -1. Complete Stage C Loop C1: log decryptor + PacketReader parser. -2. Rewrite and execute Loop C2: log-backed `ReceiveMessages` source abstraction. -3. Register `isphere_receive_messages` only after parser/source tests pass. -4. Add contact/group/file read tools after their source decisions are recorded. -5. Add `isphere_send_message` and `isphere_send_file` after source discovery and connector selection. -6. Keep UIA selector/report work as fallback support, not as the primary roadmap. +1. Finish Stage C Loop C4: verify and document the five-tool MCP surface. +2. Implement the next loop: safe configurable source loader or fixture path for `isphere_receive_messages`, without committing raw evidence. +3. Add contact/group/file read tools after their source decisions are recorded. +4. Add `isphere_send_message` and `isphere_send_file` after source discovery and connector selection. +5. Keep UIA selector/report work as fallback support, not as the primary roadmap. diff --git a/docs/go-mcp-runbook.md b/docs/go-mcp-runbook.md index 74db134..3309840 100644 --- a/docs/go-mcp-runbook.md +++ b/docs/go-mcp-runbook.md @@ -2,7 +2,7 @@ This runbook is for the first Go MCP phase of `isphere-ai-bridge`. It lets an operator build and verify the Windows helper and the Go MCP server without reading the source code. -Current scope: expose four WinHelper observation operations through Go MCP. Business contact/group/message/file tools are defined in the core MCP contract and implemented in later stages. +Current scope: expose four WinHelper observation operations plus the first log-backed business read tool, `isphere_receive_messages`, through Go MCP. The receive tool is registered with an empty default source until a safe evidence loader is wired. ## 1. Prerequisites @@ -90,10 +90,11 @@ The verification confirms: - C# helper can be built. - Go MCP binary can be built. - MCP initialize/list/call flow works through the SDK harness. -- Exactly four tools are exposed. +- Exactly five tools are exposed: four WinHelper observation tools plus `isphere_receive_messages`. - `win_helper_version` returns `ISphereWinHelper`. -- Verification uses synthetic/local helper checks. -- Message/file/search business tools are covered by the core MCP contract, not this runbook phase. +- `isphere_receive_messages` is callable and returns an empty `messages` array from the default empty source. +- Verification uses synthetic/local helper checks and does not load raw N12-pre evidence. +- Contact/group/file/search/send business tools remain later-stage work. ## 6. Configure MCP client command @@ -132,14 +133,15 @@ E:\coding\codex\isphere-ai-bridge\isphere-mcp.exe ## 7. Allowed tools -Only these four tools are allowed in the first phase: +These five tools are currently allowed: -| Tool | Helper op | Purpose | +| Tool | Source/op | Purpose | | --- | --- | --- | -| `win_helper_version` | `version` | Read helper version and protocol metadata. | -| `win_helper_self_check` | `self_check` | Read desktop/UIA availability status. | -| `win_helper_scan_windows` | `scan_windows` | Read visible window metadata or likely iSphere candidates. | -| `win_helper_dump_uia` | `dump_uia` | Read a UI Automation tree for a specified window handle. | +| `win_helper_version` | helper `version` | Read helper version and protocol metadata. | +| `win_helper_self_check` | helper `self_check` | Read desktop/UIA availability status. | +| `win_helper_scan_windows` | helper `scan_windows` | Read visible window metadata or likely iSphere candidates. | +| `win_helper_dump_uia` | helper `dump_uia` | Read a UI Automation tree for a specified window handle. | +| `isphere_receive_messages` | log-backed `EncryptedPacketLogSource` | Read normalized messages from the configured PacketReader log source. The default server source is empty until a loader is wired. | Allowed parameters: @@ -147,15 +149,19 @@ Allowed parameters: - `win_helper_self_check`: zero business parameters. - `win_helper_scan_windows`: `include_all_visible` only. - `win_helper_dump_uia`: `hwnd`, `max_depth`, `include_text`, `max_children` only. +- `isphere_receive_messages`: `limit` only. ## 8. Current tool surface and next-stage route -The current runbook verifies four WinHelper observation tools: +The current runbook verifies five tools: - `win_helper_version` - `win_helper_self_check` - `win_helper_scan_windows` - `win_helper_dump_uia` +- `isphere_receive_messages` + +`isphere_receive_messages` currently has parser/source/tool registration and an empty default server source. Raw N12-pre evidence remains under ignored `runs/` paths and is not committed. The next implementation slice should wire a safe configurable loader or fixture path before claiming production message ingestion. Core business tools for contacts, groups, messages, and files are defined in `docs/mcp-core-tools-contract.md`. Send/file capabilities enter the roadmap through that contract after source discovery, connector selection, preview metadata, execution metadata, and audit records are in place. @@ -207,7 +213,7 @@ iSphere is internal-network only. If the current repository environment is outsi 1. **N12-pre offline evidence** for copied files and static analysis. Follow `docs/offline-evidence-intake-plan.md` and place packages under `runs\offline-evidence-intake\\`. 2. **N12R internal-sandbox live capture** for real logged-in UIA evidence. Follow `docs/internal-sandbox-live-capture-plan.md` and `docs/internal-sandbox-operator-runbook.md`; place returned packages under `runs\internal-sandbox-live-capture\\`. -N12R is the active live-capture route when login is only possible in an internal-network test sandbox. It still allows only the four first-phase read-only tools: +N12R is the active live-capture route when login is only possible in an internal-network test sandbox. The UIA capture procedure still uses only the four helper observation tools: ```text win_helper_version @@ -216,6 +222,8 @@ win_helper_scan_windows win_helper_dump_uia ``` +The MCP server also registers `isphere_receive_messages`, but its default source is empty and it should not be used as N12R UIA evidence collection. + After receiving an N12R package, validate it with `docs/n12r-return-package-validation-checklist.md` before using it as evidence for selector-design review. Prefer the scripted read-only validator: ```powershell diff --git a/docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md b/docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md index b896234..86899c8 100644 --- a/docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md +++ b/docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md @@ -725,7 +725,7 @@ Implemented and verified in this loop: - Default `isphere_receive_messages` source: `isphere.EncryptedPacketLogSource{}` with no configured lines, so the operator smoke call should return `messages: []` until a real evidence loader is wired. - Verification script should still call `win_helper_version`, and should additionally call `isphere_receive_messages` with a small `limit` and assert the returned `messages` field is an array. -- [ ] **Step 1: Capture current verification red state** +- [x] **Step 1: Capture current verification red state** Run before editing the script: @@ -735,15 +735,15 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/verify-go-mcp.ps1 Expected: FAIL because the existing harness still expects exactly four tools and treats `receive` as a forbidden term. -- [ ] **Step 2: Update `scripts/verify-go-mcp.ps1`** +- [x] **Step 2: Update `scripts/verify-go-mcp.ps1`** Change expected tools to five, remove `receive` from the forbidden write/scope terms, and add an SDK call to `isphere_receive_messages` with `{ "limit": 5 }`. Assert `messages` exists and is an array; for the default empty source, assert the smoke result has zero messages. -- [ ] **Step 3: Update operator docs** +- [x] **Step 3: Update operator docs** Update `docs/go-mcp-runbook.md` and `docs/current-status-card.md` so they describe the current five-tool surface, the empty default read source, and the fact that raw N12-pre evidence still stays under ignored `runs/` paths. -- [ ] **Step 4: Run full verification** +- [x] **Step 4: Run full verification** Run: @@ -756,12 +756,86 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/verify-go-mcp.ps1 Expected: all exit 0. Remove any generated root `isphere-mcp.exe` after build. -- [ ] **Step 5: Commit Loop C4 and rewrite next loop** +- [x] **Step 5: Commit Loop C4 and rewrite next loop** Commit exact C4 paths only. Rewrite the next loop around the remaining gap: a safe configurable source loader or fixture path for `isphere_receive_messages`, without committing raw evidence. --- +## Loop C4 Result + +Implemented and verified in this loop: + +- `scripts/verify-go-mcp.ps1` now expects five tools and treats `isphere_receive_messages` as an allowed read capability rather than a forbidden action term. +- The SDK harness still verifies `win_helper_version`, and now also calls `isphere_receive_messages` with `{ "limit": 5 }`. +- Default server wiring returns `messages: []` for `isphere_receive_messages`, proving the tool is callable without loading raw evidence. +- `docs/go-mcp-runbook.md` and `docs/current-status-card.md` now describe the five-tool surface and the empty default source behavior. +- Verification completed for C4 with `git diff --check`, `go test ./...`, `go build ./cmd/isphere-mcp`, and `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/verify-go-mcp.ps1`; the generated root `isphere-mcp.exe` build artifact was removed. + +--- + +## Loop C5: Safe configurable PacketReader log source loader + +**Goal:** Add a safe configuration path so `isphere_receive_messages` can read encrypted PacketReader log lines from a local operator-supplied file without committing raw evidence and while preserving the empty default source. + +**Planned files:** +- Create: `internal/isphere/file_source.go` +- Create: `internal/isphere/file_source_test.go` +- Modify: `internal/mcpserver/server.go` +- Modify: `internal/mcpserver/server_test.go` +- Modify: `cmd/isphere-mcp/main.go` +- Modify: `scripts/verify-go-mcp.ps1` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` +- Modify: `docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md` + +**Planned interface:** +- Environment variable: `ISPHERE_PACKET_LOG_FILE`. +- Default behavior: env var unset means `isphere_receive_messages` remains an empty-source read tool returning `messages: []`. +- Configured behavior: env var set to a local text file means the server reads non-empty encrypted Base64 lines from that file into `isphere.EncryptedPacketLogSource{Lines: lines}`. +- Evidence rule: the configured file should live under ignored `runs/` or another operator-local path; the loader must not require or commit raw evidence. + +- [ ] **Step 1: Write failing file loader test** + +Create `internal/isphere/file_source_test.go` first and run: + +```powershell +go test ./internal/isphere -run TestLoadEncryptedPacketLogSourceFromFileReadsNonEmptyLines -v +``` + +Expected initial failure: loader function/type does not exist. + +- [ ] **Step 2: Implement file loader** + +Implement a small loader that reads a UTF-8 text file, trims whitespace, skips blank lines, and returns `EncryptedPacketLogSource{Lines: lines}`. It should return clear path/context errors for missing or unreadable files. + +- [ ] **Step 3: Write failing env-wired server test** + +Update `internal/mcpserver/server_test.go` with a temp encrypted fixture file and a test that sets `ISPHERE_PACKET_LOG_FILE`, constructs the env-wired server, calls `isphere_receive_messages`, and expects one redacted message. Run the specific test before implementation. + +- [ ] **Step 4: Implement env-wired server construction** + +Add `NewServerFromEnv()` or equivalent env-aware construction. Keep `NewServer()` as the empty default for existing tests. Update `cmd/isphere-mcp/main.go` to use the env-aware server. + +- [ ] **Step 5: Update operator verification/docs** + +Extend `scripts/verify-go-mcp.ps1` so default verification still asserts zero messages when no env var is set. Document optional `ISPHERE_PACKET_LOG_FILE` usage in `docs/go-mcp-runbook.md` and update the status card. + +- [ ] **Step 6: Run full verification and commit** + +Run: + +```powershell +git diff --check +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/verify-go-mcp.ps1 +``` + +Expected: all exit 0. Remove any generated root `isphere-mcp.exe` after build. Commit exact C5 paths only and rewrite the next loop from actual results. + +--- + ## Self-Review - Spec coverage: the plan follows the user's cyclic requirement: plan first, implement one loop at a time, update the next loop after each result, and ask only on blockers. diff --git a/scripts/verify-go-mcp.ps1 b/scripts/verify-go-mcp.ps1 index 80199e1..54fb864 100644 --- a/scripts/verify-go-mcp.ps1 +++ b/scripts/verify-go-mcp.ps1 @@ -6,7 +6,8 @@ $expectedTools = @( "win_helper_version", "win_helper_self_check", "win_helper_scan_windows", - "win_helper_dump_uia" + "win_helper_dump_uia", + "isphere_receive_messages" ) $tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("isphere-go-mcp-verify-" + [guid]::NewGuid().ToString("N")) @@ -74,9 +75,10 @@ var expectedTools = []string{ "win_helper_self_check", "win_helper_scan_windows", "win_helper_dump_uia", + "isphere_receive_messages", } -var forbiddenTerms = []string{"send", "search", "file", "login", "conversation", "upload", "download", "receive", "autologin"} +var forbiddenTerms = []string{"send", "search", "file", "login", "conversation", "upload", "download", "autologin"} func main() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) @@ -122,7 +124,7 @@ func main() { sort.Strings(toolNames) want := append([]string(nil), expectedTools...) sort.Strings(want) - if len(toolNames) != 4 || !reflect.DeepEqual(toolNames, want) { + if len(toolNames) != 5 || !reflect.DeepEqual(toolNames, want) { fail("tools/list", fmt.Errorf("tool names = %#v, want %#v", toolNames, want)) } @@ -153,6 +155,26 @@ func main() { fail("tools/call win_helper_version", fmt.Errorf("helper_name = %q, want ISphereWinHelper", helperName)) } + receiveResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_receive_messages", Arguments: map[string]any{"limit": 5}}) + if err != nil { + fail("tools/call isphere_receive_messages", err) + } + if receiveResult.IsError { + fail("tools/call isphere_receive_messages", fmt.Errorf("tool returned isError=true: %#v", receiveResult.Content)) + } + var receivePayload map[string]any + receiveEncoded, _ := json.Marshal(receiveResult.StructuredContent) + if err := json.Unmarshal(receiveEncoded, &receivePayload); err != nil { + fail("tools/call isphere_receive_messages", fmt.Errorf("decode structuredContent %s: %w", receiveEncoded, err)) + } + messagesValue, ok := receivePayload["messages"].([]any) + if !ok { + fail("tools/call isphere_receive_messages", fmt.Errorf("messages missing or not array: %s", receiveEncoded)) + } + if len(messagesValue) != 0 { + fail("tools/call isphere_receive_messages", fmt.Errorf("default empty source returned %d messages, want 0", len(messagesValue))) + } + result := map[string]any{ "ok": true, "transport": "sdk_in_memory", @@ -160,6 +182,7 @@ func main() { "tool_count": len(toolNames), "tools": toolNames, "helper_name": helperName, + "receive_message_count": len(messagesValue), "real_isphere_login_required": false, "python_runtime_required": false, "action_tools_present": false, @@ -195,7 +218,8 @@ func fail(step string, err error) { Assert-True ($script:harnessJson.ok -eq $true) "SDK harness did not return ok=true" Assert-True ($script:harnessJson.helper_name -eq "ISphereWinHelper") "SDK harness helper_name mismatch: $($script:harnessJson.helper_name)" - Assert-True ($script:harnessJson.tool_count -eq 4) "SDK harness tool_count mismatch: $($script:harnessJson.tool_count)" + Assert-True ($script:harnessJson.tool_count -eq 5) "SDK harness tool_count mismatch: $($script:harnessJson.tool_count)" + Assert-True ($script:harnessJson.receive_message_count -eq 0) "SDK harness receive_message_count mismatch: $($script:harnessJson.receive_message_count)" [ordered]@{ ok = $true @@ -204,6 +228,7 @@ func fail(step string, err error) { helper_name = $script:harnessJson.helper_name tool_count = $script:harnessJson.tool_count tools = $script:harnessJson.tools + receive_message_count = $script:harnessJson.receive_message_count python_runtime_required = $false real_isphere_login_required = $false action_tools_present = $false @@ -216,4 +241,4 @@ finally { if (Test-Path -LiteralPath $harnessDir) { Remove-Item -LiteralPath $harnessDir -Recurse -Force } -} +}