Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions .cursor-mcp.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
{
"mcpServers": {
"bstack": {
"command": "npx",
"args": ["-y", "@browserstack/mcp-server@1.2.27-beta.1"],
"env": {
"BROWSERSTACK_USERNAME": "${BROWSERSTACK_USERNAME}",
"BROWSERSTACK_ACCESS_KEY": "${BROWSERSTACK_ACCESS_KEY}",
"O11Y_TFA_RCA_BASE_URL": "${O11Y_TFA_RCA_BASE_URL}"
}
"type": "http",
"url": "https://mcp.browserstack.com/mcp?isTfaPlugin=true"
}
}
}
8 changes: 0 additions & 8 deletions .env.example

This file was deleted.

10 changes: 2 additions & 8 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
{
"mcpServers": {
"bstack": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@browserstack/mcp-server@1.2.27-beta.1"],
"env": {
"BROWSERSTACK_USERNAME": "${BROWSERSTACK_USERNAME}",
"BROWSERSTACK_ACCESS_KEY": "${BROWSERSTACK_ACCESS_KEY}",
"O11Y_TFA_RCA_BASE_URL": "${O11Y_TFA_RCA_BASE_URL}"
}
"type": "http",
"url": "https://mcp.browserstack.com/mcp?isTfaPlugin=true"
}
}
}
4 changes: 4 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ engine-strict=true
legacy-peer-deps=false
audit-level=high
access=public
# Wait 7 days before resolving a newly published version: a package compromised at
# publish time is usually caught and unpublished inside that window. Requires npm
# >= 11.10; older npm ignores the key rather than failing.
min-release-age=7
32 changes: 20 additions & 12 deletions INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ orchestration. On every host the run finishes the same way: glimpse table →
## Claude Code

```bash
cp .env.example .env # BROWSERSTACK_USERNAME / BROWSERSTACK_ACCESS_KEY
claude --plugin-dir ./
/rca-build <build-id>
/tfa-rca:rca-build <build-id>
```

No credentials to set: `.mcp.json` points at the hosted server and Claude Code runs
the OAuth flow on first connect. `/mcp` shows the connection and re-triggers sign-in.

`.claude-plugin/plugin.json` + root `.mcp.json` + `skills/` + `agents/` are
auto-discovered. (No `commands/rca-build.md` on purpose — a command and skill
with the same name collide and the skill body fails to load.)
Expand All @@ -43,7 +45,7 @@ with the same name collide and the skill body fails to load.)

The repo ships Cursor parity files mirroring `slack-mcp-plugin`:
`.cursor-plugin/plugin.json` (points at `../.cursor-mcp.json` and `./skills/`)
and `.cursor-mcp.json` (the stdio `bstack` server).
and `.cursor-mcp.json` (the hosted `bstack` server over HTTP).

**Wire the MCP server** — either:
- copy `.cursor-mcp.json`'s `bstack` entry into your project `.cursor/mcp.json`
Expand All @@ -52,8 +54,8 @@ and `.cursor-mcp.json` (the stdio `bstack` server).
- use an **Add to Cursor** deeplink:
`cursor://anysphere.cursor-deeplink/mcp/install?name=bstack&config=<base64-of-the-bstack-entry-body>`

Set `BROWSERSTACK_USERNAME` / `BROWSERSTACK_ACCESS_KEY` / `O11Y_TFA_RCA_BASE_URL`
in your environment (or replace the `${…}` placeholders with literals).
Nothing else to set — the entry carries only `type` and `url`, and Cursor performs the
OAuth sign-in itself. There are no environment variables and no placeholders to fill.

**Skill + agent discovery** — Cursor reads `.cursor/skills/` and `.cursor/agents/`
(and also `.claude/agents/`). The simplest no-duplication setup is to symlink the
Expand All @@ -75,12 +77,14 @@ Codex reads the global `~/.codex/config.toml` (no per-project MCP file).
into `~/.codex/config.toml`, or:

```bash
codex mcp add bstack \
--env BROWSERSTACK_USERNAME=… --env BROWSERSTACK_ACCESS_KEY=… \
--env O11Y_TFA_RCA_BASE_URL=https://api-observability-rengg-tfa.bsstag.com \
-- npx -y @browserstack/mcp-server@1.2.27-beta.1
codex mcp add bstack --url "https://mcp.browserstack.com/mcp?isTfaPlugin=true"
```

