MCP Server¶
Connect Claude Code or any Model Context Protocol client to your Bifract instance. Query logs with BQL, manage detection alerts, annotate logs with comments, and more from your local terminal.
The MCP server is a mode of the bifract CLI, so there is nothing extra to install. It
runs on your own machine and reaches your instance over the same HTTP API the web UI
uses, authenticating with a Bifract API key: a tool can never do more than that key is
allowed to do.
Prerequisites¶
- A running Bifract instance
- The
bifractCLI (the same binary used to install and manage a deployment) - A Bifract API key with at least
querypermission
Configure Claude Code¶
Create a .mcp.json file in the directory where you use Claude Code:
{
"mcpServers": {
"bifract": {
"command": "bifract",
"args": ["--mcp"],
"env": {
"BIFRACT_URL": "https://your-bifract-instance.example.com",
"BIFRACT_API_KEY": "bifract_your_api_key_here"
}
}
}
}
Use the full path to the binary if bifract is not on the PATH the client launches with.
bifract --mcp --help prints the settings it reads.
Environment Variables¶
| Variable | Required | Description |
|---|---|---|
BIFRACT_URL |
Yes | Base URL of your Bifract instance |
BIFRACT_API_KEY |
Yes | API key. Determines the scope and the role. |
BIFRACT_FRACTAL_ID |
No | The fractal to act in. Only an instance-wide key needs it; see below. |
BIFRACT_PRISM_ID |
No | A prism to act in instead, for reading across fractals |
BIFRACT_CA_CERT |
No | Path to a CA bundle, for an instance behind a private or self-signed CA |
BIFRACT_CLIENT_CERT |
No | Client certificate for mTLS (a combined PEM, or the cert half of a pair) |
BIFRACT_CLIENT_KEY |
No | Client private key, when BIFRACT_CLIENT_CERT holds only the certificate |
BIFRACT_VERIFY_SSL |
No | Set to false to skip certificate verification. Prefer BIFRACT_CA_CERT. |
BIFRACT_TIMEOUT |
No | Request timeout in seconds (default 60) |
bifract --mcp --help prints this list without leaving the terminal.
Choosing the scope¶
A key issued for one fractal or prism (bifract_<name>_...) carries its scope already.
Every query, alert and comment lands there with nothing else to configure, and
get_context reports which one.
An instance-wide key (bifract_admin_...) belongs to no fractal, so it names the one it
means on each request. Give the session a fractal to act in:
"env": {
"BIFRACT_URL": "https://bifract.example.com",
"BIFRACT_API_KEY": "bifract_admin_...",
"BIFRACT_FRACTAL_ID": "588f9ff8-4fe9-484a-9ca8-2ee77260e0b8"
}
Every tool also takes a fractal_id argument, so one session can work across several
fractals without being reconfigured. A key issued for one fractal ignores it: the server
fixes that key's scope, and no argument widens it.
Without either, a call that acts in a fractal is refused rather than answered in whichever
one the server falls back to. list_fractals and get_bql_reference run unscoped and
ignore the argument, since naming a fractal cannot be a precondition of finding out which
fractals exist.
Prefer a fractal-scoped key where one will do: it cannot reach past the fractal it was issued for, whichever way the session is configured.
Connecting through mTLS¶
A deployment fronted by Caddy with mTLS needs the client certificate generated under Manage > Access > Users > Client Certificate:
"env": {
"BIFRACT_URL": "https://bifract.example.com",
"BIFRACT_API_KEY": "bifract_...",
"BIFRACT_CA_CERT": "/etc/bifract/ca.pem",
"BIFRACT_CLIENT_CERT": "/etc/bifract/client.pem",
"BIFRACT_CLIENT_KEY": "/etc/bifract/client-key.pem"
}
Available Tools¶
Orientation¶
| Tool | Description |
|---|---|
get_context |
Which instance, fractal, and role this session is bound to |
list_fractals |
The fractals and prisms this credential can reach |
get_fields |
The field names available to queries, optionally filtered |
get_bql_reference |
The full BQL syntax reference |
Log Querying¶
| Tool | Description |
|---|---|
query_logs |
Execute a BQL query with optional time range |
validate_bql |
Check a query for syntax errors without running it (no database work) |
get_field_stats |
Per-field coverage, cardinality, and top values for a query's matches |
get_recent_logs |
Fetch recent logs to see the real event shape and data freshness |
Hot storage only. To search past the fractal's retention window, see Recall.
Provenance¶
| Tool | Description |
|---|---|
find_processes |
Locate process-creation events and their process_guid |
get_provenance_graph |
Expand a GUID into a scored process tree with its notable activity |
get_provenance_graph runs pgr() and returns a rendered tree, the highest-anomaly file, network, and DNS actions, and any cross-tree reconnections, rather than a raw edge list. It needs endpoint behavioral analytics enabled and endpoint/EDR data normalized to bifract_category process_creation.
A typical sequence:
find_processes(image="rundll32", start="2026-07-26T00:00:00Z")
get_provenance_graph(guid="{390eae98-...}", threshold=0.3, start="2026-07-26T00:00:00Z")
add_comment(log_id="...", text="...", tags=["IR-Rundll32"])
Behavioral Models¶
| Tool | Description |
|---|---|
list_models |
The behavioral baselines defined in this fractal |
get_model |
One model's full definition and backfill state |
get_model_data |
The rows a model has accumulated, to check whether an artifact is normal |
Dashboards¶
| Tool | Description |
|---|---|
list_dashboards |
Dashboard summaries |
get_dashboard |
A dashboard with every widget and the BQL behind it |
Alerts¶
| Tool | Description |
|---|---|
list_alerts |
List all detection alerts in the fractal |
get_alert |
Get full details of a specific alert |
create_alert |
Create a new detection alert with a BQL query |
update_alert |
Modify an existing alert |
delete_alert |
Remove an alert |
get_alert_executions |
View when an alert fired and what it matched |
get_alert_tests |
Show the test cases stored with an alert |
run_alert_tests |
Run test cases against a query without saving anything |
get_alert_policies |
List the policy rules this scope enforces on a definition |
Alert governance¶
A fractal or prism can keep a history of every alert definition, enforce policy rules on
what a definition must contain, and review changes before they go live. Where review is
on, create_alert, update_alert and delete_alert are refused and the work is
submitted with propose_alert_change instead.
Read get_alert_policies before writing a definition: a rule with severity block
refuses the save outright. Where a policy requires passing tests, run_alert_tests
checks them before anything is submitted. An update proposal carries the alert's existing
actions and tests forward unless new ones are named.
Approving and merging a proposal are not exposed as tools. A model that could approve its own proposal would make the gate meaningless, so those stay with a reviewer.
| Tool | Description |
|---|---|
get_alert_history |
Show how an alert's definition changed, and who changed it |
list_alert_changes |
List proposed alert changes awaiting review |
propose_alert_change |
Propose a create, update or delete for review |
Dictionaries¶
Watchlists and lookup tables detections join against, rather than hard-coding values in a query.
| Tool | Description |
|---|---|
list_dictionaries |
The dictionaries in the fractal, with their key column and row count |
get_dictionary |
One dictionary's columns and key definition |
search_dictionary |
Read rows, optionally filtered, to check an indicator against a watchlist |
add_dictionary_rows |
Insert or update rows, changing what live detections match on |
upload_dictionary_file |
Load a local file into a dictionary, whatever its size |
get_dictionary_key_collisions |
The keys that stop being distinct when case is ignored |
set_dictionary_case_insensitive |
Turn case-insensitive key lookups on or off |
upload_dictionary_file takes a path, not rows. The file is read on the machine the MCP
server runs on and streamed to the instance in chunks, so a list of millions of
indicators is one call rather than a conversation the model has to hold the file in.
CSV, TSV, JSON array, NDJSON, and a plain one-value-per-line list are recognised from the
extension and the content, and a .gz file is decompressed as it is read.
Rows are keyed, so re-uploading a corrected file updates rather than duplicates; rows the
file no longer lists stay as they are. A file that repeats a key therefore loads fewer rows
than it holds, which the summary says rather than leaving as an apparent loss. Columns the
dictionary lacks are added, and a column name the schema will not take is renamed and the
rename reported. Naming a dictionary_name that does not exist creates it, keyed on the
file's first column; pass is_global to make that one visible to every fractal. Pass
key_field where the file's key column is named something else, and dry_run to see what
a file would write before writing it.
An instance-wide key (bifract_admin_...) belongs to no fractal, so it must name one:
pass fractal_id, or set BIFRACT_FRACTAL_ID for the session. Without either, the load is
refused rather than sent to whichever fractal the server falls back to. The summary always
reports the fractal and scope the rows landed in.
It is the only tool that touches the filesystem, and it exists only here: the same tool in the in-product chat would read files off the server rather than off the analyst's machine.
ATT&CK Coverage¶
| Tool | Description |
|---|---|
get_attack_coverage |
Which techniques the configured detections cover, optionally by tactic, and which uncovered ones a synced feed already has rules for |
Coverage is derived from the attack.* labels on the alerts that exist, so it reports what is configured rather than what has fired.
Recall¶
Recall searches the object-storage archive, for hunts that reach further back than hot retention. A search is submitted as a job and polled, because an archive scan can take minutes.
| Tool | Description |
|---|---|
search_archive |
Submit a BQL search over the archive for an explicit time window |
get_archive_search |
Poll a Recall job and read its rows once it succeeds |
cancel_archive_search |
Stop a pending or running Recall job |
Collaboration¶
| Tool | Description |
|---|---|
add_comment |
Annotate a log entry with findings or notes |
list_comments |
View all comments in the fractal |
list_comment_tags |
Find the IR-<Name> tag for an investigation already under way |
add_tag |
Add a tag to existing comments, to group them into one investigation |
remove_tag |
Remove a tag from existing comments |
get_log_comments |
Read the comments on one log entry |
list_saved_queries |
Browse saved BQL queries for common patterns |
Notebooks¶
| Tool | Description |
|---|---|
list_notebooks |
List notebooks in the fractal |
get_notebook |
Read a notebook and all its sections |
create_notebook |
Create a new notebook |
add_notebook_section |
Append a markdown or query section to a notebook |
Instruction Libraries¶
| Tool | Description |
|---|---|
list_instruction_libraries |
List available instruction libraries |
get_instruction_library |
Read a library and its page structure |
read_instruction_page |
Read a single page's content |
create_instruction_library |
Create a new library |
create_instruction_page |
Add a page to a library |
update_instruction_page |
Edit an existing page |
Example Prompts¶
Once configured, ask Claude Code things like:
- "Query Bifract for all error logs in the last hour"
- "Show me the top 10 source IPs with failed logins"
- "What alerts are currently configured?"
- "Show me recent logs so I can understand the field structure"
- "Find every rundll32 process yesterday and build the provenance graph for the most suspicious one"
- "What did this process write to disk, and is that path normal for our fleet?"
- "Add a comment to log abc123 noting this is a confirmed true positive"
Creating an API Key¶
- Log in to your Bifract instance
- Navigate to the fractal you want to query
- Go to Manage > Access > API Keys
- Create a new key with at least
querypermission - For alert management, also enable
alert_manage - Copy the generated key. It is shown once.
The key is named for the scope it was issued for, so bifract_default_... belongs to the
fractal called default. An instance-wide key is bifract_admin_... and belongs to none;
see Choosing the scope.
How It Compares to AI Chat¶
The built-in AI Chat runs inside the Bifract UI and uses a server-side LLM via LiteLLM. The MCP server is the inverse: it runs locally and lets your own Claude Code instance call Bifract's API directly. Use whichever fits your workflow, or both.
| AI Chat | MCP Server | |
|---|---|---|
| Runs in | Bifract UI (browser) | Local terminal (Claude Code) |
| LLM | Server-side via LiteLLM | Your local Claude Code |
| Auth | Session cookie | API key |
| Best for | Quick in-app investigations | Deep analysis alongside code, scripting, automation |