fx-audit-mcp
MCP tools for Firefox security auditing. Provides async Python tools and a FastMCP server for running testcases in Firefox and SpiderMonkey, building Firefox and NSS with ASAN, and querying Bugzilla — all with structured Pydantic return types suitable for use with LLM agent frameworks.
Tools
| Tool | Description |
|---|---|
browser_evaluator |
Run a multi-file testcase in ASAN Firefox via grizzly replay, detect crashes |
package_testcase |
Bundle a testcase directory with prefs and env into a grizzly TestCase |
js_shell_evaluator |
Run a JS testcase in the SpiderMonkey shell, detect crashes and sanitizer output |
nss_gtest_evaluator |
Run an NSS GTest and report any ASan crash |
build_firefox |
Build Firefox via mach build with a specified MOZCONFIG |
build_nss |
Build NSS with ASAN via security/nss/build.sh |
search_bugs |
Search Bugzilla using raw REST query parameters |
get_bugs |
Fetch bugs by ID in bulk |
get_bug_comments |
Fetch all comments for a single bug |
get_bug_attachments |
Fetch attachments for a bug |
Installation
pip install fx-audit-mcp
Requires Python 3.12+.
Usage
As Python functions
The execution tools (browser, JS shell, NSS gtest, Firefox/NSS build) are async functions with structured Pydantic return types. The Bugzilla tools are only available via the MCP server (see below).
import asyncio
from pathlib import Path
from fx_audit_mcp import browser_evaluator, js_shell_evaluator
async def main():
result = await browser_evaluator(
# Maps the name each file takes in the testcase to its path on disk,
# so testcases may span several files and include binary assets.
# Use forward slashes for subdirectories ("sub/frame.html").
file_paths={
"test.html": Path("/repro/test.html"),
"boom.js": Path("/repro/boom.js"),
"font.woff2": Path("/repro/font.woff2"),
},
entry_point="test.html",
firefox_binary=Path("/path/to/obj-firefox-asan/dist/bin/firefox"),
timeout=30,
)
print(result.crashed, result.timed_out)
# Complete, untruncated logs are on disk; result.logs holds their paths,
# grouped into stderr/stdout/crashdata.
# The ASAN report is in crashdata (log_ffp_asan_<pid>.txt), not stderr.
for log in result.logs.crashdata:
print(Path(log).read_text()[:500])
# Logs live in a fresh temp directory that is never deleted; cleaning it
# up is up to you: shutil.rmtree(Path(result.logs.stderr[0]).parent)
asyncio.run(main())
As an MCP server
fx-audit-mcp exposes all execution tools (browser, JS shell, NSS gtest,
Firefox/NSS build) as an MCP server over stdio:
fx-audit-mcp
fx-audit-bugzilla-mcp exposes the Bugzilla query tools separately:
BUGZILLA_API_KEY=your_key fx-audit-bugzilla-mcp
Set BUGZILLA_URL to override the default Mozilla Bugzilla instance.
Claude Desktop / Claude Code .mcp.json example:
{
"mcpServers": {
"fx-audit": {
"command": "fx-audit-mcp",
"env": {
"FIREFOX_SOURCE_ROOT": "/path/to/firefox",
"FIREFOX_BINARY": "/path/to/firefox/obj-firefox-asan/dist/bin/firefox"
}
},
"fx-audit-bugzilla": {
"command": "fx-audit-bugzilla-mcp",
"env": {
"BUGZILLA_API_KEY": "your_key_here"
}
}
}
}
With pydantic-ai
Tools integrate directly with pydantic-ai agents:
from pydantic_ai import Agent
from fx_audit_mcp import browser_evaluator, js_shell_evaluator
agent = Agent(
"anthropic:claude-opus-4-7",
tools=[browser_evaluator, js_shell_evaluator],
)
Environment Variables
| Variable | Used by | Description |
|---|---|---|
BUGZILLA_API_KEY |
fx-audit-bugzilla-mcp |
Required; your Bugzilla API key |
BUGZILLA_URL |
fx-audit-bugzilla-mcp |
Bugzilla REST base URL (default: Mozilla's) |
FIREFOX_SOURCE_ROOT |
fx-audit-build-firefox |
Default --firefox-dir for the CLI entry point |
Crash Detection
Every tool writes complete logs to a fresh temp directory each run and
returns their paths in logs instead of the contents, so they can be grepped
rather than truncated to fit. The build tools return stderr/stdout; the
evaluators add crashdata.
The directory is never deleted and logs are unbounded, so callers should clean
up — take the parent of any returned path. The returned paths are only
meaningful to a client sharing a filesystem with the server.
- browser_evaluator: Crash signatures in
ignored_signatures/(FuzzManager format) are filtered out before returning, so common shutdown hangs don't pollute results. The ASAN report is incrashdata(log_ffp_asan_<pid>.txt), notstderr. - js_shell_evaluator: Detects crashes via negative exit code (signal) or
AddressSanitizer/UndefinedBehaviorSanitizerin stderr. JS errors (positive exit codes) are not treated as crashes. Crash diagnostics arrive on stderr, so on a crashcrashdatanames that same stderr file. - nss_gtest_evaluator: Detects
AddressSanitizerin stdout or stderr, andcrashdatanames whichever of those files carried the report.
A run that hits its time limit does not raise: the evaluators return
timed_out: true with the logs, which show how far the run got before the
hang. For the browser this means a process was still busy executing when the
window expired (it is aborted to capture stacks); an idle browser at the time
limit is a normal clean run, since testcases are not expected to close the
browser. A timed-out browser is never reported as a crash, since the report
that abort produces is a hang.
The JS shell and NSS gtest evaluators do report a crash on a timed-out run,
when the log holds a sanitizer report the process finished writing — UBSAN does
not halt on error, so a testcase can trip it and then hang. Since a kill can
cut a report off mid-stack, a timeout is judged on the report's closing
SUMMARY: line rather than any mention of the sanitizer, and the exit code is
ignored because it is the kill signal rather than a fault.
Development
# Install with dev dependencies
uv sync --group dev
# Run tests
uv run pytest
# Lint and format
uv run ruff check --fix .
uv run ruff format .
# Type check
uv run mypy src/
# Install pre-commit hooks
uv run pre-commit install
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fx_audit_mcp-4.0.0.tar.gz.
File metadata
- Download URL: fx_audit_mcp-4.0.0.tar.gz
- Upload date:
- Size: 165.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81adc3ad29068dffe92f97d925e478f601b21596410bb731d58b185096096899
|
|
| MD5 |
234843851dbae1884e6758c2cde9edb6
|
|
| BLAKE2b-256 |
51357840a697a180378a549d2e93e77caf3cc08b2afa0a424298e1eb30d8d58c
|
Provenance
The following attestation bundles were made for fx_audit_mcp-4.0.0.tar.gz:
Publisher:
ci.yml on MozillaSecurity/fx-audit-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fx_audit_mcp-4.0.0.tar.gz -
Subject digest:
81adc3ad29068dffe92f97d925e478f601b21596410bb731d58b185096096899 - Sigstore transparency entry: 2699256347
- Sigstore integration time:
-
Permalink:
MozillaSecurity/fx-audit-mcp@229e9013ca60c085b797ea4c881207d75751633e -
Branch / Tag:
refs/heads/main - Owner: https://github.com/MozillaSecurity
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@229e9013ca60c085b797ea4c881207d75751633e -
Trigger Event:
push
-
Statement type:
File details
Details for the file fx_audit_mcp-4.0.0-py3-none-any.whl.
File metadata
- Download URL: fx_audit_mcp-4.0.0-py3-none-any.whl
- Upload date:
- Size: 31.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd037eb1e19e1d0361e24069fbf1ab9f7a464c93ffeefac095802980e93c5089
|
|
| MD5 |
699f3ed150e17b596db19b17b4b0b8ee
|
|
| BLAKE2b-256 |
f7dc81e59225b588dff2202082f2dacc5bf78928d29389bec3306f182dc942b6
|
Provenance
The following attestation bundles were made for fx_audit_mcp-4.0.0-py3-none-any.whl:
Publisher:
ci.yml on MozillaSecurity/fx-audit-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fx_audit_mcp-4.0.0-py3-none-any.whl -
Subject digest:
fd037eb1e19e1d0361e24069fbf1ab9f7a464c93ffeefac095802980e93c5089 - Sigstore transparency entry: 2699257195
- Sigstore integration time:
-
Permalink:
MozillaSecurity/fx-audit-mcp@229e9013ca60c085b797ea4c881207d75751633e -
Branch / Tag:
refs/heads/main - Owner: https://github.com/MozillaSecurity
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@229e9013ca60c085b797ea4c881207d75751633e -
Trigger Event:
push
-
Statement type: