feat: build interactive cortex MCP Apps query widget (syslog-mcp-yi66.2) - #74
Conversation
Replace the placeholder query widget HTML with a self-contained, dependency-free Aurora-dark widget: FTS5 query input, hostname/severity/limit filters, results table, and idle/loading/empty/error/bridge-unavailable states. Submit calls the cortex tool with action=search via a window.openai-or-mcp-ui postMessage bridge. Log fields render via textContent to prevent injection from untrusted messages. Extend resources/read test with stable wire-format anchors. Refresh stale MCPUI.md. Bump 1.20.1 -> 1.21.0.
Address review findings on the postMessage bridge: - P1: bind onMessage to event.source===window.parent and require a positive messageId match (predictable ids + global listener allowed sibling-frame response spoofing and stale-response cross-talk). - P2: wrap window.parent.postMessage in try/catch with a single 'done' guard + cleanup() so a throw (DataCloneError/SecurityError) no longer leaks the listener/timer or hangs the promise. - P3: shared withTimeout() bounds the window.openai.callTool path too, so a hung host can no longer wedge the form; surface a 'not responding' status; 15s->12s. - Cap reflected host error strings at 200 chars; drop redundant Date.now guard. Lock the origin guard with an event.source wire-format test anchor.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughAdds the interactive ChangesCortex Query Widget v1.21.2 Release
Sequence Diagram(s)sequenceDiagram
participant User
participant QueryWidget as query_widget.html
participant HostBridge as window.openai.callTool
participant ParentFrame as Parent Frame
participant MCPServer as Cortex MCP Server
User->>QueryWidget: submit form (query, host, severity, limit)
QueryWidget->>QueryWidget: disable submit, set status "Searching..."
alt Host bridge available
QueryWidget->>HostBridge: callTool("cortex", { action: "search", ... })
HostBridge->>MCPServer: tools/call action=search
MCPServer-->>HostBridge: { structuredContent: { logs }, content[0].text }
HostBridge-->>QueryWidget: result object
else postMessage fallback
QueryWidget->>ParentFrame: postMessage({ messageId, type: "mcp-tool-call", params })
ParentFrame->>MCPServer: tools/call action=search
MCPServer-->>ParentFrame: response
ParentFrame-->>QueryWidget: postMessage({ messageId, result })
end
QueryWidget->>QueryWidget: normalize to { count, logs }
QueryWidget->>QueryWidget: render rows via textContent (no innerHTML)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add a 'MCP Apps query widget' section to README (URI ui://cortex/query-widget, MIME text/html;profile=mcp-app, progressive-enhancement framing, JSON-RPC verification, 'simple search UI not a dashboard') and a verify step in docs/SETUP.md. Add host-agnostic widget wire-contract checks to scripts/smoke-test.sh (resources/list, resources/read MIME+anchors, tools/list _meta.ui.resourceUri, tools/call action=search structuredContent) — no browser/Node/named UI host required. Verified live against a worktree build. Bump 1.21.0 -> 1.21.1.
|
Updated: this branch now also contains syslog-mcp-yi66.3 — README |
…lpers test_state/mounted_state now delegate to a single make_state(auth_policy) builder; fold the byte-identical rmcp_router_no_auth_middleware into rmcp_router (call sites already document the no-auth-middleware intent inline). Test-only, no behavior change — all 36 rmcp server tests pass. Bump 1.21.1 -> 1.21.2.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Line 10: The [Unreleased] compare link reference at the bottom of the
CHANGELOG.md file still points to v1.20.0 as the base comparison version, but
now that version 1.21.2 has been released, this link needs to be updated. Find
the [Unreleased] link reference (typically at the end of the file) and update
its comparison range to use v1.21.2 as the base version instead of v1.20.0, so
the link compares changes between v1.21.2 and HEAD.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 258250cd-f310-4fc9-9c93-635f23afcf0d
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lockand included by**/*
📒 Files selected for processing (11)
CHANGELOG.mdCargo.tomlREADME.mddocker-compose.prod.ymldocs/SETUP.mddocs/mcp/MCPUI.mdmcpb/manifest.jsonscripts/smoke-test.shserver.jsonsrc/mcp/rmcp_server_tests.rssrc/mcp/ui/query_widget.html
|
|
||
| ## [Unreleased] | ||
|
|
||
| ## [1.21.2] - 2026-06-15 |
There was a problem hiding this comment.
Update the Unreleased compare target.
The changelog now includes 1.21.2, but the [Unreleased] link still compares against v1.20.0. That makes the release diff range misleading; point it at v1.21.2 instead.
Suggested fix
-[Unreleased]: https://github.com/jmagar/cortex/compare/v1.20.0...HEAD
+[Unreleased]: https://github.com/jmagar/cortex/compare/v1.21.2...HEAD📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## [1.21.2] - 2026-06-15 | |
| [Unreleased]: https://github.com/jmagar/cortex/compare/v1.21.2...HEAD |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CHANGELOG.md` at line 10, The [Unreleased] compare link reference at the
bottom of the CHANGELOG.md file still points to v1.20.0 as the base comparison
version, but now that version 1.21.2 has been released, this link needs to be
updated. Find the [Unreleased] link reference (typically at the end of the file)
and update its comparison range to use v1.21.2 as the base version instead of
v1.20.0, so the link compares changes between v1.21.2 and HEAD.
Address full-PR review follow-ups (PR #74): - syslog-mcp-7kqp2: add a defense-in-depth CSP to query_widget.html (img/object/ base/form locked; script-src/connect-src left open so host bridge injection isn't broken) and document the origin-bound message handling + ui:// targetOrigin '*' constraint in docs/mcp/MCPUI.md. - syslog-mcp-l0gmx: assert_gte reads its value via stdin instead of python -c interpolation; json_get documents its literal-accessor-only contract. - syslog-mcp-zvvzn: factor PUBLIC_URL McpConfig literals into public_url_config(); fix a stray tab in smoke-test.sh. Bump 1.21.2 -> 1.21.3.
Reconcile the test-coverage branch with main after PRs #73/#74. All conflicts were version/metadata skew only (no source/test conflicts); the branch forked at 1.20.1 before main bumped to 1.21.3. - Resolve Cargo.toml/Cargo.lock/server.json/mcpb/manifest.json/ docker-compose.prod.yml to main's line, then bump to 1.21.4. - Consolidate the branch's parallel 1.20.2–1.20.12 CHANGELOG entries (never released on main) into a single 1.21.4 entry; keep main's 1.21.0–1.21.3 entries. Verified: cargo fmt --check, cargo clippy --all-targets, and the canonical `cargo nextest run` (1826 passed) all green.
Summary
The cortex MCP-UI widget rendered as a bare, unstyled, non-functional form — the prior
query_widget.htmlwas only a placeholder stub from the server-contract slice (syslog-mcp-yi66.1). This builds out the actual widget (syslog-mcp-yi66.2).cortextool withaction=searchover an MCP Apps host bridge —window.openai.callToolwhen injected, else an mcp-uipostMessageadapter (type:"tool"→ui-message-response).textContent— no HTML injection from untrusted log data.text/html;profile=mcp-app,_meta.ui.resourceUri,ui://cortex/query-widget) is unchanged — it was an intentional Locked decision, not a bug.Review hardening (folded in)
Multi-agent review (security / frontend-races / simplicity) + goal-verifier ran; findings fixed inline:
postMessagehandler (event.source === window.parent) and required a positivemessageIdmatch — predictable ids + a global listener allowed sibling-frame response spoofing and stale-response cross-talk.try/catch+ singledoneguard aroundpostMessageso a throw no longer leaks the listener/timer or hangs the promise.withTimeout()bounds thewindow.openai.callToolpath too (hung host no longer wedges the form); reflected host error strings capped at 200 chars.Bead
syslog-mcp-yi66.2: Build the initial log query widget HTML
Testing
resources/readtest with 9 stable wire-format anchors (incl.event.sourceorigin guard). Fullcargo testgreen via pre-push hook; clippy/fmt clean.syslog-mcp-yi66.3.Follow-ups
syslog-mcp-6jbt7(P3, pre-existing): consolidate duplicated rmcp test-state helpers.Version: 1.20.1 → 1.21.0
Summary by cubic
Builds the interactive MCP Apps query widget for
ui://cortex/query-widget, documents it as a progressive enhancement with host-agnostic smoke tests, and hardens the host bridge with a defense-in-depth CSP. Also consolidates test helpers and deduplicates PUBLIC_URL config. Version: 1.21.3.New Features
window.openai.callToolwhen available; otherwise uses apostMessageadapter (type:"tool"→ui-message-response) with a bounded timeout.resources/list,resources/readMIME + anchors,_meta.ui.resourceUri, andtools/call action=searchreturning bothstructuredContentand text.Bug Fixes
postMessage(event.source === window.parent) with matchingmessageId, shared timeout with “not responding” status, try/catch + single completion guard, and host error strings capped at 200 chars.img-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'); docs note origin-bound handling and theui://targetOrigin:"*"constraint.python -c(assert_gtereads via stdin;json_getdocuments literal-accessor contract).McpConfigliterals viapublic_url_config()and fold the no-auth router intormcp_router(no behavior change).Written for commit c16a57b. Summary will update on new commits.
Summary by CodeRabbit
New Features
Documentation
Chores