Skip to content

feat(team-settings): connect your AI assistant [HDX-4440]#2407

Merged
kodiakhq[bot] merged 10 commits into
mainfrom
alex/HDX-4440-team-settings-mcp-install
Jun 5, 2026
Merged

feat(team-settings): connect your AI assistant [HDX-4440]#2407
kodiakhq[bot] merged 10 commits into
mainfrom
alex/HDX-4440-team-settings-mcp-install

Conversation

@alex-fedotyev

@alex-fedotyev alex-fedotyev commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a "Connect your AI Agents" section to the Team Settings page in a new "API & Agents" tab, alongside the existing API Keys card. Users get a one-click install path for the ClickStack MCP server in Claude Code, Cursor, VS Code, Codex CLI, OpenCode, or any MCP-compatible host without hand-rolling JSON.

The MCP server resolves the active team from the bearer token, so the install snippet uses a single fixed clickstack server name; no per-tenant suffix needed on the client. Header values are shell-escaped against \", $, backtick, and \\ so a future access-key format with metacharacters can't turn a copy-paste install into a shell-injection vector.

What's here

  • packages/app/src/components/ClickStackOnboarding/ (new directory). Houses the install panel and its pieces:
    • installSnippets.ts: pure per-host snippet builders. Unit-tested round-trip through buildAllSnippets.
    • McpInstallPanel.tsx: host picker (Mantine SegmentedControl) + render branch per host. Exhaustive switch via satisfies never so adding a host variant fails the compile.
    • CopySnippet.tsx: code block with a <CopyButton> affordance.
    • DeeplinkInstall.tsx: primary "Add to " button + Manual setup toggle that reveals a fallback JSON snippet.
  • packages/app/src/components/TeamSettings/McpServerSection.tsx: wraps the panel, derives the deployment shape from useMe(), mounts in the new tab.
  • packages/app/src/TeamPage.tsx: introduces a new "API & Agents" tab (URL value api-agents) that houses the API Keys card and the AI Agents install panel. The Integrations tab keeps webhooks only.
  • .changeset/connect-your-ai-assistant.md: user-facing change note.

