Skip to content

Add a shared Bugzilla broker lib with a public-only mode - #7338

Closed
alastor0325 wants to merge 3 commits into
mozilla:masterfrom
alastor0325:bugzilla-broker/pr
Closed

alastor0325 wants to merge 3 commits into
mozilla:masterfrom
alastor0325:bugzilla-broker/pr

Conversation

@alastor0325

Copy link
Copy Markdown
Member

Background

A Hackbot agent reads Bugzilla through a broker. The broker is a sidecar container. It runs in the same Cloud Run job as the agent. The broker holds the Bugzilla API key. It serves the agent-tools Bugzilla tools over MCP. The agent container does not have the key. Thus, text in a bug cannot make the model read or use the key.

Today, each agent has its own copy of the broker. Two examples are agents/frontend-triage and agents/bug-fix. This PR adds one shared broker in libs/bugzilla-broker. A new agent can use it, and it does not need a new copy.

The lib comes from the broker of the gecko-triage agent (#7239). That agent is not landed yet. It will be the first user of the lib, in the public-only mode. This PR does not change an existing agent.

Why a public-only mode

An agent can write in a public comment what it read. The API key belongs to a bot account, and the deploy selects that account. This account can read more than the public can read:

  • security bugs;
  • private comments and private attachments on public bugs.

For example, a duplicate search can find a restricted security bug about the same crash. Then the agent can write the number and the cause of that bug in a public comment. After that, all persons know about a security problem that is not fixed.

A rule in the prompt does not prevent this. The model can disobey the rule. Also, text in a bug can make the model disobey it. The public-only mode is code, and the model cannot change it. The agent cannot write what it did not read.

An agent with output that is not public uses the default mode. This mode serves all data that the API key can read. The brokers of today do the same.

The layers

agent  --MCP-->  bugzilla_broker (filter, paging, timeout)
                   --calls-->  agent_tools.bugzilla (bugsy)  -->  Bugzilla REST

The lib does not replace agent_tools/bugzilla.py. Each tool calls the function in agent-tools. Then the tool filters the result.

The commits

Each commit message has the details.

  1. Export the default get_bugs fields. No behavior change. The public-only get_bugs uses the list to add the groups field.
  2. Add the lib. The broker, its two modes, the worker threads and the request timeout.
  3. Comment paging. The public-only get_bug_comments can give one range of comments.

Testing

  • libs/bugzilla-broker: 96 tests pass. They test the filter, the history tool, the paging, the modes and the settings.
  • The gecko-triage branch on top of this lib: its 1665 tests pass.
  • A test of the public-only broker with bugzilla.mozilla.org:
    • A public bug came back with its description, history and attachments.
    • A restricted bug came back in inaccessible.
  • Two full gecko-triage dry runs through this broker, on bug 2086026. The runs did not post anything. In the second run, the close check read a long comment thread. Then it confirmed a duplicate.

Follow-ups

This PR does not include these changes:

  • Move the brokers of the other agents to the lib. These agents are frontend-triage, build-repair, uplift-resolve, bug-fix and the three autowebcompat agents.
  • In agent-tools, run _request in a worker thread with a timeout. Then all agents get this fix, and blocking.py is not necessary.
  • In agent-tools, add a public get_bug_history tool. Also add a hint argument to get_bug_attachments. Then the lib does not call the private bugzilla._request. Also, it does not change the text of a note.

`get_bugs` asks Bugzilla for a default field list when the caller names no
fields. That list was a string inside the function, so no other code could read
it.

The Bugzilla broker lib in the next change serves a public-only version of
`get_bugs`. It checks every requested field against a list of the fields that a
logged-out reader can see. It must check the default list in the same way. With
the list as a module constant, it reads the same fields that `get_bugs` reads.
Without the constant, the broker would keep a copy of the list. The copy
could become different from the original.

The behavior of `get_bugs` does not change.
A Hackbot agent reads Bugzilla through a broker. The broker is a sidecar
container that holds the Bugzilla API key. It serves the agent-tools Bugzilla
tools over MCP. The agent container then has no key. So if text in a bug
tricks the model, the model still cannot read or use the key. Each agent keeps
its own copy of this broker today.

This lib is one shared broker, so a new agent does not need a new copy:

- `create_app` serves the tools at `/mcp`. `mcp_manager` gives the MCP session
  manager, for a broker that mounts it beside other routes.
- By default, it serves the agent-tools tools as they are.
- With `public_only`, it serves only what a logged-out reader sees. It drops
  the bugs that have a group, and the private comments and attachments. It
  returns only the fields in an allowlist. Its history tool gives field names
  only. It refuses search terms that match private content.
- `attachment_hint` and `comment_search` set the parts of the public-only mode
  that depend on the agent.
- Each tool runs in a worker thread and has a request timeout. The agent-tools
  handlers make blocking calls. On one event loop, a slow Bugzilla call would
  stop all the other clients.
- `python -m bugzilla_broker` runs the broker, with settings from the
  environment.

Why the public-only mode: an agent that posts public comments can repeat in a
comment what it read. The API key belongs to a bot account that the deploy
chooses. That account can read more than the public can: security bugs, and
private comments and attachments on public bugs. For example, a duplicate
search can return a restricted security bug about the same crash. If the agent
names that bug and its cause in a public comment, everyone learns about an
unfixed security problem. A rule in the prompt is not enough. The model can
break the rule, and text in a bug can trick the model. The filter is code that
the model cannot change, so the agent cannot leak what it never read. An agent
whose output is not public uses the default mode.

The gecko-triage agent, which is not landed yet, will use the public-only mode.
The existing agent brokers do not move in this change. They can move to the lib
in follow-ups.
The comments of a long bug can be larger than one tool result can be. The
Claude Code CLI then saves the result to a file. The file is often one long
line of JSON, and the model cannot read it in parts.

The public-only `get_bug_comments` now takes two optional arguments:

- `start`: the number of the first comment to return, as Bugzilla numbers
  them. The page starts at the first public comment with that number or a
  higher one. An agent's prompt names comments by these numbers, so the agent
  can give a number from its prompt.
- `limit`: the maximum number of public comments to return.

The result also gives:

- `total`: the number of public comments.
- `next_start`: the `start` of the next page, or null on the last page.
  Comment numbers count the private comments too, so they can be higher than
  `total`. An agent that stops at `total` can miss the newest comments. So the
  agent takes the next page from `next_start`.

With no arguments, the tool returns all public comments, as before.
@alastor0325
alastor0325 requested a review from a team as a code owner October 28, 2026 08:38
@suhaibmujahid

Copy link
Copy Markdown
Member

This should be resolved with #6740

@alastor0325

Copy link
Copy Markdown
Member Author

@suhaibmujahid What’s your plan for #6740? Should I keep #7239 as is for now and migrate it to the Bugzilla proxy you’re working on later? My initial plan was to extract the services used by #7239 into separate PRs to keep it smaller.

@suhaibmujahid

Copy link
Copy Markdown
Member

What’s your plan for #6740?

I'm actively working on it now. Expecting it to land by next week.

Should I keep #7239 as is for now and migrate it to the Bugzilla proxy you’re working on later?

You do not need to wait for the proxy. Other agents already use the rad-only Bugzilla mcp through the broker approach. You could extend the existing one if it missing something you need:

bugzilla_config = build_sdk_server(
"bugzilla", BugzillaContext(client=client), bugzilla.TOOLS
)

My initial plan was to extract the services used by #7239 into separate PRs to keep it smaller.

Can you reuse the existing Bugzilla mcp in libs/agent-tools/agent_tools/bugzilla.py?

@alastor0325

Copy link
Copy Markdown
Member Author

I'm actively working on it now. Expecting it to land by next week.

So I suppose we do not need this PR?

You do not need to wait for the proxy. Other agents already use the rad-only Bugzilla mcp through the broker approach. You could extend the existing one if it missing something you need:

Thanks. #7239 already uses this approach.

Can you reuse the existing Bugzilla mcp in libs/agent-tools/agent_tools/bugzilla.py?

Yes, it already does in. This patch reuses the read-only Bugzilla MCP in libs/agent-tools/agent_tools/bugzilla.py, with the same broker approach as bug-fix. Each tool calls the agent-tools function. The lib adds only a layer on top:

  • a public-only filter, because gecko-triage posts public comments, and the bot key can read restricted bugs and private comments;
  • a get_bug_history tool with field names only;
  • comment paging, for long threads;
  • a worker thread and a timeout for each call.

@suhaibmujahid I want to confirm two points with you:

  1. This PR is not necessary, because your work in Add Bugzilla proxy and scoped token flow #6740 will cover it. I can close it.
  2. The review of Add the gecko-triage agent for Gecko bug triage #7239 can continue as it is. After Add Bugzilla proxy and scoped token flow #6740 lands, I will change Add the gecko-triage agent for Gecko bug triage #7239 in follow-up PRs, so that it uses your service and some shared in-tree skills/files as well.

Is this correct? Thanks!

@suhaibmujahid

suhaibmujahid commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

So I suppose we do not need this PR?

I do not think it is needed as a separate package. What is missing in agent_tools/bugzilla.py we can add it there, in this PR or in a new PR as you prefer.

a public-only filter

That is not necessary; the account that is used for the broker does not have any privileges. It is equivalent to a fresh account that anyone can just create. So nothing to revel with that auth. It does not even use a Hackbot account; it is a separate one.

a get_bug_history tool with field names only;

Could we add it to agent_tools/bugzilla.py?

comment paging

Can we support this as well in agent_tools/bugzilla.py?

a worker thread and a timeout for each call.

Why do we need that?

This PR is not necessary, because your work in #6740 will cover it. I can close it.

Some items mentioned might be interesting to have in agent_tools/bugzilla.py, so you could do that here, or close this OR and open a new one for that.

The review of #7239 can continue as it is.

Yes!

@alastor0325

Copy link
Copy Markdown
Member Author

Per offline discussion, we don't need this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants