Skip to content

Bruno CLI Integration

Matimo ships a first-class Bruno CLI provider package (@matimo/bruno / matimo-bruno) that exposes all aspects of the Bruno API testing lifecycle as callable Matimo tools.

Bruno is an open-source API client (alternative to Postman/Insomnia) with a Git-friendly collection format and a powerful headless CLI (bru).


The Bruno package adds 7 tools that cover the complete API collection lifecycle:

Tool Purpose
bruno_create_collection Create a new collection directory + bruno.json scaffold
bruno_add_request Add a .bru request file (GET / POST / PUT / DELETE / PATCH)
bruno_get_collection_info Read collection metadata and list all requests with methods
bruno_list_collections Recursively discover all Bruno collections in a workspace
bruno_run_collection Execute all requests via bru run with JSON reporter output
bruno_run_request Execute a single named request for targeted debugging
bruno_import_openapi Bootstrap a collection from an OpenAPI 3.0 spec URL or file

All tools work identically from any calling pattern: factory, decorator, LangChain, CrewAI, or MCP.


Terminal window
# Install the provider package
pnpm add @matimo/bruno
# Ensure Bruno CLI is installed globally
npm install -g @usebruno/cli
bru --version # should print 1.x or later
Terminal window
# Install the provider package
pip install matimo-bruno
# or with uv
uv add matimo-bruno
# Ensure Bruno CLI is installed globally
npm install -g @usebruno/cli
bru --version

import { MatimoInstance } from 'matimo';
const matimo = await MatimoInstance.init({ autoDiscover: true });
// 1. Create a collection
await matimo.execute('bruno_create_collection', {
collection_name: 'My API Tests',
collection_path: './collections/my-api',
});
// 2. Add requests
await matimo.execute('bruno_add_request', {
collection_path: './collections/my-api',
request_name: 'list-users',
method: 'GET',
url: 'https://api.example.com/users',
headers: { Accept: 'application/json' },
});
// 3. Run the collection
const result = await matimo.execute('bruno_run_collection', {
collection_path: './collections/my-api',
});
console.log(result.summary); // { total, passed, failed, duration }
from matimo import Matimo
from matimo_bruno import get_tools_path
matimo = await Matimo.init([get_tools_path()])
# 1. Create a collection
await matimo.execute('bruno_create_collection', {
'collection_name': 'My API Tests',
'collection_path': './collections/my-api',
})
# 2. Add a request
await matimo.execute('bruno_add_request', {
'collection_path': './collections/my-api',
'request_name': 'list-users',
'method': 'GET',
'url': 'https://api.example.com/users',
'headers': {'Accept': 'application/json'},
})
# 3. Run the collection
result = await matimo.execute('bruno_run_collection', {
'collection_path': './collections/my-api',
})
print(result['summary']) # {'total': 1, 'passed': 1, 'failed': 0, 'duration': '...'}

Use convertToolsToLangChain / convert_tools_to_langchain to drive Bruno tools with an LLM agent. The agent can make natural language requests like “Create a petstore collection and run all tests” and the model will autonomously select and invoke the correct Bruno tools.

import { MatimoInstance, convertToolsToLangChain, ToolDefinition } from 'matimo';
import { createAgent } from 'langchain';
import { ChatOpenAI } from '@langchain/openai';
const matimo = await MatimoInstance.init({ autoDiscover: true });
const brunoTools = matimo.listTools().filter((t: ToolDefinition) => t.name.startsWith('bruno'));
const langchainTools = await convertToolsToLangChain(brunoTools, matimo);
const agent = await createAgent({
model: new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }),
tools: langchainTools,
});
const response = await agent.invoke({
messages: [{
role: 'user',
content: 'Create a "petstore" collection, add a GET /pets request, then run it.',
}],
});
from matimo import Matimo, convert_tools_to_crewai
from matimo_bruno import get_tools_path
from crewai import Agent, Task, Crew
matimo = await Matimo.init([get_tools_path()])
crewai_tools = convert_tools_to_crewai(matimo.list_tools(), matimo)
tester = Agent(
role='API Tester',
goal='Create and run a Bruno API test collection autonomously',
tools=crewai_tools,
llm='gpt-4o-mini',
)
task = Task(
description='Create a collection called "smoke-tests", add a GET request to https://httpbin.org/get, then run it.',
expected_output='Test results summary with pass/fail counts',
agent=tester,
)
crew = Crew(agents=[tester], tasks=[task])
result = crew.kickoff()

bruno_run_collection and bruno_run_request are risk: high: they run the bru CLI and send real HTTP requests to whatever the collection targets. The authoring tools (bruno_create_collection, bruno_add_request, bruno_import_openapi) are medium and write files; the two read tools are low.

None of them declares requires_approval, so by default they run without asking. To have a person approve each run, turn on quarantine at high:

const matimo = await MatimoInstance.init({
autoDiscover: true,
policyConfig: { enableHITL: true, hitlMinRiskLevel: 'high' },
onHITL: async (request) => askReviewer(request), // Promise<boolean>
});
matimo = await Matimo.init(
auto_discover=True,
policy_config=PolicyConfig(enable_hitl=True, hitl_min_risk_level="high"),
on_hitl=ask_reviewer,
)

Creates an empty Bruno collection with a bruno.json descriptor.

Parameter Type Required Description
collection_name string Yes Human-readable collection name
collection_path string Yes Absolute or relative path where the collection directory is created

Returns: { success, collection_path, message, errors }


Writes a .bru request file into an existing collection.

Parameter Type Required Description
collection_path string Yes Path to the collection root
request_name string Yes Request name (used as the filename slug, alphanumeric + hyphens)
method string Yes HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
url string Yes Request URL (can contain {variables})
headers object No Key-value headers
body string No JSON or raw text request body (for POST/PUT/PATCH)
tests string No Bruno test script (JavaScript assertions)
documentation string No Request documentation/description

Returns: { success, request_path, request_name, message }


Reads collection metadata and enumerates all .bru files.

Parameter Type Required Description
collection_path string Yes Path to the collection root (file or directory)

Returns: { success, collection: { name, path, requests: [{ name, method, url, tags, has_tests }], environments, variables, authentication } }


Recursively scans a workspace directory for Bruno collections (directories containing bruno.json).

Parameter Type Required Description
workspace_path string Yes Root directory to search
filter string No Filter collections by name (substring match)

Returns: { success, collections: [{ name, path, request_count, environments, tags }] }


Executes all requests in a collection using bru run and captures the JSON report.

Parameter Type Required Description
collection_path string Yes Path to the collection root
environment string No Bruno environment name to load
env_file string No Path to environment file to override collection environment
data_file string No Path to CSV or JSON file for data-driven testing
iteration_count number No Number of times to run the collection (default: 1)
delay_ms number No Delay between each request in milliseconds
tags string No Comma-separated tags, only run requests with ALL specified tags
exclude_tags string No Comma-separated tags, skip requests with ANY of these tags
tests_only boolean No Only run requests that have tests or active assertions (default: false)
bail_on_failure boolean No Stop execution after first failure (default: false)
parallel boolean No Run requests in parallel (default: false)
sandbox_mode string No JavaScript execution mode: safe or developer (default: safe)
report_format string No Report format: json, junit, or html
report_path string No Path to write the report file

Returns: { success, summary: { total_requests, passed, failed, execution_time_ms }, results, report_path, errors }


Executes a single named request from a collection.

Parameter Type Required Description
collection_path string Yes Path to the collection root
request_name string Yes Name of the request to run (exact match)
environment string No Environment name to use (overrides collection default)
env_file string No Path to environment file to override variables
sandbox_mode string No JavaScript execution mode: safe or developer (default: safe)

Returns: { success, request: { method, url, headers, body }, response: { status, headers, body, duration_ms }, assertions, errors }


Generates a Bruno collection from an OpenAPI 3.0 specification.

Parameter Type Required Description
spec_source string Yes URL or file path to an OpenAPI 3.0 JSON/YAML spec
output_directory string Yes Output directory for the generated collection
collection_name string No Override the collection name (defaults to spec name)
collection_format string No bru (classic) or opencollection (YAML, default: bru)
group_by string No Group requests by tags or path (default: tags)
insecure boolean No Skip TLS verification when spec source is an HTTPS URL (default: false)

Returns: { success, collection_path, collection_name, requests_created, message, errors }


Terminal window
cd typescript/examples/tools
# Complete workflow: 7 tools × 6 workflows (no API key needed)
pnpm bruno:complete
# LangChain agent driving Bruno tools autonomously
pnpm bruno:langchain
Terminal window
cd python/examples
# Complete workflow: no API key needed
make bruno-complete
# or: uv run python bruno/complete_workflow.py
# CrewAI agent (needs OPENAI_API_KEY)
make bruno-crewai
# or: uv run python bruno/crewai_agent.py

  • Bruno CLI (bru), install globally: npm install -g @usebruno/cli. The Matimo packages (@matimo/bruno, matimo-bruno) only call the bru on your PATH and do not bundle it
  • bru version: 1.x or later (tested with 3.1.3)
  • No API key required for bruno:complete / make bruno-complete
  • OPENAI_API_KEY required for LangChain and CrewAI agent examples

Bruno requires bru run to be invoked from the collection root (the directory containing bruno.json). The bruno_run_collection and bruno_run_request executors handle this automatically using execFileSync(bru, args, { cwd: absoluteCollectionPath }), you never need to cd manually.

The JSON reporter (--reporter-json) writes results to a temporary file which is read back and returned as structured data.