Hosts covered

  • Claude Code (CLI one-liner)
  • Cursor (cursor:// deep link, JSON fallback behind Manual setup)
  • VS Code (vscode:mcp/install deep link, JSON fallback; requires Copilot Chat 1.99+)
  • Codex CLI (one-liner mirroring Claude Code; documented at https://developers.openai.com/codex/mcp)
  • OpenCode (JSON config under an mcp key, type: "remote"; documented at https://opencode.ai/docs/mcp-servers/)
  • Other (canonical mcpServers JSON block for Claude Desktop, Continue, Cline, and the long tail)

Test plan

  • yarn jest --testPathPatterns="installSnippets|McpServerSection" (25 tests pass).
  • ESLint clean on every touched file.
  • tsc --noEmit clean on every touched file.
  • prose-lint clean on every touched file.
  • Predicted tier: Tier 3.
  • Manual: bring up a self-managed deployment, navigate to Team Settings -> API & Agents, copy each host's snippet, install in the host, confirm the host connects against /api/mcp. Holding this checkbox until I drive the flow once.

Notes

  • Branch prefix alex/ (human prefix) keeps the classifier on the standard human thresholds.
  • Initial revision wrapped the e2e suite in test.fixme(true, ...) because the e2e webServer runs in IS_LOCAL_MODE, which short-circuits useMe/useTeam before the route mocks can land. That suite was deleted in the review-feedback fix-pack; component coverage in __tests__/McpServerSection.test.tsx exercises every render branch. A real e2e spec can come back once the e2e build-flag work lands.

Linear: HDX-4440

Co-Authored-By: Claude Opus noreply@anthropic.com

Add a "Connect your AI assistant" section to the Team Settings
page (Integrations tab), rendered directly above the API Keys
card. The section gives users a one-click install path for the
ClickStack MCP server in Claude Code, Cursor, VS Code + Copilot,
Codex CLI, or any MCP-compatible host without hand-rolling JSON.

Per-host install primitives:
- Claude Code: `claude mcp add ... --transport http <url> --header
  "Authorization: Bearer ..."` one-liner.
- Codex CLI: `codex mcp add ...` one-liner (mirrors Claude Code).
- Cursor: `cursor://anysphere.cursor-deeplink/mcp/install?...`
  deep link with base64-encoded config; manual JSON fallback
  behind a `Manual setup` toggle.
- VS Code + Copilot: `vscode:mcp/install?<encoded JSON>` deep
  link; manual JSON fallback. Requires VS Code 1.99+ with the
  Copilot Chat MCP feature enabled.
- Other: canonical `mcpServers` JSON block that covers Claude
  Desktop, Continue, Cline, and the long tail.

The MCP server resolves the active team from the bearer token, so
the install snippet doesn't need to disambiguate by name on the
client. A single fixed `clickstack` server name is used across
every host primitive.

Header values are shell-escaped against `"`, `$`, backtick, and
backslash so a future access-key format with metacharacters can't
turn a copy-paste install into a shell-injection vector.

Tests: 16 component + snippet tests pass. Lint, TS, prose-lint
clean.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5bbb2d8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jun 5, 2026 6:12pm
hyperdx-storybook Ready Ready Preview, Comment Jun 5, 2026 6:12pm

Request Review

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 199 passed • 3 skipped • 1266s

Status Count
✅ Passed 199
❌ Failed 0
⚠️ Flaky 2
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@alex-fedotyev alex-fedotyev marked this pull request as ready for review June 3, 2026 23:45
@alex-fedotyev alex-fedotyev removed the WIP label Jun 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 618 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 6
  • Production lines changed: 618 (+ 432 in test files, excluded from tier calculation)
  • Branch: alex/HDX-4440-team-settings-mcp-install
  • Author: alex-fedotyev

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Jun 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. No P0/P1 ship-blockers: the snippet builders, shell-escaping, encoding round-trips, useMe() gating, and exhaustive-switch logic all trace correctly, and the access-key exposure intentionally mirrors the existing API Keys card pattern. The items below are recommendations and nits.

🟡 P2 — recommended

  • packages/app/src/components/ClickStackOnboarding/installSnippets.ts:227 — the Codex CLI snippet is generated by buildCliOneLiner('codex', ...), reusing Claude Code's exact mcp add --transport http <url> --header flag grammar, which the Codex CLI may not accept; a wrong grammar makes the copy-pasted one-liner error out instead of installing.
    • Fix: Verify codex mcp add flag syntax against the targeted Codex CLI version and, if it diverges, give Codex its own builder (or a config-block snippet) rather than sharing the Claude Code generator.
  • packages/app/src/components/TeamSettings/__tests__/McpServerSection.test.tsx:183 — the manual-fallback test asserts only that the toggle label flips, which is driven by the same manualOpen state as <Collapse expanded>, so it would still pass if the fallback never rendered; the new reveal behavior is effectively unverified.
    • Fix: After clicking the toggle, add a positive assertion that the fallback is shown, e.g. expect(screen.getByText(/"mcpServers":/)).toBeVisible().
🔵 P3 nitpicks (6)
  • .changeset/connect-your-ai-assistant.md:7 — the changeset body describes the section as living on the "Integrations tab, above the API Keys card", but the diff actually introduces a new "API & Agents" tab and places the section below the API Keys card.
    • Fix: Update the changeset body to describe the new "API & Agents" tab placement.
  • .changeset/connect-your-ai-assistant.md:5 — the changeset title says "Connect your AI assistant" while the rendered header and tests use "Connect your AI Agents".
    • Fix: Align the changeset wording with the rendered header text.
  • packages/app/src/components/TeamSettings/McpServerSection.tsx:11 — the component JSDoc states the section renders on the "Integrations tab", which no longer matches the new "API & Agents" tab.
    • Fix: Correct the doc comment to reference the "API & Agents" tab.
  • packages/app/src/components/ClickStackOnboarding/McpInstallPanel.tsx:1 — the new ClickStackOnboarding/ directory holds a Team Settings MCP install panel, not a first-run onboarding flow, so the name misleads on where the code lives.
    • Fix: Rename or co-locate under components/TeamSettings/ (or a neutral name like McpInstall/) to match the actual role.
    • maintainability, project-standards
  • packages/app/src/components/ClickStackOnboarding/installSnippets.ts:1 — comment density (multi-paragraph rationale on nearly every function, plus essays documenting unreachable defensive paths) far exceeds sibling Team Settings files, which carry near-zero comments, and risks drifting out of sync with the code.
    • Fix: Trim to the non-obvious "why" (URL-safe base64 for Cursor, OpenCode type: remote) and drop the restated/speculative prose.
  • packages/app/src/components/ClickStackOnboarding/McpInstallPanel.tsx:83buildAllSnippets eagerly inlines the live access key into every snippet, deep-link href, and the clipboard copy with no mask or reveal-to-copy step, so the bearer can land in browser history or a clipboard manager.
    • Fix: Track the already-noted shared mask/reveal-to-copy follow-up before broad rollout.
    • adversarial, security

Reviewers (9): correctness, security, adversarial, kieran-typescript, testing, maintainability, project-standards, agent-native, learnings-researcher.

Testing gaps:

  • Deep-link config round-trips are decoded only at the builder level; the rendered-component tests assert just the href prefix/charset, not a full decode of the emitted config.
  • The VS Code-specific fallbackLabel and the "Requires VS Code 1.99+" note are never asserted.
  • The browser btoa branch of base64() is never exercised (Jest runs the Node Buffer branch), leaving the UTF-8 path for code points > 0xFF unverified.

- Cursor deeplink: emit URL-safe base64 (-, _, no padding) for the
  config query value so the standard alphabet's + / / / = cannot be
  re-interpreted by the host's URL parser. + decodes as space under
  form-urlencoded, which would corrupt the embedded JSON config.
  Updated round-trip test to base64url and added a regression case
  asserting the encoded value only uses the URL-safe alphabet across
  inputs that would have produced + or / in standard base64.
- base64(): UTF-8-encode before btoa so a future access-key or origin
  format with code points > 0xFF cannot raise InvalidCharacterError
  and blank the panel (buildAllSnippets builds every host eagerly).
- installSnippets: extract shared buildCliOneLiner(binary, deployment)
  helper; Claude Code and Codex CLI now share the generator and differ
  only in the binary name.
- McpServerSection: drop the unreachable typeof window === 'undefined'
  guard; the section renders client-only via useMe(). Drop the
  defensive me.accessKey ?? '' coalesce now that MeApiResponseSchema
  pins accessKey to z.string(); the downstream empty-string guard in
  the panel still fires.
- McpInstallPanel: replace the value as AgentHost cast on
  SegmentedControl.onChange with an isAgentHost(value) type guard
  derived from CHOICES so a future out-of-band value cannot silently
  install an invalid host.
- CopySnippet: fix the stale JSDoc reference (the cited
  ExporterFormatSelector.tsx is not in the repo) and drop the
  redundant color="gray" on the subtle Button; variant="subtle" is
  the canonical pattern per agent_docs/code_style.md.
- Add component tests for host-switching, the Cursor / VS Code deep
  link href shape, the canonical JSON branch on Other, and the
  Manual setup toggle in DeeplinkInstall. Closes the host-coverage
  gap without enabling the e2e fixme (the e2e webServer still runs
  in IS_LOCAL_MODE, which short-circuits useMe / useTeam before the
  route mocks land). 22 tests pass.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@alex-fedotyev

Copy link
Copy Markdown
Contributor Author

Fix-pack for the deep-review findings on 4a58af6a.

P2: Cursor deeplink URL-safe base64. Fixed in 9e6e5fa5. The config value now uses base64url (-, _, no padding) so + / / / = cannot be re-interpreted by the host's URL parser. Updated the round-trip test to base64url and added a regression that asserts the encoded value only uses the URL-safe alphabet on inputs that would have produced + or / in standard base64.

P2: real-host install validation. Holding. The PR's test plan already calls this out as the manual end-to-end checkbox against a self-managed deployment, which is the gate on the DO NOT MERGE lift. Driving that flow against the local stack now; will post screenshots back in this thread.

P2: host-switching / deeplink / manual-setup coverage. Fixed in 9e6e5fa5. Added component tests in McpServerSection.test.tsx covering: host switch renders the correct snippet (Codex CLI variant), Cursor deeplink href shape, VS Code deeplink href shape, canonical JSON branch on Other, and the Manual setup toggle in DeeplinkInstall. Did not gate the e2e fixme on a CI env var: the e2e webServer still runs in IS_LOCAL_MODE, which short-circuits useMe / useTeam before the route mocks land, so the gate would be cosmetic until that flag is wired up. 22 tests pass (was 16).

P3: btoa Latin1. Fixed in 9e6e5fa5. base64() now UTF-8-encodes via TextEncoder before btoa, so a future key or origin format with a code point > 0xFF cannot raise InvalidCharacterError and blank the panel.

P3: McpServerSection SSR guard. Fixed in 9e6e5fa5. Dropped typeof window === 'undefined'; the section renders client-only via useMe() so the branch was unreachable.

P3: McpServerSection accessKey ?? ''. Fixed in 9e6e5fa5. MeApiResponseSchema.accessKey is z.string() (non-nullable), so the coalesce was defensive against a state the type disallows. Dropped it. The panel's empty-string guard still fires if a deployment somehow lands with an empty key.

P3: McpInstallPanel as AgentHost. Fixed in 9e6e5fa5. Replaced the cast on SegmentedControl.onChange with an isAgentHost(value) type guard derived from CHOICES. An out-of-band value now falls through silently instead of installing an invalid host.

P3: CopySnippet stale JSDoc. Fixed in 9e6e5fa5. Removed the ExporterFormatSelector.tsx reference (not in repo).

P3: CopySnippet color="gray" on subtle Button. Fixed in 9e6e5fa5. Dropped the redundant prop; variant="subtle" is the documented Mantine pattern in agent_docs/code_style.md and renders identically.

P3: buildClaudeCodeOneLiner / buildCodexCliOneLiner duplication. Fixed in 9e6e5fa5. Extracted buildCliOneLiner(binary, deployment). Both hosts share the generator and differ only in the binary name.

P3: McpInstallPanel Alert color="yellow". Carried over (not changed). Five existing call sites in the repo (DBInfraPanel, TileAlertEditor, ChartDisplaySettingsDrawer, DBDashboardPage, plus a Badge color="yellow" in KubeComponents) use color="yellow" for warning surfaces. Switching just these two Alerts to a semantic token would diverge from the rest of the surface; if we want to migrate the warning palette repo-wide, that's a separate cleanup. Happy to take that on as a follow-up if you'd prefer.

…omments

Strip JSDoc + inline comments in installSnippets.ts and
McpInstallPanel.tsx that referenced internal tracking labels
("AC16", "AC18") and downstream non-OSS deployment branches
("CHC managed (BYC)", "ClickStack Cloud", "CP MCP proxy", "EE
Team Settings page"). Those notes belonged in the tracking doc,
not in the OSS source.

Comments-only change; no logic, no test changes. The remaining
JSDoc still explains each builder's contract without speculating
about future deployment shapes.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@alex-fedotyev alex-fedotyev changed the title DO NOT MERGE: feat(team-settings): connect your AI assistant [HDX-4440] feat(team-settings): connect your AI assistant [HDX-4440] Jun 4, 2026
@alex-fedotyev alex-fedotyev self-assigned this Jun 4, 2026
const CHOICES: HostChoice[] = [
{ id: 'claude-code', label: 'Claude Code' },
{ id: 'cursor', label: 'Cursor' },
{ id: 'vscode-copilot', label: 'VS Code + Copilot' },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could just call this VS Code imo

{ id: 'claude-code', label: 'Claude Code' },
{ id: 'cursor', label: 'Cursor' },
{ id: 'vscode-copilot', label: 'VS Code + Copilot' },
{ id: 'codex-cli', label: 'Codex CLI' },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thoughts on dropping Codex for OpenCode (biased maybe). I know we have several devs internally who use OpenCode so this would help with adoption

The config for OpenCOde is slightky different

    "clickstack": {
      "type": "remote", <- Note not type http
      "url": "http://localhost:xxx/api/mcp",
      "headers": {
        "Authorization": "Bearer xxx"
      }
    },

// mocks. Skip in local mode until the e2e build flag is wired
// up; component coverage in `__tests__/McpServerSection.test.tsx`
// exercises the render branches in the meantime.
test.fixme(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test is disabled - we should either remove this (I think thats fine) or we should fix it so it runs if it can (instead of true do a conditional on local mode)

},
{
id: 'team-integrations-mcp-server',
content: <McpServerSection />,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thoughs on moving below APiKeys? I think that flows more naturally, I need to understand access keys before I can use the MCP


return (
<Box id="mcp_server" data-testid="mcp-server-section">
<Text size="md">Connect your AI assistant</Text>

@brandon-pereira brandon-pereira Jun 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thoughts?

Suggested change
<Text size="md">Connect your AI assistant</Text>
<Text size="md">Connect your AI Agents</Text>


{!deployment ? (
<Alert color="yellow" variant="light">
Sign in to load your personal access key before installing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can someone get to this state??? seems like this should be caught sooner

</Alert>
) : !deployment.accessKey ? (
<Alert color="yellow" variant="light">
No access key on this account yet. Ask an admin to create one and sign

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I thought access key was required, is there a state where its not defined?

}
// Exhaustiveness check: adding a new AgentHost variant without
// extending this switch fails the compile here.
return assertNever(id);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should return null here - crashing the whole page seems extreme

Move ApiKeysSection and McpServerSection out of Integrations and
into a new "API & Agents" tab on the Team Settings page. The
Integrations tab keeps webhooks only.

Rationale: API keys and the MCP install are programmatic-access
surfaces that pair naturally (the MCP server authenticates with
the same personal access key shown in the API Keys card).
Webhooks are a different category of integration and were sharing
the tab only by accident of where each section originally landed.

Section IDs renamed:
  team-integrations-api-keys     -> team-api-agents-api-keys
  team-integrations-mcp-server   -> team-api-agents-mcp-server

Tab URL value is `api-agents`. Placed between Integrations and
Query Settings.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@alex-fedotyev

Copy link
Copy Markdown
Contributor Author

Follow-up change in 8d20b140: split API Keys + AI Agents into their own tab.

Webhooks, API keys, and the MCP install were sharing the Integrations tab only because that's where each section first landed. API keys and the MCP install pair naturally (the MCP server authenticates with the personal access key shown in the API Keys card), and webhooks are a different category of integration. Easier to triage as separate tabs.

New tab: API & Agents (URL value api-agents), placed between Integrations and Query Settings. Contains the API Keys card and the AI Agents install panel.

Integrations tab: webhooks only.

Section IDs renamed for the moved sections:

  • team-integrations-api-keysteam-api-agents-api-keys
  • team-integrations-mcp-serverteam-api-agents-mcp-server

PR body updated to reflect the new layout. 22 jest tests still pass, tsc / eslint / prose-lint clean.

The new "API & Agents" tab is the more frequently visited surface
for self-managed deployments (personal access key + MCP install
sit there), so it should land earlier in the tab strip than
Integrations (webhooks).

Co-Authored-By: Claude Opus <noreply@anthropic.com>
OpenCode's MCP config lives under an `mcp` key (not `mcpServers`)
and uses `type: "remote"` for HTTP transport. Earlier reading of
the spec + the open anomalyco/opencode#8058 issue suggested that
shape only supports the deprecated HTTP+SSE transport and would
fail against our Streamable HTTP server with a 405. Verified
empirically against a running ClickStack instance on 2026-06-04:
pure `type: "remote"` config connects successfully. Either
OpenCode has shipped Streamable HTTP support since the issue was
filed or the SDK's StreamableHTTPServerTransport serves the
legacy handshake transparently; either way, the snippet works.

Adds:
- `BuiltSnippets.openCode` + `buildOpenCodeJsonBlock` builder in
  installSnippets.ts. JSDoc cites the docs URL and the empirical
  verification.
- 'opencode' variant in McpInstallPanel: AgentHost union, CHOICES
  entry (positioned before "Other"), HostIcon (IconBraces), and
  the HostInstall render branch. The install primitive is a
  CopySnippet with a label that names both config-file locations
  (project-local `opencode.json` and global
  `~/.config/opencode/config.json`).
- 3 new tests: shape verification (mcp + type: "remote"), negative
  assertion (must NOT emit `mcpServers` or `type: "http"`), and a
  panel-level test that exercises the render branch.

25 tests pass (was 22).

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@alex-fedotyev

Copy link
Copy Markdown
Contributor Author

Adding OpenCode in 6b11267c.

Earlier comment hedged that OpenCode's type: "remote" only supports the deprecated HTTP+SSE handshake (per anomalyco/opencode#8058) and would fail against our StreamableHTTPServerTransport server with a 405. Tested locally against a real ClickStack instance: the pure type: "remote" config connects and queries work. Either OpenCode has shipped Streamable HTTP support since the issue was filed, or the SDK's transport serves the legacy handshake transparently. Empirical answer trumps documentation; that's why we test these things.

The earlier observation that other hosts should NOT migrate to type: "remote" still stands. Claude Code, Cursor, VS Code, and Codex CLI all document type: "http". The type: field is per-client naming, not a transport-compatibility marker.

Snippet shape:

{
  "mcp": {
    "clickstack": {
      "type": "remote",
      "url": "<origin>/api/mcp",
      "headers": { "Authorization": "Bearer <accessKey>" }
    }
  }
}

UI: positioned between Codex CLI and Other in the segmented control. Install primitive is a CopySnippet with a label that names both config-file locations (project-local opencode.json and global ~/.config/opencode/config.json); no deep link, OpenCode doesn't expose one.

Tests: 25 pass (was 22). The 3 new tests are positive shape (mcp key + type: "remote"), negative assertion (must NOT emit mcpServers or type: "http"), and a panel-level render-branch check.

The earlier tab split moved ApiKeysSection out of Integrations and
into a dedicated "API & Agents" tab on TeamPage. Three e2e tests
in team.spec.ts still asserted the old layout (API Keys lives on
Integrations) and were failing Shard 4 with the structural change.

Changes:

TeamPage page-object (tests/e2e/page-objects/TeamPage.ts):
- Add apiAndAgentsTabButton locator (matches `API & Agents` tab).
- Add mcpServerSection locator (matches `mcp-server-section`
  testid on the new install panel).
- Add openApiAndAgentsTab() helper that mirrors the existing
  openTab pattern, waiting on the API Keys section to render.
- Expose apiAndAgentsTab and mcpServer getters for assertions.

team.spec.ts:
- "should load team page tabs and show all sections": assert the
  new API & Agents tab in the visible-tabs check; add steps that
  open the new tab and assert ApiKeysSection + McpServerSection
  render with their headings. Integrations tab steps now only
  assert webhooks (no API Keys).
- "should display API keys": switch openIntegrationsTab() to
  openApiAndAgentsTab().
- "should open and cancel rotate API key modal": same switch.

Webhook tests (createWebhook, redact-secrets) are unchanged; they
still belong on the Integrations tab and still pass.

Co-Authored-By: Claude Opus <noreply@anthropic.com>
@alex-fedotyev alex-fedotyev added automerge ai-generated AI-generated content; review carefully before merging. and removed review/tier-3 Standard — full human review required labels Jun 4, 2026
@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Jun 4, 2026
@alex-fedotyev

Copy link
Copy Markdown
Contributor Author

@brandon-pereira - implemented your feedback, moved API & AI into separate tab to make them easier to find

@brandon-pereira brandon-pereira left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM - thanks!

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a "Connect your AI Agents" install panel to Team Settings under a new "API & Agents" tab, giving users a one-click or copy-paste path to register the ClickStack MCP server in Claude Code, Cursor, VS Code, Codex CLI, OpenCode, or any generic MCP host.

  • installSnippets.ts builds all six host snippets as pure functions with shell-escape and URL-safe base64 handling; comprehensive unit tests cover every host and escape edge case.
  • McpInstallPanel.tsx / DeeplinkInstall.tsx / CopySnippet.tsx compose the UI, and McpServerSection.tsx gates rendering on a non-empty accessKey from useMe(), mirroring the existing ApiKeysSection pattern.
  • TeamPage.tsx moves ApiKeysSection out of Integrations and into the new "API & Agents" tab; e2e page-object and spec are updated accordingly.

Confidence Score: 3/5

Safe to merge after fixing the VS Code manual-setup JSON format; all other paths are correct and well-tested.

The VS Code manual-setup fallback uses the wrong JSON top-level key (mcpServers instead of servers), silently breaking that install path for users who cannot use the deep link.

packages/app/src/components/ClickStackOnboarding/McpInstallPanel.tsx and installSnippets.ts need a dedicated VS Code JSON builder emitting the servers key.

Important Files Changed

Filename Overview
packages/app/src/components/ClickStackOnboarding/installSnippets.ts Pure snippet builders for 6 agent hosts; shell-escape and base64url logic is well-tested, but no separate VS Code .vscode/mcp.json format is defined (VS Code uses servers, not mcpServers)
packages/app/src/components/ClickStackOnboarding/McpInstallPanel.tsx Host picker + install primitive renderer; VS Code manual fallback uses jsonBlock (mcpServers key) which is incorrect for .vscode/mcp.json (needs servers key)
packages/app/src/components/TeamSettings/McpServerSection.tsx Auth-gated wrapper deriving deployment shape from window.location.origin + useMe().accessKey; mirrors ApiKeysSection pattern cleanly
packages/app/src/components/ClickStackOnboarding/tests/installSnippets.test.ts Comprehensive round-trip tests for all 6 hosts including shell-escape and URL-safe base64 coverage
packages/app/tests/e2e/features/team.spec.ts Adds API & Agents tab e2e steps; mcpServer visibility assertion may be fragile in environments where useMe() returns no accessKey
.changeset/connect-your-ai-assistant.md Changeset note says the section is on the 'Integrations tab' but the implementation puts it in the new 'API & Agents' tab

Comments Outside Diff (1)

  1. packages/app/src/components/ClickStackOnboarding/McpInstallPanel.tsx, line 347-360 (link)

    P1 VS Code manual-setup fallback uses the wrong JSON format

    The fallback snippet for VS Code is snippets.jsonBlock, which uses the Claude Desktop / generic mcpServers top-level key. VS Code's .vscode/mcp.json schema uses servers as the top-level key (confirmed in the VS Code MCP docs). A user who opens Manual setup under the VS Code tab and pastes the displayed JSON into .vscode/mcp.json will get no server configured (VS Code silently ignores unknown top-level keys). installSnippets.ts needs a dedicated buildVSCodeJsonBlock that emits servers instead of mcpServers, and McpInstallPanel should pass snippets.vscodeJsonBlock as fallbackSnippet for the vscode-copilot case.

    Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "Merge branch 'main' into alex/HDX-4440-t..." | Re-trigger Greptile

Comment on lines +8 to +11
card) lets a user install the HyperDX MCP server in Claude Code, Cursor,
VS Code + Copilot, Codex CLI, or any MCP-compatible host without hand-rolling
JSON. Per-host snippets carry the user's personal access key so the install
works against the existing `/api/mcp` route without extra setup.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Changeset description references the wrong tab

The note says the section lives on the "Integrations tab, above the API Keys card", but the actual implementation introduces a dedicated "API & Agents" tab and removes the API Keys card from Integrations entirely. The user-facing change note will be misleading in the changelog.

Suggested change
card) lets a user install the HyperDX MCP server in Claude Code, Cursor,
VS Code + Copilot, Codex CLI, or any MCP-compatible host without hand-rolling
JSON. Per-host snippets carry the user's personal access key so the install
works against the existing `/api/mcp` route without extra setup.
A new "API & Agents" tab on the Team Settings page lets a user install the
HyperDX MCP server in Claude Code, Cursor, VS Code + Copilot, Codex CLI,
OpenCode, or any MCP-compatible host without hand-rolling JSON. Per-host
snippets carry the user's personal access key so the install works against
the existing `/api/mcp` route without extra setup. The API Keys card has
moved to this tab alongside the new install panel.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Comment on lines +116 to +119
function shellQuoteHeader(name: string, value: string): string {
const escaped = value.replace(/(["\\$`])/g, '\\$1');
return `--header "${name}: ${escaped}"`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Newline characters in the header value are not escaped

shellQuoteHeader escapes ", \, $, and backtick, but a \n inside the value would split the printed command across multiple lines in a way that corrupts it visually. The comment explicitly calls out future key format changes as the motivation for this escaping, so covering \n and \r would make the defensive story complete.

Suggested change
function shellQuoteHeader(name: string, value: string): string {
const escaped = value.replace(/(["\\$`])/g, '\\$1');
return `--header "${name}: ${escaped}"`;
}
function shellQuoteHeader(name: string, value: string): string {
const escaped = value
.replace(/(["\\$`])/g, '\\$1')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r');
return `--header "${name}: ${escaped}"`;
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@kodiakhq kodiakhq Bot merged commit 20fabc6 into main Jun 5, 2026
22 checks passed
@kodiakhq kodiakhq Bot deleted the alex/HDX-4440-team-settings-mcp-install branch June 5, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-generated AI-generated content; review carefully before merging. automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants