Skip to main content
Glama
README.md
# ๐Ÿ”Œ Bruno MCP Server for Python

<div align="center">

[![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
[![MCP](https://img.shields.io/badge/Model_Context_Protocol-Enabled-blue?style=for-the-badge)](https://modelcontextprotocol.io/)
[![Bruno](https://img.shields.io/badge/Bruno-API_Client-orange?style=for-the-badge)](https://www.usebruno.com/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)

</div>

> Python MCP server for running [Bruno](https://www.usebruno.com/) collections. It exposes a Model Context Protocol server over `stdio` with tools that run collections through the `bru` CLI and return normalized JSON results.

*Note: All examples in this repository use placeholder names (`project1`, `/home/user/project/bruno`, `example.test`). Replace them with your own paths and collection names.*

---

## โœจ Features

* **Run Bruno collections** natively using the Bruno CLI.
* **Discover collections** and sibling environment files automatically.
* **Support environment files** and dynamic environment variables.
* **Secure Secret Injection:** Pass secrets to Bruno without exposing values to the LLM via `inherited_variables`. Secret values are injected through a temporary, owner-only `--env-file` and the child process environment โ€” they never appear in CLI arguments (`ps` output) or logs.
* **Filter Inspection:** Inspect documented query filters and run temporary filter scenarios without modifying the source collection.
* **Two-Phase Full Validation:** Execute baseline tests + all documented filters in a single tool call.
* **Normalized Outputs:** Return structured execution results containing `success`, `summary`, `failures`, and `timings`.

---

## ๐Ÿ“ฆ Requirements

* **Python:** 3.10 or newer
* **Package Manager:** `uv`
* **Node Package Manager:** `npm` *(only if the Bruno CLI is not already installed)*

The installer checks whether the Bruno CLI command `bru` is available. If it is missing, the server fails with an explicit error instead of silently installing packages. To install the pinned version manually:

```bash
npm install -g @usebruno/cli@4.2.0
```

Runtime auto-install is available but opt-in: set `BRUNO_MCP_AUTO_INSTALL_BRU=1` (or `auto_install = true` under `[bruno]` in the config file) and the server installs exactly the pinned version from `cli_version` / `BRUNO_MCP_BRU_VERSION`.

---

## ๐Ÿš€ Installation & Running

### 1. Installation
Install dependencies using `uv`:

```bash
uv sync
```
*(If your configured package index does not mirror the MCP Python SDK, point uv at PyPI for the sync: `UV_DEFAULT_INDEX=https://pypi.org/simple uv sync`)*

### 2. Running the Server
You can run the server directly using `uv`:

```bash
uv run bruno-mcp
```
*Alternatively, run the module directly inside the uv environment: `uv run python -m bruno_mcp`*

---

## โš™๏ธ Configuration

### MCP Configuration
Example MCP `stdio` configuration from this workspace root:

```json
{
  "mcpServers": {
    "bruno-runner": {
      "command": "uv",
      "args": ["run", "bruno-mcp"]
    }
  }
}
```

### Configuration File (`bruno-mcp.toml`)
Default roots and auth aliases can be configured in `bruno-mcp.toml` in the current working directory, or globally in `~/.config/bruno-mcp/config.toml`. 

See `bruno-mcp.example.toml` for a commented template:

```toml
[workspace]
roots = [
  "/home/user/project/bruno"
]

[bruno]
cli_version = "4.2.0"   # pinned Bruno CLI version
auto_install = false     # never install bru silently at runtime

[limits]
run_timeout_seconds = 300
max_output_bytes = 8388608
max_concurrent_runs = 2

[artifacts]
ttl_hours = 24           # raw reports are auto-deleted after this
max_files = 50

[security]
enforce_root_confinement = true   # reject collection paths outside workspace roots

[auth]
inherited_variables = [
  "BRUNO_AUTH_TOKEN",
  "BRUNO_API_KEY"
]

[defaults]
environment = "dev"
```

Every setting can also be set via environment variables (`BRUNO_MCP_BRU_VERSION`, `BRUNO_MCP_AUTO_INSTALL_BRU`, `BRUNO_MCP_RUN_TIMEOUT`, `BRUNO_MCP_MAX_OUTPUT_BYTES`, `BRUNO_MCP_MAX_CONCURRENT_RUNS`, `BRUNO_MCP_ARTIFACTS_DIR`, `BRUNO_MCP_ARTIFACT_TTL_HOURS`, `BRUNO_MCP_ARTIFACT_MAX_FILES`, `BRUNO_MCP_ENFORCE_ROOT_CONFINEMENT`, `BRUNO_MCP_LOG_LEVEL`), which take precedence over the file.

> **Note:** The installer creates `~/.config/bruno-mcp/config.toml` with a dummy root. Local `bruno-mcp.toml` files are git-ignored so real paths and environment names are never committed.

---

## ๐Ÿ’ป Local VS Code Installation

From the project root, install dependencies with uv:

```bash
UV_DEFAULT_INDEX=[https://pypi.org/simple](https://pypi.org/simple) uv sync
```

Ensure the Bruno CLI is available (`bru --version`). This repository includes `.vscode/mcp.json`, allowing VS Code to discover the local MCP server from the workspace:

```json
{
  "servers": {
    "bruno-runner": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "bruno-mcp"]
    }
  }
}
```

Reload the VS Code window after syncing dependencies. The `bruno-runner` server should now be available in the MCP servers list.

### Global Installation
To install the MCP server in the VS Code user profile so it is available from **any** workspace:

```bash
uv run python scripts/install_vscode.py
```
*(To also install the reusable Copilot prompt and agent globally, append `--with-copilot-customizations` to the command above).*

To install it manually in another local VS Code workspace, change the command args to include `--directory`:
```json
"args": ["--directory", "/home/user/mcp-bruno", "run", "bruno-mcp"]
```

---

## ๐Ÿงฐ Available Tools

### ๐Ÿ” `list-collections`
Lists Bruno collections below a root directory or configured roots. Use it when the user provides a partial collection name instead of a full path.
* **`root`** *(optional)*: Bruno root directory (usually contains `collections/` and `environments/`).
* **`query`** *(optional)*: Case-insensitive text used to filter collection names and paths.

### โ–ถ๏ธ `run-collection`
Runs a Bruno collection and returns normalized execution results.
* **`collection`** *(required)*: Path to the Bruno collection.
* **`environment`** *(optional)*: Path to an environment file.
* **`variables`** *(optional)*: Environment variables as `KEY=value` strings.
* **`inherited_variables`** *(optional)*: Names of environment variables to read from the MCP server process and inject into Bruno without exposing values to the LLM. Values travel via a temporary owner-only `--env-file` and the child process environment (`{{process.env.NAME}}` also works) โ€” never in CLI arguments.

<details>
<summary><b>View detailed authentication & path behavior</b></summary>

**Auth Handling:**
For secrets, prefer `inherited_variables` instead of writing values in chat. By default, the secure MCP input `BRUNO_AUTH_TOKEN` can satisfy Bruno variables named `bearerToken`, `BEARER_TOKEN`, `AUTH_TOKEN`, `TOKEN`, `accessToken`, or `access_token`. Secrets are injected via a temporary private `--env-file`; if the selected `--env` environment file declares the same variable (which would take precedence inside bru), the run transparently switches to a temporary sanitized copy of the collection with the conflicting entry removed, so the injected secret always wins and source files are never modified.

**Workspace confinement:**
When `[workspace] roots` are configured (and at least one exists on disk), `collection` paths outside those roots are rejected with a clear error. Set `enforce_root_confinement = false` to disable.

**Execution limits:**
Each bru run has a configurable timeout (`run_timeout_seconds`, default 300s), bounded stdout/stderr capture (`max_output_bytes`), and a concurrency cap (`max_concurrent_runs`). Raw JSON reports are stored as artifacts with owner-only (`0600`) permissions and expire automatically (`ttl_hours` / `max_files`). Structured logs go to stderr (`BRUNO_MCP_LOG_LEVEL`).

**Supported Collection Inputs:**
* Collection directory: `/path/to/bruno/collections/project1`
* Bruno request file: `/path/to/bruno/collections/project1/request.bru`
* Internal `.vru` request file: `/path/to/bruno/collections/project1/request.vru`
* Open collection descriptor: `/path/to/bruno/collections/project1/opencollection.yml`

When Bruno failures look like authentication problems, the response includes `auth_failure: true` and an `auth_message`. 

Example with inherited secrets:
```json
{
  "collection": "/home/user/project/bruno/collections/project1",
  "environment": "dev",
  "inherited_variables": ["BRUNO_AUTH_TOKEN"]
}
```
</details>

### ๐ŸŒ `discover-environments`
Inspects the folder structure around a Bruno collection and returns the sibling environments directory, available environment names, and variable names (without returning secret values).

### ๐Ÿ“– `read-result-artifact`
Reads a bounded, redacted summary from the raw Bruno JSON artifact path returned by `run-collection`. 
* **`path`** *(required)*: The `artifact.path` value returned by a previous run.
* **`max_items`** *(optional)*: Number of response items to sample per request (default 3, max 20).

### ๐Ÿงช `list-request-filters` & `run-filter-scenarios`
* `list-request-filters`: Inspects YAML request files and returns query params split into enabled and disabled groups.
* `run-filter-scenarios`: Runs temporary request variants with selected disabled query params enabled without modifying the source files.

### ๐Ÿ›ก๏ธ `run-full-validation`
Two-phase orchestration in a single tool call:
1. **Baseline**: Runs the collection once and checks every endpoint responds without errors (no `4xx`/`5xx`).
2. **Filters**: Only runs if the baseline is green. Automatically discovers and tests every disabled query filter across every endpoint.

---

## ๐Ÿค– Prompt and Agent Automation

The project includes reusable Copilot prompt and agent templates:
* **Prompt template:** `copilot/prompts/run-bruno-collection.prompt.md`
* **Agent template:** `copilot/agents/bruno-runner.agent.md`

Install them globally with:
```bash
uv run python scripts/install_vscode.py --with-copilot-customizations
```

The agent will seamlessly navigate the workspace, discover collections/environments, handle credentials securely via `inherited_variables`, and return detailed execution summaries:

```json
{
  "success": true,
  "summary": {
    "total": 5,
    "failed": 0,
    "passed": 5
  },
  "failures": [],
  "auth_failure": false,
  "auth_message": null,
  "timings": {
    "started": "2024-03-14T10:00:00.000000Z",
    "completed": "2024-03-14T10:00:01.000000Z",
    "duration": 1000
  }
}
```

---

## ๐Ÿณ Docker

The Docker image installs both this Python server and the Bruno CLI:

```bash
docker build -t bruno-mcp-python .
docker run --rm -i bruno-mcp-python
```

---

## ๐Ÿ› ๏ธ Development & Project Structure

**Commands:**
* Compile-check the sources: `uv run python -m compileall src`
* Run the test suite: `uv run python -m unittest discover -s tests -v`

**Structure:**
```text
.
โ”œโ”€โ”€ src/bruno_mcp/         # MCP server, runner, config, and types
โ”œโ”€โ”€ scripts/               # VS Code installer script
โ”œโ”€โ”€ copilot/               # Reusable Copilot prompt and agent templates
โ”œโ”€โ”€ tests/                 # Unit tests
โ”œโ”€โ”€ .vscode/mcp.json       # Workspace MCP server entry
โ””โ”€โ”€ bruno-mcp.example.toml # Commented configuration template
```
---
mcp-name: io.github.kta41/mcp-bruno
---



## ๐Ÿ“„ License
Released under the [MIT License](LICENSE).


TDQS

A3.9/5.0

Scored across 7 tools

Disambiguation4/5

Tools have distinct primary purposes (listing, running, discovering, reading, validating). However, run-filter-scenarios and run-full-validation overlap in testing disabled query filters, requiring careful reading to choose between targeted filter testing and comprehensive two-phase validation.

Naming Consistency5/5

All tool names follow a consistent lowercase hyphen-separated verb_noun pattern (list-, run-, discover-, read-), with no mixed casing or verb styles.

Tool Count5/5

Seven tools is well-scoped for a Bruno collection runner/validator; each tool covers a distinct stage (discovery, execution, filtering, validation, artifact reading) without bloat.

Completeness4/5

The surface covers discovery, execution, filter validation, and artifact reading, which are the core lifecycle for running and validating collections. Minor gaps exist (e.g., no dedicated single-request execution or collection editing), but agents can work around them via run-collection and filter scenarios.

Maintenance

ActivityMaintained
ResponsivenessNo issues