Skip to content

feat(chatboxes): publish an environment as a chatbox from the Environments route - #3642

Merged
chelojimenez merged 2 commits into
mainfrom
feat/chatbox-environments-ui
Aug 3, 2026
Merged

feat(chatboxes): publish an environment as a chatbox from the Environments route#3642
chelojimenez merged 2 commits into
mainfrom
feat/chatbox-environments-ui

Conversation

@chelojimenez

@chelojimenez chelojimenez commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

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/unpublishEnvironmentChatbox mutations. 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 (getHostPublishChatbox filters out rows with environmentId, by documented design). So their management surface is the Environments route, not the chatbox publish bar.

What

  • EnvironmentChatboxSection in 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; backend FORBIDDEN/CONFLICT copy surfaces verbatim via convexErrMessage. Hidden for archived environments (backend rejects publishing those; an existing link is already dead on archive per the archive-confirm copy).
  • Hooks (useChatboxes.ts): useEnvironmentChatbox (the 0-or-1 backing row off the same listChatboxes subscription) + useEnvironmentChatboxMutations. ChatboxListItem gains the link {token,path,url} field the backend already returns. Publish is idempotent (second publish returns the existing row — surfaced as "already published").
  • Stale copy fix: the sandbox-image tooltip in ConnectEnvironmentsStrip claimed chatboxes don't use the env's pinned image; server/utils/chatbox-runtime-config.ts has 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 chatboxId if 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

  • New 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.
  • Full client/src/components/project-environments suite (79 tests) green; typecheck:client clean.
  • Manual check once deployed: publish an env from /environments, open the link in a guest window — the runtime path is already pinned by 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.

useChatboxes gains useEnvironmentChatbox (0-or-1 row from listChatboxes by environmentId), useEnvironmentChatboxMutations (publishEnvironmentChatbox / unpublishEnvironmentChatbox), and link on ChatboxListItem.

ConnectEnvironmentsStrip tooltip now says published chatboxes use the environment’s sandbox image (was incorrectly excluded).

Tests: new environment-chatbox-section.test.tsx; archive-consumer-counts stubs 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

    • EnvironmentChatboxSection in /environments: publish, copy/open share link, and unpublish with confirm. Admin-only actions; members see read-only state; hidden for archived envs. Surfaces backend FORBIDDEN/CONFLICT copy via convexErrMessage. Re-publish shows “already published.”
    • Hooks in useChatboxes.ts: useEnvironmentChatbox and useEnvironmentChatboxMutations. ChatboxListItem now has link {token, path, url}. Publish is idempotent and returns the existing row.
  • Bug Fixes

    • ConnectEnvironmentsStrip tooltip now correctly states that published chatboxes use the environment’s sandbox image.

Written for commit 1f60dcb. Summary will update on new commits.

Review in cubic

…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>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Aug 3, 2026
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@dosubot

dosubot Bot commented Aug 3, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-09-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about inspector Add Dosu to your team

@chelojimenez

chelojimenez commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Internal preview

Preview URL: https://mcp-inspector-pr-3642.up.railway.app
Deployed commit: a7345da
PR head commit: 1f60dcb
Backend target: staging fallback.
Health: ✅ Convex reachable
Access is employee-only in non-production environments.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds 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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: ESLint configuration in --config is invalid:

  • Unexpected top-level property "__esModule".

    at ConfigValidator.validateConfigSchema (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2177:19)
    at ConfigArrayFactory._normalizeConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3019:19)
    at ConfigArrayFactory._loadConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2984:21)
    at ConfigArrayFactory.loadFile (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2850:40)
    at createCLIConfigArray (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3660:35)
    at new CascadingConfigArrayFactory (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3735:29)
    at new CLIEngine (/soundcheck/node_modules/eslint/lib/cli-engine/cli-engine.js:617:36)
    at new ESLint (/soundcheck/node_modules/eslint/lib/eslint/eslint.js:430:27)
    at Object.execute (/soundcheck/node_modules/eslint/lib/cli.js:410:24)
    at async main (/soundcheck/node_modules/eslint/bin/eslint.js:152:22)


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx (1)

130-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a CONFLICT-path test alongside the existing FORBIDDEN test.

The component's docstring commits to surfacing backend FORBIDDEN and CONFLICT copy verbatim, but only FORBIDDEN is tested here. Add a parallel case for CONFLICT (e.g., on unpublishMock or publishMock) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 86cb60f and a478cda.

📒 Files selected for processing (6)
  • mcpjam-inspector/client/src/components/project-environments/ConnectEnvironmentsStrip.tsx
  • mcpjam-inspector/client/src/components/project-environments/ProjectEnvironmentsRoute.tsx
  • mcpjam-inspector/client/src/components/project-environments/__tests__/archive-consumer-counts.test.tsx
  • mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx
  • mcpjam-inspector/client/src/components/project-environments/environment-chatbox-section.tsx
  • mcpjam-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>
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between a478cda and 1f60dcb.

📒 Files selected for processing (1)
  • mcpjam-inspector/client/src/components/project-environments/__tests__/environment-chatbox-section.test.tsx

Comment on lines +100 to +117
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")
);
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
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.

@chelojimenez
chelojimenez merged commit ab8241d into main Aug 3, 2026
13 checks passed
@chelojimenez
chelojimenez deleted the feat/chatbox-environments-ui branch August 3, 2026 02:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant