MCP server for Bugzilla bug trackers (any instance; base URL via --bugzilla-base-url)
  • Go 96.7%
  • Dockerfile 3.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Alexey Shabalin 56e3f213d9
All checks were successful
release / binaries (push) Successful in 53s
ci / test (push) Successful in 1m18s
release / image (push) Successful in 1m20s
Release 0.2.3
2026-08-06 16:00:56 +03:00
.forgejo/workflows ci: one shared build recipe for images and release artifacts 2026-08-05 19:14:33 +03:00
internal chore: move to altlinux.space/ai-stack 2026-08-05 19:17:41 +03:00
.gitignore chore: rename to bugzilla-mcp 2026-07-30 16:02:37 +03:00
bugzilla-mcp.container chore: rename to bugzilla-mcp 2026-07-30 16:02:37 +03:00
bugzilla-mcp.network chore: rename to bugzilla-mcp 2026-07-30 16:02:37 +03:00
Containerfile ci: one shared build recipe for images and release artifacts 2026-08-05 19:14:33 +03:00
go.mod chore: move to altlinux.space/ai-stack 2026-08-05 19:17:41 +03:00
go.sum feat: alt-bugzilla-mcp — MCP server for Bugzilla bug trackers 2026-07-14 19:49:31 +03:00
LICENSE chore: MIT license, copyright ALTLinux Team 2026-08-05 19:17:41 +03:00
main.go Release 0.2.3 2026-08-06 16:00:56 +03:00
README.md chore: rename to bugzilla-mcp 2026-07-30 16:02:37 +03:00

bugzilla-mcp

A Go MCP server for any Bugzilla bug tracker, wrapping the Bugzilla REST API. It exposes read tools plus optional write tools over the Model Context Protocol. The target instance is configurable with --bugzilla-base-url (env-friendly) and works with any Bugzilla — the default is only an example and does not bind the server to any particular host. Read is anonymous and always available; write requires an API key and is off by default.

Note: tool responses include Russian text, and tool descriptions carry bilingual RU/EN keywords so the client can pick the right tool from prompts in either language.

Tools

Read (2, always, anonymous)

Tool Purpose
bug_get Details of a bug by numeric id: status, product/component, first comments
bug_search Search bugs via quicksearch (keywords, package, component)

Write (3, only when write mode is enabled)

Tool Purpose
create_bug Create/file a NEW real bug in the configured Bugzilla instance
add_comment Add a comment to an existing bug
update_bug Change status/resolution, close a bug, mark as duplicate (+optional comment)

The write tools are registered and appear in tools/list only when an API key is set and read-only mode is not enabled (see below). Otherwise only the two read tools are available.

The "not found" behavior and Bugzilla errors are returned as friendly text rather than a protocol error.

Build and run

go build -o bugzilla-mcp .

# stdio transport (default) — for local MCP clients
./bugzilla-mcp --transport=stdio --bugzilla-base-url=https://bugzilla.example.org

# streamable-http — /mcp endpoint
./bugzilla-mcp --transport=http --addr=:8091 --bugzilla-base-url=https://bugzilla.example.org

Flags

Flag Env Default Description
--transport stdio stdio or http
--addr :8091 address for the http transport
--bugzilla-base-url https://bugzilla.altlinux.org Bugzilla base URL, REST at /rest (any instance; the default is only an example)
--bugzilla-api-key BUGZILLA_API_KEY (none) Static Bugzilla API key forwarded to Bugzilla. Optional — read is anonymous if unset, required for the write tools
--bugzilla-auth-mode BUGZILLA_AUTH_MODE query How the key is sent to Bugzilla: query (?api_key=) or bearer (Authorization: Bearer, for Red Hat Bugzilla)
--mcp-auth-header MCP_AUTH_HEADER (none) http only: HTTP header clients use to send a Bugzilla API key to this server. Unset → inbound header auth disabled, client headers ignored
--read-only BUGZILLA_READ_ONLY false force read-only

For each flag with an env var, the flag takes priority, otherwise the env var is used.

Authentication

Access is anonymous by default: with no key the server makes unauthenticated read-only requests, which work on public Bugzilla instances (e.g. bugzilla.altlinux.org). A key is only needed for the write tools or private data.

  • Outbound (MCP → Bugzilla). The static key comes from --bugzilla-api-key / BUGZILLA_API_KEY. --bugzilla-auth-mode selects how it is sent: query (default) as ?api_key=<KEY>, or bearer as Authorization: Bearer <KEY> (required by Red Hat Bugzilla and similar). The key never leaks into error messages.
  • Inbound (client → MCP), http transport only. When --mcp-auth-header / MCP_AUTH_HEADER names a header, each client request may carry its own Bugzilla key in that header, which takes priority over the static key for that session. When unset, inbound header auth is disabled and client headers are ignored. For stdio the key is always the static one.
  • Resolution order (http): per-request header → --bugzilla-api-key → anonymous.

Write gating. The write tools are enabled only when a key is present && not --read-only. At startup (stdio) the mode is logged:

  • bugzilla-mcp: read-write mode (BUGZILLA_API_KEY set) — a key is present;
  • ... read-only mode (anonymous, no BUGZILLA_API_KEY) — no key is set;
  • ... read-only mode (--read-only) — write is force-disabled by flag/env.

In http mode write availability is evaluated per session, so a client that supplies a key via the inbound header gets the write tools.

The read tools (bug_get/bug_search) stay anonymous and work in all modes. --read-only (or BUGZILLA_READ_ONLY=1) removes the write tools from tools/list entirely, even if a key is set.

In HTTP mode it also serves GET /healthz (a health-check for quadlet/orchestrator).

Usage with MCP clients

The server speaks MCP over stdio; the binary is bugzilla-mcp (make sure it is on your PATH). Point --bugzilla-base-url at your Bugzilla instance, and supply BUGZILLA_API_KEY only if you want the write tools.

Claude (.mcp.json)

{
  "mcpServers": {
    "bugzilla": {
      "command": "bugzilla-mcp",
      "args": ["--transport=stdio", "--bugzilla-base-url=https://bugzilla.example.org"],
      "env": { "BUGZILLA_API_KEY": "<optional for write>" }
    }
  }
}

Codex (~/.codex/config.toml)

[mcp_servers.bugzilla]
command = "bugzilla-mcp"
args = ["--transport=stdio", "--bugzilla-base-url=https://bugzilla.example.org"]
env = { BUGZILLA_API_KEY = "..." }

opencode (opencode.json)

{
  "mcp": {
    "bugzilla": {
      "type": "local",
      "command": ["bugzilla-mcp", "--transport=stdio", "--bugzilla-base-url=https://bugzilla.example.org"],
      "enabled": true
    }
  }
}

Tests

go test ./...

The client tests (internal/bugzilla/*_test.go) cover the HTTP layer (a 4xx error body → domain layer, query/bearer auth modes and anonymous requests, HTTPError on non-JSON), fetching bugs and comments (including the single-bug fallback, private comments → auth error), quicksearch, and human-readable formatting (FormatBug, FormatSearch, StatusLine, firstLines, NormalizeLimit). The write tests cover create/ comment/update requests and friendly error handling.

Container (podman/docker)

podman build -t bugzilla-mcp -f Containerfile .
podman run --rm -p 127.0.0.1:8091:8091 bugzilla-mcp \
  --transport=http --addr=:8091 --bugzilla-base-url=https://bugzilla.example.org

The image is multi-stage: build on golang:1.25 (CGO_ENABLED=0, static binary), the final stage is FROM scratch with a CA bundle for HTTPS to the configured Bugzilla instance, USER 65534.

Quadlet (rootless systemd)

The bugzilla-mcp.network and bugzilla-mcp.container files are for deploying as a rootless service. Copy them into ~/.config/containers/systemd/, then:

systemctl --user daemon-reload
systemctl --user start bugzilla-mcp

bugzilla-mcp.network sets Options=mtu=1500 — required, otherwise outbound TCP connections from the container break. The port is published on 127.0.0.1:8091.

Layout

main.go                 # CLI + server startup (stdio/http)
internal/bugzilla/      # Bugzilla client + tests
  http.go               # HTTP GET, auth modes (query/bearer), 4xx handling, User-Agent
  errors.go             # BugNotFound / BugzillaSearchError / BugzillaAuthError
  bugs.go               # FetchBug, FetchComments, FormatBug
  search.go             # quicksearch, FormatSearch, NormalizeLimit
  write.go              # CreateBug/AddComment/UpdateBug (POST/PUT + WriteError)
internal/tools/         # MCP tools (Input structs + handlers)
  tools.go              # read: bug_get, bug_search + Register gating
  write.go              # write: create_bug, add_comment, update_bug
Containerfile
bugzilla-mcp.network bugzilla-mcp.container