diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8dae338c..fc11d2fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: printf 'dist_repo=%s\n' "$dist_repo" printf 'paths<.zip` to the distribution GitHub Release. OpenAI submission remains manual. + Claude releases update `plugins/braintrust` in the distribution repository. Anthropic directory submission remains manual, from the developer portal. + Human approval and merge into `main` authorize tagging the merge commit and publishing this version to the production destination. This preparation does not tag or deploy anything. - name: Summarize release pull request diff --git a/AGENTS.md b/AGENTS.md index 28ea4158..cf584a2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,9 +67,9 @@ Versioning is per distribution. Claude, Codex, Cursor, and Grok plugins carry their version in their plugin manifests. `scripts/set-plugin-version.py` updates those manifests for a release. Antigravity's schema does not expose a version field, so its distribution is versioned by repository tags and GitHub Releases. -Claude's marketplace manifest carries a separate catalog version; bump it -independently for breaking marketplace changes. The Codex marketplace manifest -is unversioned. +Claude's marketplace manifest carries a catalog version that the same script +keeps in sync with its plugins; Claude Code does not read it. The Codex +marketplace manifest is unversioned. | Agent | Distribution repository | |---|---| diff --git a/README.md b/README.md index 94bbe5ba..626fcba2 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,12 @@ This monorepo contains the plugins and the shared Rust tracing daemon embedded in the Braintrust CLI (`bt`). Plugins forward native events locally; the daemon builds traces, journals events for recovery, and delivers spans to Braintrust. -This repository also packages the [Braintrust -plugin](src/plugins/codex/content/plugins/braintrust/README.md) for submission to -the official ChatGPT and Codex plugin directory. It connects to Braintrust's -hosted MCP server through OAuth and requires no `bt` installation. Installing it -does not enable session tracing. +This repository also packages the Braintrust MCP plugin for the official +directories: the [Codex package](src/plugins/codex/content/plugins/braintrust/README.md) +for ChatGPT and Codex, and the [Claude +package](src/plugins/claude/content/plugins/braintrust/README.md) for Anthropic's +directory. Each connects to Braintrust's hosted MCP server through OAuth and +requires no `bt` installation. Installing it does not enable session tracing. ## Get started diff --git a/scripts/set-plugin-version.py b/scripts/set-plugin-version.py index 22ffdfdf..185d14ce 100644 --- a/scripts/set-plugin-version.py +++ b/scripts/set-plugin-version.py @@ -2,7 +2,8 @@ """Set a release version on every distributed version surface for an agent. Versioning is per distribution: portable root manifests and compatibility -manifests share one version. Marketplace manifests are NOT touched. +manifests share one version. Claude's marketplace catalog version follows its +plugins; other marketplace manifests are NOT touched. Only version values are rewritten, so surrounding files keep their formatting. @@ -43,6 +44,11 @@ def main() -> None: manifests.extend(matches) surfaces = [(path, VERSION_RE, "version") for path in manifests] + if agent == "claude": + # Top-level catalog version; plugin entries carry no version of their own. + surfaces.append( + ("src/plugins/claude/content/.claude-plugin/marketplace.json", VERSION_RE, "catalog version") + ) if agent == "cursor": content = "src/plugins/cursor/content" surfaces.extend([ diff --git a/src/plugins/claude/build.sh b/src/plugins/claude/build.sh index a7676728..8bc99429 100755 --- a/src/plugins/claude/build.sh +++ b/src/plugins/claude/build.sh @@ -4,6 +4,7 @@ # # The Claude Code marketplace consumes a repo whose ROOT is the marketplace: # .claude-plugin/marketplace.json marketplace manifest +# plugins/braintrust/ hosted MCP plugin (Anthropic directory listing) # plugins/trace-claude-code/ tracing plugin (thin daemon hook shim) # # Everything is plain shell + config — no compiled artifacts — so the whole diff --git a/src/plugins/claude/content/.claude-plugin/marketplace.json b/src/plugins/claude/content/.claude-plugin/marketplace.json index d6f66bd9..0729b7e6 100644 --- a/src/plugins/claude/content/.claude-plugin/marketplace.json +++ b/src/plugins/claude/content/.claude-plugin/marketplace.json @@ -1,13 +1,19 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "braintrust-claude-plugin", - "version": "2.0.0", - "description": "Braintrust session tracing for Claude Code", + "version": "3.0.5", + "description": "Braintrust MCP tools and session tracing for Claude Code", "owner": { "name": "Braintrust", "email": "support@braintrust.dev" }, "plugins": [ + { + "name": "braintrust", + "description": "Explore Braintrust traces, manage prompts and datasets, and run evaluations through the hosted Braintrust MCP server.", + "source": "./plugins/braintrust", + "category": "development" + }, { "name": "trace-claude-code", "description": "Automatically trace Claude Code conversations to Braintrust. Captures user messages, assistant responses, and tool calls for observability.", diff --git a/src/plugins/claude/content/AGENTS.md b/src/plugins/claude/content/AGENTS.md index b631f338..3ccc0c6d 100644 --- a/src/plugins/claude/content/AGENTS.md +++ b/src/plugins/claude/content/AGENTS.md @@ -2,7 +2,7 @@ ## About this repository -This is the **Braintrust Claude Code plugin marketplace** - a repository that distributes Claude Code tracing for Braintrust. +This is the **Braintrust Claude Code plugin marketplace** - a generated repository that distributes the Braintrust MCP plugin and Claude Code tracing for Braintrust. ### Structure @@ -11,6 +11,7 @@ claude-plugin/ ├── .claude-plugin/ │ └── marketplace.json # Marketplace catalog (lists available plugins) ├── plugins/ +│ ├── braintrust/ # Plugin: Hosted Braintrust MCP server (directory listing) │ └── trace-claude-code/ # Plugin: Session tracing to Braintrust └── evals/ # Evaluation suite for Braintrust MCP behavior ``` @@ -19,8 +20,15 @@ claude-plugin/ | Plugin | Description | |--------|-------------| +| `braintrust` | Connects to the hosted Braintrust MCP server over HTTP with OAuth. No hooks, skills, commands, or local processes. Submitted to Anthropic's directory. | | `trace-claude-code` | Forwards Claude Code lifecycle hooks to `bt trace hook --source claude-code`; the shared daemon builds and delivers traces. | +Both plugins share one release version. Make source changes in +`braintrustdata/braintrust-coding-agent-plugins` and release through its Release +Plugin workflow; do not edit or tag this generated repository manually. Never +rename the `braintrust` plugin: directory and marketplace installs are keyed +by name. + ### Terminology - **Marketplace**: A repository with a `marketplace.json` that catalogs multiple plugins for distribution diff --git a/src/plugins/claude/content/README.md b/src/plugins/claude/content/README.md index 1f088c31..982c0589 100644 --- a/src/plugins/claude/content/README.md +++ b/src/plugins/claude/content/README.md @@ -1,11 +1,26 @@ -# Braintrust tracing for Claude Code +# Braintrust plugins for Claude Code > **This repository is generated.** It is built from > [braintrustdata/braintrust-coding-agent-plugins](https://github.com/braintrustdata/braintrust-coding-agent-plugins). > Don't edit files here — make changes and file issues in that repository, and they > will be rebuilt into this one. -A Claude Code plugin marketplace for tracing Claude Code sessions to [Braintrust](https://braintrust.dev). +This marketplace contains two independently opt-in plugins: + +- **Braintrust**, `braintrust@braintrust-claude-plugin`: hosted MCP tools with + OAuth. No `bt` dependency or tracing hooks. See the + [MCP installation and data plane guide](plugins/braintrust/README.md). +- **Trace Claude Code**, `trace-claude-code@braintrust-claude-plugin`: session + tracing to [Braintrust](https://braintrust.dev) via `bt`. + +For MCP, install Braintrust from this marketplace: + +```bash +claude plugin marketplace add braintrustdata/braintrust-claude-plugin +claude plugin install braintrust@braintrust-claude-plugin +``` + +The sections below describe optional tracing setup only. ## Prerequisites @@ -43,8 +58,8 @@ Every registered lifecycle event is forwarded synchronously to `bt trace hook --source claude-code`, preserving per-session ordering. Hook failures never fail a Claude Code turn. -This marketplace does not install or configure the Braintrust MCP server. Use -your agent's native connector or MCP configuration when you want MCP access. +Tracing setup does not install the Braintrust MCP plugin. MCP sign-in and `bt` +tracing sign-in are independent. ## Data handling diff --git a/src/plugins/claude/content/plugins/braintrust/.claude-plugin/icon.png b/src/plugins/claude/content/plugins/braintrust/.claude-plugin/icon.png new file mode 100644 index 00000000..e1cca9e0 Binary files /dev/null and b/src/plugins/claude/content/plugins/braintrust/.claude-plugin/icon.png differ diff --git a/src/plugins/claude/content/plugins/braintrust/.claude-plugin/plugin.json b/src/plugins/claude/content/plugins/braintrust/.claude-plugin/plugin.json new file mode 100644 index 00000000..f65b31d3 --- /dev/null +++ b/src/plugins/claude/content/plugins/braintrust/.claude-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", + "name": "braintrust", + "displayName": "Braintrust", + "version": "3.0.5", + "description": "Explore Braintrust traces, manage prompts and datasets, and run evaluations.", + "author": { + "name": "Braintrust", + "email": "support@braintrust.dev", + "url": "https://www.braintrust.dev/" + }, + "homepage": "https://www.braintrust.dev/docs/integrations/developer-tools/mcp", + "repository": "https://github.com/braintrustdata/braintrust-coding-agent-plugins", + "license": "MIT", + "keywords": ["braintrust", "evaluations", "observability", "mcp"], + "icon": "./.claude-plugin/icon.png", + "documentationUrl": "https://www.braintrust.dev/docs/integrations/developer-tools/mcp", + "supportUrl": "https://www.braintrust.dev/docs/kb/troubleshooting-guides", + "privacyPolicyUrl": "https://www.braintrust.dev/legal/privacy-policy", + "termsOfServiceUrl": "https://www.braintrust.dev/legal/terms-of-service" +} diff --git a/src/plugins/claude/content/plugins/braintrust/.mcp.json b/src/plugins/claude/content/plugins/braintrust/.mcp.json new file mode 100644 index 00000000..a6149a03 --- /dev/null +++ b/src/plugins/claude/content/plugins/braintrust/.mcp.json @@ -0,0 +1,6 @@ +{ + "braintrust": { + "type": "http", + "url": "https://api.braintrust.dev/mcp" + } +} diff --git a/src/plugins/claude/content/plugins/braintrust/LICENSE b/src/plugins/claude/content/plugins/braintrust/LICENSE new file mode 100644 index 00000000..f8a1f4ae --- /dev/null +++ b/src/plugins/claude/content/plugins/braintrust/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Braintrust Data, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/plugins/claude/content/plugins/braintrust/README.md b/src/plugins/claude/content/plugins/braintrust/README.md new file mode 100644 index 00000000..1098e05b --- /dev/null +++ b/src/plugins/claude/content/plugins/braintrust/README.md @@ -0,0 +1,60 @@ +# Braintrust + +Query traces, investigate failures, manage prompts and datasets, and run +evaluations using the hosted [Braintrust MCP server](https://www.braintrust.dev/docs/integrations/developer-tools/mcp). +Requires a Braintrust account with access to your organization's data plane. +Tools use that account's permissions and can read or change Braintrust data. + +## Install + +```text +/plugin marketplace add braintrustdata/braintrust-claude-plugin +/plugin install braintrust@braintrust-claude-plugin +``` + +Or from your shell: + +```bash +claude plugin marketplace add braintrustdata/braintrust-claude-plugin +claude plugin install braintrust@braintrust-claude-plugin +``` + +Then run `/mcp`, select **braintrust**, and complete the browser sign-in. +Try "Show me my Braintrust projects" or "Summarize recent errors in my +Braintrust project." + +Installing this plugin does not enable session tracing. To trace Claude Code +sessions to Braintrust, use the separate `trace-claude-code` plugin through +`bt trace enable claude`. + +## What this plugin runs and sends + +This plugin contains one remote MCP server, `braintrust`, reached over +Streamable HTTP at `https://api.braintrust.dev/mcp`. It has no hooks, skills, +commands, scripts, or local processes, and it does not require the `bt` CLI. + +Sign-in uses OAuth, which Claude Code discovers from the server. The plugin +contains no API key, client secret, or authorization header. Claude sends the +arguments of each Braintrust tool call it makes, such as project names, SQL +queries, prompt contents, or dataset rows, to that server, and receives +Braintrust data in response. + +## Other data planes + +The packaged server URL is Braintrust's North America (US) data plane. To use +another data plane, copy this plugin into your own marketplace and replace +`braintrust.url` in `.mcp.json` with the URL for your deployment. +Do not enable this plugin and a second connection to the same tools at once. + +| Data plane | MCP URL | +| --- | --- | +| North America (US) | `https://api.braintrust.dev/mcp` | +| EU | `https://api-eu.braintrust.dev/mcp` | +| Custom / self-hosted | Copy the complete MCP URL from **Settings > Data plane**. | + +## Support + +See [Braintrust troubleshooting](https://www.braintrust.dev/docs/kb/troubleshooting-guides) +or contact [support@braintrust.dev](mailto:support@braintrust.dev). +[Privacy policy](https://www.braintrust.dev/legal/privacy-policy) · +[Terms of service](https://www.braintrust.dev/legal/terms-of-service). diff --git a/src/plugins/claude/validate.sh b/src/plugins/claude/validate.sh index e322c487..7e38a8bf 100755 --- a/src/plugins/claude/validate.sh +++ b/src/plugins/claude/validate.sh @@ -3,7 +3,8 @@ # validate.sh — Sanity-check a built Claude Code dist tree in $1 before publishing. # # Fails (non-zero) on the mistakes that would ship a broken marketplace: -# - missing marketplace manifest / tracing plugin manifest / hooks +# - missing marketplace manifest / plugin manifests / hooks +# - an MCP plugin that ships anything beyond its hosted server connection # - malformed JSON in any manifest # - marketplace entries whose `source` path does not exist in the tree # @@ -30,6 +31,8 @@ check_json "$MARKETPLACE" # Required files for each shipped plugin. required=( + "plugins/braintrust/.claude-plugin/plugin.json" + "plugins/braintrust/.mcp.json" "plugins/trace-claude-code/.claude-plugin/plugin.json" "plugins/trace-claude-code/.cursor-plugin/plugin.json" "plugins/trace-claude-code/hooks/hooks.json" @@ -40,7 +43,7 @@ for rel in "${required[@]}"; do done python3 - "$MARKETPLACE" "$TARGET_DIR/plugins" <<'PY' \ - || fail "Claude marketplace must contain only the tracing plugin" + || fail "Claude marketplace must contain exactly the MCP and tracing plugins" import json import sys from pathlib import Path @@ -48,14 +51,60 @@ from pathlib import Path with open(sys.argv[1]) as f: plugins = json.load(f)["plugins"] -assert [plugin["name"] for plugin in plugins] == ["trace-claude-code"] +assert [plugin["name"] for plugin in plugins] == ["braintrust", "trace-claude-code"] +for plugin in plugins: + assert plugin["source"] == f"./plugins/{plugin['name']}" assert sorted(path.name for path in Path(sys.argv[2]).iterdir() if path.is_dir()) == [ - "trace-claude-code" + "braintrust", "trace-claude-code" ] PY -if find "$TARGET_DIR" -name '.mcp.json' -print -quit | grep -q .; then - fail "Claude dist still contains an MCP proxy configuration" +# The braintrust plugin is the Anthropic directory listing: a hosted MCP +# connection only, with no hooks, skills, commands, or local processes. +python3 - "$TARGET_DIR/plugins" <<'PY' || fail "Claude braintrust plugin breaks its MCP-only contract" +import json +import struct +import sys +from pathlib import Path + +plugins = Path(sys.argv[1]) +root = plugins / "braintrust" +assert not any(p.is_symlink() for p in root.rglob("*")) +files = {p.relative_to(root).as_posix() for p in root.rglob("*") if p.is_file()} +assert files == { + ".claude-plugin/plugin.json", ".claude-plugin/icon.png", ".mcp.json", "README.md", "LICENSE", +}, files + +manifest = json.loads((root / ".claude-plugin/plugin.json").read_text()) +assert manifest["name"] == "braintrust" and manifest["displayName"] == "Braintrust" +assert manifest["author"]["name"] == "Braintrust" +assert manifest["license"] == "MIT" +assert manifest["icon"] == "./.claude-plugin/icon.png" +for field in ["homepage", "repository", "documentationUrl", "supportUrl", "privacyPolicyUrl", "termsOfServiceUrl"]: + assert manifest[field].startswith("https://"), field +assert not ({"hooks", "commands", "agents", "skills", "lspServers", "mcpServers"} & set(manifest)) + +tracing = json.loads((plugins / "trace-claude-code/.claude-plugin/plugin.json").read_text()) +assert manifest["version"] == tracing["version"], "Claude plugins must share a release version" +marketplace = json.loads((plugins.parent / ".claude-plugin/marketplace.json").read_text()) +assert marketplace["version"] == manifest["version"], "Claude catalog version must match its plugins" +assert not any("version" in entry for entry in marketplace["plugins"]) + +# No headers or credentials: Claude Code discovers OAuth from the server. +# Plugin-root .mcp.json is the server map itself, with no mcpServers wrapper. +assert json.loads((root / ".mcp.json").read_text()) == { + "braintrust": {"type": "http", "url": "https://api.braintrust.dev/mcp"}, +} + +# Anthropic directory listing icon: square PNG, 512..2048px, under 2 MB. +data = (root / ".claude-plugin/icon.png").read_bytes() +assert data[:8] == b"\x89PNG\r\n\x1a\n" and data[12:16] == b"IHDR" and len(data) < 2 * 1024 * 1024 +width, height = struct.unpack(">II", data[16:24]) +assert width == height and 512 <= width <= 2048, (width, height) +PY + +if find "$TARGET_DIR" -name '.mcp.json' ! -path "$TARGET_DIR/plugins/braintrust/.mcp.json" -print -quit | grep -q .; then + fail "Claude dist contains an MCP configuration outside plugins/braintrust" fi python3 - "$TARGET_DIR/plugins/trace-claude-code/hooks/hooks.json" <<'PY' \