feat(chatboxes): publish an environment as a chatbox from the Environments route - #3642
Conversation
…ments route The backend has shipped the entire env-backed chatbox runtime (Phase 5, mcpjam-backend #805): pointer rows that live-follow the environment, runtime resolution in chat-v2, and admin-gated publish/unpublish mutations — but no inspector UI ever called them. Env-backed chatboxes are also deliberately invisible to the host-first ChatboxesTab (getHostPublishChatbox filters them out), so the Environments route is their natural management surface. - EnvironmentChatboxSection in the environment detail view: publish, copy/open the share link, unpublish (explicit confirm; the link dies immediately). Admin-gated actions; members see read-only state; backend FORBIDDEN/CONFLICT copy surfaces verbatim. - useEnvironmentChatbox / useEnvironmentChatboxMutations in useChatboxes.ts; ChatboxListItem gains the `link` field the backend already returns. - ConnectEnvironmentsStrip sandbox-image tooltip no longer claims chatboxes ignore the env's pinned image (chatbox-runtime-config.ts resolves it per call). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_aaaf9efa-797a-4cfa-98a0-8b67e0b3975f) |
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Internal previewPreview URL: https://mcp-inspector-pr-3642.up.railway.app |
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
WalkthroughAdds environment chatbox publishing controls to live environment details. The controls support publishing, share-link copying and opening, unpublishing with confirmation, loading states, error handling, and management permission gating. Adds hooks for environment chatbox lookup and typed mutations. Adds tests for publishing, permissions, links, unpublishing, errors, and environment matching. Updates sandbox-image tooltip text. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsxOops! Something went wrong! :( ESLint: 8.57.1 Error: ESLint configuration in --config is invalid:
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx (1)
130-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a CONFLICT-path test alongside the existing FORBIDDEN test.
The component's docstring commits to surfacing backend
FORBIDDENandCONFLICTcopy verbatim, but onlyFORBIDDENis tested here. Add a parallel case forCONFLICT(e.g., onunpublishMockorpublishMock) using the same pattern as this test, to lock in the other half of the documented error contract.🧪 Proposed additional test
+ it("surfaces backend CONFLICT copy verbatim on unpublish", async () => { + chatboxList = [publishedChatbox]; + unpublishMock.mockRejectedValue({ + data: { + code: "CONFLICT", + message: "This chatbox was already modified by another admin.", + }, + }); + renderSection(); + fireEvent.click(screen.getByTestId("environment-chatbox-unpublish")); + fireEvent.click( + screen.getByTestId("environment-chatbox-unpublish-confirm") + ); + await waitFor(() => { + expect(toastMock.error).toHaveBeenCalledWith( + expect.stringContaining("already modified by another admin") + ); + }); + });🤖 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 `@mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx` around lines 130 - 145, Add a parallel test next to “surfaces backend FORBIDDEN copy verbatim on publish” that mocks a backend CONFLICT response, triggers the corresponding publish or unpublish action, and asserts toastMock.error receives the conflict message verbatim or containing its distinctive text. Reuse the existing renderSection and waitFor pattern to cover the documented CONFLICT error contract.
🤖 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.
Nitpick comments:
In
`@mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx`:
- Around line 130-145: Add a parallel test next to “surfaces backend FORBIDDEN
copy verbatim on publish” that mocks a backend CONFLICT response, triggers the
corresponding publish or unpublish action, and asserts toastMock.error receives
the conflict message verbatim or containing its distinctive text. Reuse the
existing renderSection and waitFor pattern to cover the documented CONFLICT
error contract.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5a62b632-dfd1-4938-b6eb-043011905172
📒 Files selected for processing (6)
mcpjam-inspector/client/src/components/project-environments/ConnectEnvironmentsStrip.tsxmcpjam-inspector/client/src/components/project-environments/ProjectEnvironmentsRoute.tsxmcpjam-inspector/client/src/components/project-environments/__tests__/archive-consumer-counts.test.tsxmcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsxmcpjam-inspector/client/src/components/project-environments/environment-chatbox-section.tsxmcpjam-inspector/client/src/hooks/useChatboxes.ts
Cubic: the publish test only exercised created: true. Add the created: false 'already published' branch and an assertion that a published row renders the live-follow copy + link/unpublish actions instead of the publish button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_cff0133a-2e29-414b-9fd7-aed06e6ce410) |
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
`@mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx`:
- Around line 100-117: Update the idempotent re-publish test around
renderSection and the environment-chatbox-publish click to assert that
publishMock was called with the expected environmentId and other publish payload
values. Keep the existing “already published” toast assertion while verifying
the mutation receives the correct target environment.
🪄 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: CHILL
Plan: Pro Plus
Run ID: 71550d32-15ea-4820-ad00-adfb827b6af5
📒 Files selected for processing (1)
mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx
| it("idempotent re-publish (created: false) reports 'already published'", async () => { | ||
| publishMock.mockResolvedValue({ | ||
| chatboxId: "cb-1", | ||
| environmentId: "env-1", | ||
| name: "Prod-like", | ||
| mode: "project_members", | ||
| accessVersion: 1, | ||
| link: publishedChatbox.link, | ||
| created: false, | ||
| }); | ||
| renderSection(); | ||
| fireEvent.click(screen.getByTestId("environment-chatbox-publish")); | ||
| await waitFor(() => { | ||
| expect(toastMock.success).toHaveBeenCalledWith( | ||
| expect.stringContaining("already published") | ||
| ); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert the publish mutation arguments.
This test can pass if the component sends the wrong environmentId, because publishMock resolves for any arguments. Verify the mutation payload in this idempotent path.
Suggested assertion
await waitFor(() => {
+ expect(publishMock).toHaveBeenCalledWith({
+ environmentId: "env-1",
+ });
expect(toastMock.success).toHaveBeenCalledWith(
expect.stringContaining("already published")
);📝 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.
| it("idempotent re-publish (created: false) reports 'already published'", async () => { | |
| publishMock.mockResolvedValue({ | |
| chatboxId: "cb-1", | |
| environmentId: "env-1", | |
| name: "Prod-like", | |
| mode: "project_members", | |
| accessVersion: 1, | |
| link: publishedChatbox.link, | |
| created: false, | |
| }); | |
| renderSection(); | |
| fireEvent.click(screen.getByTestId("environment-chatbox-publish")); | |
| await waitFor(() => { | |
| expect(toastMock.success).toHaveBeenCalledWith( | |
| expect.stringContaining("already published") | |
| ); | |
| }); | |
| }); | |
| it("idempotent re-publish (created: false) reports 'already published'", async () => { | |
| publishMock.mockResolvedValue({ | |
| chatboxId: "cb-1", | |
| environmentId: "env-1", | |
| name: "Prod-like", | |
| mode: "project_members", | |
| accessVersion: 1, | |
| link: publishedChatbox.link, | |
| created: false, | |
| }); | |
| renderSection(); | |
| fireEvent.click(screen.getByTestId("environment-chatbox-publish")); | |
| await waitFor(() => { | |
| expect(publishMock).toHaveBeenCalledWith({ | |
| environmentId: "env-1", | |
| }); | |
| expect(toastMock.success).toHaveBeenCalledWith( | |
| expect.stringContaining("already published") | |
| ); | |
| }); | |
| }); |
🤖 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
`@mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx`
around lines 100 - 117, Update the idempotent re-publish test around
renderSection and the environment-chatbox-publish click to assert that
publishMock was called with the expected environmentId and other publish payload
values. Keep the existing “already published” toast assertion while verifying
the mutation receives the correct target environment.
Why
Chatboxes should think in environments. The backend already does: Phase 5 (mcpjam-backend #805) shipped env-backed chatbox rows (live-follow pointers — no hostConfig pin, no chatbox-scope attachment; runtime resolution re-resolves the environment fresh on every read), the chat-v2 runtime path, and admin-gated
publishEnvironmentChatbox/unpublishEnvironmentChatboxmutations. The inspector never exposed any of it — there was no way to publish an environment as a chatbox from the UI.One structural fact drives the design: env-backed chatboxes are deliberately invisible to the host-first ChatboxesTab (
getHostPublishChatboxfilters out rows withenvironmentId, by documented design). So their management surface is the Environments route, not the chatbox publish bar.What
EnvironmentChatboxSectionin the environment detail view (/environments): publish as chatbox, copy/open the share link, unpublish behind an explicit confirm ("link stops working"). Actions are project-admin gated backend-side; members see read-only state; backendFORBIDDEN/CONFLICTcopy surfaces verbatim viaconvexErrMessage. Hidden for archived environments (backend rejects publishing those; an existing link is already dead on archive per the archive-confirm copy).useChatboxes.ts):useEnvironmentChatbox(the 0-or-1 backing row off the samelistChatboxessubscription) +useEnvironmentChatboxMutations.ChatboxListItemgains thelink {token,path,url}field the backend already returns. Publish is idempotent (second publish returns the existing row — surfaced as "already published").ConnectEnvironmentsStripclaimed chatboxes don't use the env's pinned image;server/utils/chatbox-runtime-config.tshas resolved it per call since Phase 5.Deliberately minimal v1 (per the keep-UI-minimal bar): mode/members/link-rotation/guest-execution editors for env chatboxes are deferred — they're admin surfaces the standard chatbox editors own, reachable later by
chatboxIdif env chatboxes need them. Also out of scope: converting a host-backed chatbox to env-backed in place (the backend models env chatboxes as publish-of-environment rows, not re-pointed host chatboxes).Testing
environment-chatbox-section.test.tsx: publish round-trip, member read-only gating, link copy from the row token, unpublish confirm flow, FORBIDDEN copy passthrough, and no false "published" from another environment's chatbox.client/src/components/project-environmentssuite (79 tests) green;typecheck:clientclean.chat-v2.chatbox-environment.test.ts.🤖 Generated with Claude Code
Note
Medium Risk
New admin-gated publish/unpublish flows affect shareable chat execution config; backend already enforces rules, but mistakes break live guest links.
Overview
Exposes Phase 5 env-backed chatboxes on the Environments detail page via new
EnvironmentChatboxSection: admins can publish (idempotent), copy/open the share link, and unpublish with confirm; members see read-only state; the section is hidden for archived environments.useChatboxesgainsuseEnvironmentChatbox(0-or-1 row fromlistChatboxesbyenvironmentId),useEnvironmentChatboxMutations(publishEnvironmentChatbox/unpublishEnvironmentChatbox), andlinkonChatboxListItem.ConnectEnvironmentsStriptooltip now says published chatboxes use the environment’s sandbox image (was incorrectly excluded).Tests: new
environment-chatbox-section.test.tsx;archive-consumer-countsstubs the new section.Reviewed by Cursor Bugbot for commit 1f60dcb. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Add UI to publish an environment as a chatbox from the Environments route, manage its share link, and unpublish. Env-backed chatboxes live-follow the environment and remain hidden from the Chatboxes tab.
New Features
EnvironmentChatboxSectionin/environments: publish, copy/open share link, and unpublish with confirm. Admin-only actions; members see read-only state; hidden for archived envs. Surfaces backendFORBIDDEN/CONFLICTcopy viaconvexErrMessage. Re-publish shows “already published.”useChatboxes.ts:useEnvironmentChatboxanduseEnvironmentChatboxMutations.ChatboxListItemnow haslink {token, path, url}. Publish is idempotent and returns the existing row.Bug Fixes
ConnectEnvironmentsStriptooltip now correctly states that published chatboxes use the environment’s sandbox image.Written for commit 1f60dcb. Summary will update on new commits.