Codex's own key for a streamable-HTTP server is `url`, and `auth = "oauth"` is its
documented fallback when no bearer token or static header is configured — which is this
case. No experimental flag is needed; `experimental_environment = "remote"` gates remote
*stdio executors*, a different feature.

**Skill + agent discovery** — Codex reads `.agents/skills/` (skills) and
`.codex/agents/` (subagents). Symlink the shared trees:

Expand All @@ -94,9 +98,13 @@ Then run the `rca-build` skill; the coordinator + `tfaRcaTurn` loop are identica

## Notes

- The `bstack` server is **stdio** (`npx @browserstack/mcp-server@1.2.27-beta.1`), not a remote
OAuth server — so the configs use `command`/`args`/`env`, unlike Slack's
`url`+`oauth`/`auth` shape.
- The `bstack` server is **remote HTTP with OAuth** — `type`/`url` in Claude Code and
Cursor, `url`/`auth` in Codex. No `command`, no `args`, no `env`, and no credential in
any config file.
- **`?isTfaPlugin=true` is load-bearing.** The server registers the TFA RCA
collaboration tools per-request only when that query parameter is present. Drop it and
the plugin loads with nothing to call, which looks like a broken install rather than a
missing flag. Query parameters in the `url` are supported by all three clients.
- Env-var interpolation (`${VAR}`) is honored by Claude Code's `.mcp.json`; on
Cursor/Codex, replace the placeholders with literals if your client doesn't
expand them.
Expand Down
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ Works in **Claude Code**, **Cursor** and **Codex**.
| You need | Why |
|---|---|
| A BrowserStack account with Test Observability | The build, its test logs, and the dashboard the report lands on |
| `BROWSERSTACK_USERNAME` + `BROWSERSTACK_ACCESS_KEY` | From your [account settings](https://www.browserstack.com/accounts/profile/details) |
| **GitHub access** — the `gh` CLI signed in, or a GitHub MCP server | Required. Without your code and its merged PRs there is no culprit PR to name, and that is the point of the run |
| Anything else you use — logs, metrics, a cluster, CI | Optional. Each one you skip is recorded and shown in the report as evidence that was not available |

GitHub is the only hard requirement. Everything else is offered, never forced.

**There are no BrowserStack credentials to configure.** The plugin talks to
BrowserStack's hosted MCP server and your client signs you in on first connect. Nothing
is stored in this repo, and there is no `.env` to fill in.

## Install

In Claude Code:
Expand All @@ -34,23 +37,16 @@ In Claude Code:
/plugin install tfa-rca@browserstack-ai-tfa
```

Then set your credentials — the plugin needs them before it can read anything:

```bash
export BROWSERSTACK_USERNAME=... # from your account settings
export BROWSERSTACK_ACCESS_KEY=...
```

Ask Claude to **run the plugin's setup** and it will check both of these, confirm your
GitHub route, and tell you exactly what is missing if anything is.
The first time it connects, your client walks you through signing in to BrowserStack.
That is the whole credential step. Ask Claude to **run the plugin's setup** if you want
that and your GitHub route checked before you start.

<details>
<summary>Installing from a clone instead (for development)</summary>

```bash
git clone https://github.com/browserstack/ai-tfa-plugins.git
cd ai-tfa-plugins
cp .env.example .env # add your BrowserStack username + access key
claude --plugin-dir ./
```

Expand Down
81 changes: 40 additions & 41 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,68 @@
---
name: setup
description: One-time setup for the tfa-rca plugin — proves the bundled BrowserStack MCP server can authenticate and that a GitHub route exists, then hands off to /tfa-rca:rca-build. Run this on install, or whenever the plugin reports that it cannot reach BrowserStack. Does NOT configure repos, logs, metrics or CI — the rca-build interview owns those.
description: One-time setup for the tfa-rca plugin — confirms the hosted BrowserStack MCP server is connected and that a GitHub route exists, then hands off to /tfa-rca:rca-build. Run this on install, or whenever the plugin reports that it cannot reach BrowserStack. Does NOT configure repos, logs, metrics or CI — the rca-build interview owns those.
---

# Setting up tfa-rca

**Scope.** This gets the two things the plugin cannot start without: the bundled
`bstack` MCP server authenticating, and a GitHub route existing. Everything else —
**Scope.** This gets the two things the plugin cannot start without: the hosted
`bstack` MCP server connected, and a GitHub route existing. Everything else —
which repos, which branches, where the logs are, what runs the services — is settled
by `/tfa-rca:rca-build`'s own first-contact interview, which writes
`.rca-context.json` in the user's project. **Do not ask about any of that here.**
Asking twice reads as not having listened the first time.

**Step 1 gates everything.** If the `bstack` tools are not in this session the user is
not signed in, and nothing further can be checked or run — stop there and ask them to
authorise, rather than working down the list reporting failures that all have one cause.

Work through the steps in order and report each outcome in one line. Nothing here
writes a file.

## 1. Credentials for the bundled MCP server

The `bstack` server needs `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY`.
Check whether they are already present in the environment.
## 1. Connect to BrowserStack

If either is missing, tell the user exactly this and stop — do not proceed to step 2:
The `bstack` server is BrowserStack's hosted MCP endpoint, and it authenticates by
**OAuth** — the client runs the sign-in flow. There are no credentials to set, no
`.env` to fill in, and nothing for you to record.

> Add your BrowserStack credentials, then reload the plugin. Copy `.env.example` to
> `.env` and fill in `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY` — both are
> on your [account settings](https://www.browserstack.com/accounts/profile/details)
> page. Exporting them in your shell works too.
Check whether the `bstack` tools are available in this session — `fetchBuildInsights`,
`listTestIds`, `tfaRcaTurn`.

**Never write a credential value anywhere, and never echo one back.** If the user
pastes a key into the conversation, say that the value is now in the transcript and
should be revoked and reissued, then ask them to put the new one in `.env` and tell
you only that it is set. You record that a variable is set; you never record what is
in it.
- **They are there** — the connection is up. Move to step 2.
- **They are not** — the user is not signed in, and this is where you stop. Ask for it
directly and do not continue to step 2:

`O11Y_TFA_RCA_BASE_URL` is optional and almost always unset — only a customer on a
non-default tenant needs it. Do not ask for it.
> I can't reach BrowserStack yet. Authorise the `bstack` MCP server — run `/mcp` in
> Claude Code and approve the sign-in, or accept the prompt your client shows — then
> ask me again.

## 2. Confirm the server loaded — and say what is still unproven
If the server is not listed **at all** (as opposed to listed and unauthorised), that
is wiring rather than sign-in: **[INTEGRATION.md](INTEGRATION.md)** has the fix per
client. Say which of the two it is; they have different fixes and guessing wastes a
round trip.

Two different things can be wrong, and they have different fixes, so separate them.
**Never ask for a username, an access key, or any token.** This route does not use
them, so a request for one is both useless and a prompt to paste a secret into a
transcript. If the user offers one anyway, say it is not needed and that they should
treat any value they pasted as disclosed — revoke and reissue it.

**Did the server load?** Check whether the `bstack` tools — `fetchBuildInsights`,
`listTestIds`, `tfaRcaTurn` — are available in this session. If they are not, the MCP
server did not start, which is client wiring rather than credentials:
**[INTEGRATION.md](INTEGRATION.md)** has the fix per client. Point at the section for
the client actually in use.
## 2. Confirm it can actually read — and say what is still unproven

**Do the credentials work?** Every Observability read on this server needs a build to
read, so there is no free call that proves authentication on its own. Handle it
honestly, in one of two ways:
Being connected is not the same as being able to read this account's builds. Every
Observability read needs a build to read, so there is no free call that proves access
on its own. Handle it honestly, in one of two ways:

- **The user has a build id to hand** — use it, and you have real proof:
`fetchBuildInsights(buildId=<id>)`. Returning the build's name and status means
credentials, entitlement and connectivity all work. An auth error means the values
are being read and rejected, so rotating or re-copying the access key is the fix,
not re-exporting them.
- **They do not** — say plainly that the variables are set and that the first real
authenticated read happens on their first `/tfa-rca:rca-build` run, which fails
loudly and immediately if the credentials are wrong. Do not call an unrelated tool
to manufacture a probe: a Test Management read succeeding or failing says nothing
reliable about Observability access, and reporting it as proof would be worse than
reporting nothing.

**Never say "verified" for a check you did not run.** "Variables are set, not yet
exercised" is the accurate sentence when no build was read, and it is the one to use.
the sign-in, the entitlement and the connection all work.
- **They do not** — say plainly that the connection is up and that the first real read
happens on their first `/tfa-rca:rca-build` run, which fails loudly and immediately
if authorisation is wrong. Do not call an unrelated tool to manufacture a probe: a
Test Management read succeeding or failing says nothing reliable about Observability
access, and reporting it as proof would be worse than reporting nothing.

**Never say "verified" for a check you did not run.** "Connected, not yet exercised"
is the accurate sentence when no build was read, and it is the one to use.

## 3. A GitHub route — the one hard requirement

Expand Down
21 changes: 15 additions & 6 deletions codex-mcp.example.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
# Codex MCP wiring for the bstack server.
# Codex reads ~/.codex/config.toml (no per-project MCP file), so copy this block
# into your global config — or use the `codex mcp add` one-liner in INTEGRATION.md.
# Replace the env values with your BrowserStack credentials.
# Codex reads ~/.codex/config.toml (no per-project MCP file), so copy this block into
# your global config — or add it in one line:
#
# codex mcp add bstack --url "https://mcp.browserstack.com/mcp?isTfaPlugin=true"
#
# Same hosted server the Claude Code and Cursor configs use (.mcp.json /
# .cursor-mcp.json). No credentials live here: `auth = "oauth"` makes Codex run the
# sign-in flow on first connect, and it is the documented fallback when no bearer token
# or static header is configured.
#
# `isTfaPlugin=true` is required, not decorative — the server registers the TFA RCA
# collaboration tools per-request only when it is present. Without it the plugin loads
# and has nothing to call.

[mcp_servers.bstack]
command = "npx"
args = ["-y", "@browserstack/mcp-server@1.2.27-beta.1"]
env = { "BROWSERSTACK_USERNAME" = "your-username", "BROWSERSTACK_ACCESS_KEY" = "your-access-key", "O11Y_TFA_RCA_BASE_URL" = "" # optional: set only to target a staging tenant (default is production) }
url = "https://mcp.browserstack.com/mcp?isTfaPlugin=true"
auth = "oauth"
startup_timeout_sec = 15
tool_timeout_sec = 120
11 changes: 10 additions & 1 deletion skills/rca-build/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
---
name: rca-build
description: Autonomous batch RCA over every failed test of a BrowserStack build via tfaRcaTurn. First contact interviews you once and writes .rca-context.json; every run after that is one gate (context validation + resolved intake) then fully autonomous — clusters failures, routes evidence, triggers the dashboard report. Args: build id, optional PR URLs / repo hints.
description: Autonomous batch RCA over every failed test of a BrowserStack build via tfaRcaTurn. Needs the hosted bstack MCP server authenticated (OAuth) — if its tools are absent, ask the user to sign in before anything else. First contact interviews you once and writes .rca-context.json; every run after that is one gate (context validation + resolved intake) then fully autonomous — clusters failures, routes evidence, triggers the dashboard report. Args: build id, optional PR URLs / repo hints.
---

# rca-build — single-gate autonomous RCA over a build

> **FIRST, before anything else: if the `bstack` MCP tools are not in this session, stop
> and ask the user to sign in.** The server is BrowserStack's hosted endpoint and it
> authenticates by OAuth, so an unauthenticated session has no `tfaRcaTurn`,
> `listTestIds` or `fetchBuildInsights` — every step below is dead and the failure looks
> like a broken plugin rather than a missing sign-in. Say plainly: *"I can't reach
> BrowserStack yet — authorise the `bstack` MCP server (`/mcp`) and re-run."* Do not
> start the interview, do not read the context file, and never ask for a username or
> access key: this route does not use them.

Drives the `tfaRcaTurn` collaborative loop over **every failed test** of a build
and lands a per-test RCA in the TRA (Test Observability) dashboard. **TFA owns
logs; the client agent owns everything else** (product code, infra/runtime, logs,
Expand Down
Loading
Loading