# Bruno MCP Studio (npm · @ostico/bruno-mcp)

Author, edit and run Bruno collections non-destructively: both dialects, groups, gRPC, WebSocket.

- Trust score: 77/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-13

## Components

- npm · `@ostico/bruno-mcp`: 77/100 (this document), [markdown](https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp.md), [page](https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp)

## Channel facts

- Registry: `npm`
- Package: `@ostico/bruno-mcp`
- Version: `2.3.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-13.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 44 of 143 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to Ostico/bruno-mcp-studio).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 57/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 6497 tokens (~360/item across 18 items; 18 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 20/100
  - Stability observed for 6 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 90/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 69% of tool parameters carry a description.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### Claude

```bash
claude mcp add ostico-bruno-mcp-studio -- npx -y @ostico/bruno-mcp
```

### Codex

```bash
codex mcp add ostico-bruno-mcp-studio -- npx -y @ostico/bruno-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ostico-bruno-mcp-studio": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@ostico/bruno-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add ostico-bruno-mcp-studio --command npx --arg -y --arg @ostico/bruno-mcp
```

### Hermes

```yaml
mcp_servers:
  ostico-bruno-mcp-studio:
    command: "npx"
    args: ["-y", "@ostico/bruno-mcp"]
```

### Other

```json
{
  "mcpServers": {
    "ostico-bruno-mcp-studio": {
      "command": "npx",
      "args": [
        "-y",
        "@ostico/bruno-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-12 (score 77, +2)

- [functional regression] Schema quality: 301 → 360
- [functional regression] Security disclosure: unverified → fail
- [functional improvement] Stability: unverified → 0.17
- [functional] Package version: 2.2.1 → 2.3.0

### 2026-08-11 (score 75, +3)

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

### 2026-08-07 (score 72)

First indexed and scored.

## MCP tools (18)

### `create_collection` (~72 tokens)

Create Bruno Collection

Create a new Bruno API testing collection with configuration

Input parameters:

- `baseUrl` (string)
- `description` (string)
- `format` (string)
- `ignore` (array)
- `name` (string, required)
- `outputPath` (string, required): Absolute path where the new collection directory will be created.

### `create_environment` (~194 tokens)

Create Bruno Environment

Create an environment file for a Bruno collection. Writes the WHOLE file, so it REFUSES a name that already exists rather than overwriting it — the error names the existing variables and says which ones a replace would delete, so you can choose between merging with update_environment, picking another name, and retrying with overwrite: true.

Input parameters:

- `collectionPath` (string, required): Absolute path to existing collection directory.
- `name` (string, required)
- `overwrite` (boolean): Replace an environment that already exists. Without this an existing name is refused. Keys in the file that this tool does not model are preserved either way.
- `variables` (required): Either a flat name-to-value map, or a list of variable objects when you need flags. Use the list form to declare a secret at create time: a secret variable is stored as a name only (no format persist…

### `update_environment` (~94 tokens)

Update Bruno Environment

Partially update an existing Bruno environment by MERGING the provided variables into the existing ones. Pre-existing variables not listed are preserved (unlike create_environment, which replaces the whole file).

Input parameters:

- `collectionPath` (string, required): Absolute path to existing collection directory.
- `name` (string, required): Name of the existing environment to update.
- `variables` (object, required): Variables to merge into the environment. Existing variables not listed here are kept.

### `set_environment_variable` (~213 tokens)

Set Environment Variable

Set (add or update) a single variable in an existing Bruno environment. MERGES into the environment — all other variables are preserved.

Input parameters:

- `collectionPath` (string, required): Absolute path to existing collection directory.
- `enabled` (boolean): Whether the variable is enabled. Persisted: enabled=false is written as a disabled variable.
- `environment` (string, required): Name of the existing environment.
- `name` (string, required): Variable key to set.
- `secret` (boolean): Whether the variable is a secret. Persisted. IMPORTANT: marking a variable secret means its VALUE IS NOT SAVED — Bruno stores a secret variable as a name only (.bru lists it under vars:secret, .yml w…
- `value` (string|number|boolean, required): Variable value.

### `remove_environment_variable` (~68 tokens)

Remove Environment Variable

Remove a single variable from an existing Bruno environment. MERGES into the environment — all other variables are preserved.

Input parameters:

- `collectionPath` (string, required): Absolute path to existing collection directory.
- `environment` (string, required): Name of the existing environment.
- `name` (string, required): Variable key to remove.

### `create_request` (~889 tokens)

Create Bruno Request

Generate request files for API testing (supports .bru and .yml formats). Authors HTTP requests by default, WebSocket requests with kind "websocket" (url plus websocket.messages) and gRPC requests with kind "grpc" (url plus grpc.method, grpc.protoPath and grpc.messages); neither takes an HTTP method or a body. Supports multipart/form-data with file uploads and per-part contentType (body.type "form-data" with formData entries of type "file"), and inline scripts (pre-request/post-response/tests) so no separate add_test_script call is needed. Scripts run as async functions: top-level await works, and bru.sleep(ms)/setTimeout/setInterval are available, spending the script timeout (settings.timeout, default 5000ms) — raise it via the settings argument.

Input parameters:

- `assert` (array): Declared assertions, evaluated on every run without needing a test() block.
- `auth` (object)
- `body` (object)
- `collectionPath` (string, required): Absolute path to existing collection directory.
- `folder` (string)
- `grpc` (object): gRPC-only fields. Applies to kind "grpc" and is refused otherwise. Headers given for a gRPC request are written as metadata, which is that transport's only header surface.
- `headers` (object)
- `kind` (string): Transport. Defaults to "http". "websocket" and "grpc" take no method and no body; their payloads are websocket.messages and grpc.messages.
- `method` (string): Required for kind "http", refused for "websocket" and "grpc", which have no HTTP method. A gRPC request names its RPC method in grpc.method.
- `name` (string, required)
- `pathParams` (object): Values for :name segments in the URL, e.g. { id: "42" } for /users/:id.
- `query` (object)
- `scripts` (object): Inline scripts to persist with the request. Keys: pre-request, post-response, tests (aliases before-request/after-response accepted). Avoids a separate add_test_script call. IMPORTANT for tests/post-…
- `sequence` (number)
- `settings` (object): Request-level settings: transport behaviour (timeouts, redirects, URL encoding), not payload. What reaches the file depends on the dialect, because Bruno's own two writers differ: a .yml request alwa…
- `url` (string, required)
- `vars` (object)
- `websocket` (object): WebSocket-only fields. Applies to kind "websocket" and is refused otherwise.

### `modify_request` (~854 tokens)

Modify Request

Update an existing Bruno request file with partial-merge semantics. Only provided fields are updated; all other fields are preserved. Supports multipart/form-data with file uploads and per-part contentType. Inline scripts REPLACE the existing script of the same type by default (idempotent — repeated calls do not accumulate duplicate blocks); pass scriptMode:"append" to concatenate instead. Use remove_script to clear a script entirely.

Input parameters:

- `assert` (array): Replaces the whole assert block. Omit to leave existing assertions untouched.
- `auth` (object)
- `body` (object)
- `filePath` (string, required): Absolute path to the .yml or .bru request file to modify. Get from list_requests or get_collection_stats.
- `grpc` (object): gRPC-only fields. Applies to kind "grpc" and is refused otherwise. Headers given for a gRPC request are written as metadata, which is that transport's only header surface.
- `headers` (object)
- `method` (string)
- `name` (string)
- `pathParams` (object): Replaces the declared path parameters; query parameters are left alone.
- `query` (object)
- `scriptMode` (string): How to write the scripts field. "replace" (default) overwrites the existing script of each provided type, so calling modify_request repeatedly is idempotent. "append" concatenates onto the existing s…
- `scripts` (object): Inline scripts to persist with the request. Keys: pre-request, post-response, tests (aliases before-request/after-response accepted). Avoids a separate add_test_script call. IMPORTANT for tests/post-…
- `settings` (object): Request-level settings: transport behaviour (timeouts, redirects, URL encoding), not payload. What reaches the file depends on the dialect, because Bruno's own two writers differ: a .yml request alwa…
- `url` (string)
- `vars` (object)
- `websocket` (object): WebSocket-only fields. Applies to kind "websocket" and is refused otherwise.

### `add_test_script` (~407 tokens)

Add Test Script

Add pre-request, post-response, or tests scripts to a Bruno request. Canonical scriptType values are pre-request/post-response/tests; the aliases before-request (→ pre-request) and after-response (→ post-response) are also accepted. Appends to any existing script of that type by default — pass scriptMode:"replace" to overwrite it, or use remove_script to clear it. Assertions must be wrapped in test("name", function() { ... }) to be reported. Scripts run as async functions: top-level await works, and bru.sleep(ms), setTimeout and setInterval are available. Time spent waiting counts against the script timeout (settings.timeout, default 5000ms); raise it with modify_request's settings argument.

Input parameters:

- `bruFilePath` (string, required): Absolute path to the .yml or .bru request file. Get from list_requests or get_collection_stats.
- `script` (string, required): Script body. For post-response/tests, wrap every assertion in a test() block — test("status is 200", function() { expect(res.getStatus()).to.equal(200); }); — because only test() blocks are recorded…
- `scriptMode` (string): How to write the script. "append" (default) concatenates onto any existing script of this type; "replace" overwrites it. Each of the three script types has its own slot in both .bru and .yml, so repl…
- `scriptType` (string, required): Script type. Canonical: pre-request, post-response, tests. Aliases: before-request (→ pre-request), after-response (→ post-response).

### `remove_script` (~170 tokens)

Remove Script

Delete a pre-request, post-response, or tests script from a Bruno request, leaving the rest of the request intact. Use this to undo or clean up a script written by create_request/modify_request/add_test_script — including duplicate blocks accumulated by appending. Canonical scriptType values are pre-request/post-response/tests; the aliases before-request and after-response are accepted. Removing all scripts also drops the now-empty script container.

Input parameters:

- `bruFilePath` (string, required): Absolute path to the .yml or .bru request file. Get from list_requests or get_collection_stats.
- `scriptType` (string, required): Which script to remove. Both .bru and .yml keep the three script types in separate slots, so removal is precise: clearing tests leaves a post-response script in place, and vice versa.

### `delete_request` (~125 tokens)

Delete Request

Permanently delete a Bruno request file from a collection. Use this to remove a request created by mistake; the file is unlinked from disk and cannot be recovered through this server. Only .yml/.bru files inside a detected Bruno collection can be deleted. To clear just a script and keep the request, use remove_script instead.

Input parameters:

- `confirm` (boolean, required): Must be true. Explicit acknowledgement that the file is deleted permanently.
- `filePath` (string, required): Absolute path to the .yml or .bru request file to delete. Get from list_requests or get_collection_stats.

### `create_test_suite` (~54 tokens)

Create Test Suite

Generate comprehensive test collections with multiple related requests

Input parameters:

- `collectionPath` (string, required): Absolute path to existing collection directory.
- `dependencies` (array)
- `requests` (array, required)
- `suiteName` (string, required)

### `create_crud_requests` (~131 tokens)

Create CRUD Requests

Generate a complete set of CRUD operations for an entity: list, get by id, create, update, delete. All five inherit the collection or folder auth block unless you pass auth.

Input parameters:

- `auth` (object): Defaults to inherit, matching what Bruno itself gives a new request. Passing "none" is an opt-OUT that stops the collection auth block from applying to these five files, not an absence of opinion.
- `baseUrl` (string, required)
- `collectionPath` (string, required): Absolute path to existing collection directory.
- `entityName` (string, required)
- `folder` (string)

### `list_collections` (~126 tokens)

List Collections

List the Bruno collections REGISTERED IN workspace.yml, with their names and paths. This is a registry listing, not a filesystem scan: a collection that exists on disk but is not registered will NOT appear, and registered entries that no longer exist are returned with "exists": false. If you already know a collection's absolute path, pass it directly to the other tools — it does not need to appear here. Use the returned path as collectionPath in other tools (get_collection_stats, list_requests, run_collection).

Input parameters:

- `workspacePath` (string): Optional explicit path to workspace.yml

### `list_requests` (~70 tokens)

List Requests

List all request files (.yml/.bru) in a Bruno collection. Returns absolute file paths, each usable as an entry in the requests list of run_collection, or of one of its groups.

Input parameters:

- `collectionPath` (string, required): Absolute path to collection directory. Use the path returned by list_collections.

### `get_collection_stats` (~106 tokens)

Get Collection Statistics

Get statistics about a Bruno collection — request counts by method, folders, environments, and per-request details including file paths. environmentDetails lists each environment with the NAMES of the variables it declares (values are withheld), so you can see what an environment already defines before merging into it with set_environment_variable. Use filePath values as entries in the requests list of run_collection.

Input parameters:

- `collectionPath` (string, required): Absolute path to collection directory. Use the path returned by list_collections.

### `run_collection` (~2609 tokens)

Run Collection

Execute requests in a Bruno collection and run test scripts. HOW TO RUN A SUBSET: requests takes an ORDERED list of entries, each a .yml/.bru request file or a directory (which expands to every request under it, recursively). Absolute, or relative to collectionPath. Order is yours and duplicates are allowed — naming the same request twice runs it twice. Omit requests to run the whole collection. GROUPS: pass groups instead when one call needs more than one identity or configuration. Each group owns its OWN variable store and cookie jar, so nothing a group sets — a bru.setVar, a session cookie — is visible to any other group. That is what makes running the same five requests as alice and as bob in one call safe. Passing both requests and groups is REJECTED rather than resolved for you. ITERATIONS: data or dataFile runs a group once per row of a table, with the row bound as variables — one call to check the same requests against 50 accounts, or one login against 50 credential pairs. Each row is reported as its own group, with the same name, its own index and an iterationIndex, because each row IS a separate group: it gets that isolation, so two rows differing only in a password authenticate separately instead of one silently reusing the other's token. Rows are independent, so a failing row does not stop the rows after it. Ceiling of 1000 rows per scope. PARALLELISM: parallel on the RUN fans the groups out against each other; parallel on a GROUP fans that group's own requests out. A group is serial unless it says otherwise, whatever the run does. seq no longer constrains execution — it is the default order and the reporting order only, so two requests in one parallel group genuinely run at the same time and may contend on the store they share. RESULTS are group-shaped: groups[] each carry their own summary, results, capturedVariableNames, capturedVariables and warnings, and the top-level summary is the run. There is no top-level results array, in the no-groups case ei…

Input parameters:

- `captureVariables` (array): Names of variables set by bru.setVar during the run whose values you want back, e.g. ["token"]. A script that captures a value out of a response — bru.setVar("token", res.body.token) — makes it avail…
- `collectionPath` (string, required): Absolute path to collection root directory. Use the path returned by list_collections.
- `collectionRoot` (string): The collection that collectionPath belongs to, when running a subfolder of one: environments and the collection- and folder-level scripts are resolved from here. Must be collectionPath itself or an a…
- `cookieJar` (boolean): Keep cookies from each response and send them on later requests in the same run, so a login carries into the requests after it. Scoped to the group — nothing crosses from one group to another, at any…
- `data` (array): Rows every group iterates over, as the group-level data but applied to all of them. A group that gives its own data or dataFile REPLACES this rather than adding to it, the same rule environment follo…
- `dataFile` (string): A CSV inside the collection whose rows every group iterates over. Same rules as the group-level dataFile, and likewise replaced by a group that names its own rows. Ceiling of 1000 rows, because every…
- `environment` (string): Environment name to use (e.g. "dev", "staging"). Get available names from get_collection_stats.
- `groups` (array): Run the same collection under more than one identity or configuration in one call. Each group owns its OWN variable store and cookie jar: nothing a group sets is visible to any other group, in either…
- `includeResponseBody` (boolean): Include the response body of each request in the results. Default: true.
- `maxConcurrency` (integer): Ceiling on requests in flight across the whole run. Omit to derive one from this machine's cores and memory, held below capacity. 0 lifts it entirely, at your own risk. Applies to THIS run and is giv…
- `maxResponseBodyBytes` (number): Maximum response body size (bytes) to return per request; longer bodies are truncated and response_body_truncated is set. Default: 10240.
- `parallel` (boolean): Fan out. At the run level this runs the GROUPS concurrently; a group's own requests are serial unless that group sets its own parallel. With no groups given the run is one group, so parallel here run…
- `report` (object): Write the run to a file, in addition to returning it here. Name at least one format. Paths are CONFINED TO THE COLLECTION: a path resolving outside it is refused, with the reason as a run warning, be…
- `requests` (array): The requests to run, IN THE ORDER GIVEN. Each entry is a .yml or .bru request file, or a directory, which expands to every request under it, recursively. Absolute, or relative to collectionPath. Get…
- `variables` (object): Variables for this run only, as {name: value}. They override the environment file and work without one. Held in memory and never written to any file — this is the only correct way to supply a secret,…
- `websocket` (object): Bounds for websocket requests in this run. Applies to every websocket request in the call; there is no per-request form. Omit and the defaults below apply. A session always ends on one of these bound…

### `read_request` (~152 tokens)

Read Bruno Request

Read a single request file back as structured JSON: method, url, headers, query and path params, body, auth mode, scripts, assertions, vars, settings and docs. Works on both .bru and .yml and returns the same shape for each, so the on-disk format stays invisible. Use this before modify_request to see current state, and after create_request to confirm what was written. A "notes" array reports anything the file declares that the runner will not act on.

Input parameters:

- `filePath` (string, required): Absolute path to the .bru or .yml request file. Use the path returned by create_request or list_requests rather than rebuilding it: request filenames are lowercased on write.

### `read_environment` (~104 tokens)

Read Bruno Environment

Read an environment back as structured JSON: every variable with its value, plus its disabled and secret flags. Omit "name" to list the collection's environments instead. Secret variables are returned by name only — Bruno stores no value for a secret in either file format, so there is none to return.

Input parameters:

- `collectionPath` (string, required): Absolute path to the collection directory.
- `name` (string): Environment name, without extension. Omit to list the available environment names.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp#diagnostics

## Score history

- 2026-08-13: 77
- 2026-08-12: 77
- 2026-08-11: 75
- 2026-08-10: 72
- 2026-08-09: 72
- 2026-08-08: 72
- 2026-08-07: 72

## Links

- npm package: https://www.npmjs.com/package/@ostico/bruno-mcp
- Socket report: https://socket.dev/npm/package/@ostico/bruno-mcp
- Repository: https://github.com/Ostico/bruno-mcp-studio
- Changelog RSS feed: https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp.json
- HTML version of this page: https://verifymcp.io/servers/ostico-bruno-mcp-studio/ostico-bruno-mcp
