Skip to content
Open
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
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
printf 'dist_repo=%s\n' "$dist_repo"
printf 'paths<<EOF\n.github/release-versions/%s\n' "$PLUGIN"
case "$PLUGIN" in
claude) printf '%s\n' 'src/plugins/claude/content/plugins/*/.claude-plugin/plugin.json' ;;
claude) printf '%s\n' 'src/plugins/claude/content/plugins/*/.claude-plugin/plugin.json' 'src/plugins/claude/content/.claude-plugin/marketplace.json' ;;
codex) printf '%s\n' 'src/plugins/codex/content/plugins/*/.codex-plugin/plugin.json' 'src/plugins/codex/content/plugins/*/plugin.json' ;;
cursor) printf '%s\n' 'src/plugins/cursor/content/.cursor-plugin/plugin.json' 'src/plugins/cursor/content/package.json' 'src/plugins/cursor/content/package-lock.json' ;;
grok) printf '%s\n' 'src/plugins/grok/content/.grok-plugin/plugin.json' 'src/plugins/grok/content/hooks/hooks.json' ;;
Expand Down Expand Up @@ -147,6 +147,8 @@ jobs:

Codex releases attach an MCP-only `braintrust-<version>.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
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|---|---|
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 7 additions & 1 deletion scripts/set-plugin-version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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([
Expand Down
1 change: 1 addition & 0 deletions src/plugins/claude/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions src/plugins/claude/content/.claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -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.",
Expand Down
10 changes: 9 additions & 1 deletion src/plugins/claude/content/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
```
Expand All @@ -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
Expand Down
23 changes: 19 additions & 4 deletions src/plugins/claude/content/README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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"
}
6 changes: 6 additions & 0 deletions src/plugins/claude/content/plugins/braintrust/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"braintrust": {
"type": "http",
"url": "https://api.braintrust.dev/mcp"
}
}
21 changes: 21 additions & 0 deletions src/plugins/claude/content/plugins/braintrust/LICENSE
Original file line number Diff line number Diff line change
@@ -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.
60 changes: 60 additions & 0 deletions src/plugins/claude/content/plugins/braintrust/README.md
Original file line number Diff line number Diff line change
@@ -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).
61 changes: 55 additions & 6 deletions src/plugins/claude/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand All @@ -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"
Expand All @@ -40,22 +43,68 @@ 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

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' \
Expand Down
Loading