ibm-mq-mcp
# ibm-mq-mcp
把 IBM MQ 的 REST API 暴露成 LLM 可调用的 MCP 工具的服务器。
## 1. 项目简介
本项目仿造 IBM 官方示例
[ibm-messaging/mq-mcp-server](https://github.com/ibm-messaging/mq-mcp-server),
把 IBM MQ 的管理与消息 REST API 包装成 [MCP](https://modelcontextprotocol.io/)
(Model Context Protocol)工具,供支持 MCP 的 LLM 客户端(如 Claude Code)直接调用。
原版是一个约 110 行的单文件示例,只暴露 `dspmq` / `runmqsc` 两个工具,配置全部硬编码,
错误一律返回 `"Something went wrong!"`。本项目在保留其「REST API → MCP 工具」核心思路的
前提下做了产品化改造:
- 工具数从 2 个扩展到 **15 个**,覆盖队列管理器、MQSC、对象查询、消息收发四类;
- 配置支持 CLI 参数 / 环境变量 / 默认值三级优先级,不再硬编码;
- 区分管理 API 与消息 API 两套凭据(详见下文「两套凭据」一节),这是实测出的真实限制,
原版没有涉及;
- 错误按四类(HTTP 层、认证、MQSC 业务失败、其他)分别解析,返回具体原因而不是笼统的
失败提示;
- 提供只读模式,可在生产环境中限制 LLM 只能查询、不能修改 MQ 配置。
所有关于 MQ REST API 行为的结论(见下文「已知限制」)均来自对真实 MQ 9.4.5.1 实例的
逐条探测,不是查文档推测得出的。
## 2. 快速开始
前提:本机已运行一个 IBM MQ 实例(例如 IBM 官方开发镜像
`icr.io/ibm-messaging/mq:9.4.5.1-r1`,`MQ_DEV=true`),mqweb 监听在
`https://127.0.0.1:9443`。
```bash
# 启动 MQ 容器(如果还没有运行)——本项目不负责容器生命周期,按需自行启动,例如:
# docker run --rm -e LICENSE=accept -e MQ_QMGR_NAME=QM1 -e MQ_DEV=true \
# -e MQ_ADMIN_PASSWORD=admin -p 1414:1414 -p 9443:9443 \
# icr.io/ibm-messaging/mq:9.4.5.1-r1
```
### 方式一:直接从 GitHub 运行(无需克隆)
`uvx` 会自动拉取源码、在临时环境里装好依赖并运行,用完即弃:
```bash
uvx --from git+https://github.com/moonfruit/ibm-mq-mcp ibm-mq-mcp
```
带参数同理,把它们接在命令后面即可:
```bash
uvx --from git+https://github.com/moonfruit/ibm-mq-mcp ibm-mq-mcp --read-only
```
想固定到某个版本或分支,在 URL 后加 `@<ref>`:
```bash
uvx --from git+https://github.com/moonfruit/ibm-mq-mcp@main ibm-mq-mcp
```
### 方式二:克隆后本地运行(要改代码时用这个)
```bash
git clone https://github.com/moonfruit/ibm-mq-mcp
cd ibm-mq-mcp
uv sync
uv run ibm-mq-mcp
```
两种方式都以 stdio 传输启动,默认连接 `https://127.0.0.1:9443`、管理凭据
`admin/admin`、忽略自签名证书校验。这组默认值正好对应 IBM 官方开发镜像,
连本机开发实例无需任何额外配置。
> 如果你的 `uv` 配了国内 PyPI 镜像,可能会遇到依赖解析失败(部分镜像同步不及时,
> 拿不到 `mcp>=2.1.1`)。临时指定官方源即可:
> `UV_DEFAULT_INDEX=https://pypi.org/simple uvx --from git+... ibm-mq-mcp`
## 3. 接入 Claude Code
在 Claude Code 的 MCP 配置中加入。**直接引用 GitHub,无需克隆仓库**:
```json
{
"mcpServers": {
"ibm-mq": {
"command": "uvx",
"args": [
"--from", "git+https://github.com/moonfruit/ibm-mq-mcp",
"ibm-mq-mcp"
],
"env": {
"MQ_BASE_URL": "https://127.0.0.1:9443",
"MQ_USERNAME": "admin",
"MQ_PASSWORD": "admin",
"MQ_MESSAGING_USERNAME": "app",
"MQ_MESSAGING_PASSWORD": "admin"
}
}
}
}
```
如果已经克隆到本地(比如你要改代码),把 `command`/`args` 换成本地路径:
```text
"command": "uv",
"args": ["--directory", "/path/to/ibm-mq-mcp", "run", "ibm-mq-mcp"],
```
想让接进来的服务器只能读、不能改配置也不能销毁消息,在 `env` 里加
`"MQ_READ_ONLY": "true"`——此时只注册 10 个只读工具,`run_mqsc` 与消息写入类工具
根本不会出现在模型的工具列表里。
也可以复制 `.env.example` 为 `.env` 并按需修改(本项目不自动加载 `.env`,
需要配合 `direnv` 之类的工具或手动 `export`)。
## 4. 配置项
配置优先级:**CLI 参数 > 环境变量 > 默认值**。空字符串环境变量(如 `MQ_PASSWORD=""`)
视为未设置,会回退到默认值,避免被误当作显式的空密码。
| 配置 | 环境变量 | CLI 参数 | 默认值 |
| --- | --- | --- | --- |
| mqweb 端点 | `MQ_BASE_URL` | `--base-url` | `https://127.0.0.1:9443` |
| 管理 API 用户名 | `MQ_USERNAME` | `--username` | `admin` |
| 管理 API 密码 | `MQ_PASSWORD` | `--password` | `admin` |
| 消息 API 用户名 | `MQ_MESSAGING_USERNAME` | `--messaging-username` | `app` |
| 消息 API 密码 | `MQ_MESSAGING_PASSWORD` | `--messaging-password` | `admin` |
| 证书校验 | `MQ_VERIFY_SSL` | `--verify-ssl` / `--no-verify-ssl` | `false` |
| 请求超时(秒) | `MQ_TIMEOUT` | `--timeout` | `30` |
| 传输方式 | `MQ_TRANSPORT` | `--transport` | `stdio` |
| 监听地址 | `MQ_HOST` | `--host` | `127.0.0.1` |
| 监听端口 | `MQ_PORT` | `--port` | `8000` |
| 只读模式 | `MQ_READ_ONLY` | `--read-only` / `--no-read-only` | `false` |
| 日志级别 | `MQ_LOG_LEVEL` | `--log-level` | `INFO` |
`--transport` 支持 `stdio`、`streamable-http`、`sse`;`stdio` 下 `--host`/`--port`
不生效。日志一律写到 stderr(stdio 传输下 stdout 被 MCP 协议占用)。
## 5. 两套凭据——为什么消息工具默认用户名不是 `admin`
IBM MQ 把管理和消息拆成 **MQWebAdmin** 与 **MQWebUser** 两个互不包含的角色:
- 管理 API(队列管理器查询、MQSC、对象查询)要求 **MQWebAdmin** 角色;
- 消息 API(浏览/取走/发送/发布消息)要求 **MQWebUser** 角色。
实测对真实 MQ 9.4.5.1 实例逐条探测的结果:用 `admin:admin` 调用消息 API 会返回
`403 MQWB0108E`——IBM 开发镜像里的 `admin` 用户只有 MQWebAdmin 角色,没有
MQWebUser 角色。必须换用另一个用户(开发镜像里是 `app:admin`)才能调用消息 API。
因此本项目的默认值是**两套凭据**:
- 管理 API 默认 `admin` / `admin`;
- 消息 API 默认 `app` / `admin`。
如果只配置了 `MQ_USERNAME`/`MQ_PASSWORD` 而不配置 `MQ_MESSAGING_USERNAME`/
`MQ_MESSAGING_PASSWORD`,消息类工具(`browse_message`、`get_message`、
`put_message`、`publish_message`)大概率会在真实环境中因权限不足而失败——这不是
本项目的 bug,是 MQ 权限模型的设计如此。
## 6. 工具清单
### 只读工具(10 个,`--read-only` 模式下依然可用)
| 工具 | 用途 |
| --- | --- |
| `list_queue_managers` | 列出 mqweb 服务器上的队列管理器及其运行状态 |
| `get_queue_manager` | 查询单个队列管理器的完整属性与运行状态 |
| `get_installation_info` | 查询 IBM MQ 的安装名称、版本与平台 |
| `list_queues` | 列出队列及其当前深度(按类型过滤:本地/别名/远程/模型) |
| `get_queue` | 查询单个队列的全部属性,包含当前深度 `curdepth` |
| `list_channels` | 列出通道及其定义 |
| `get_channel` | 查询单个通道的定义,可选附带运行状态 |
| `list_subscriptions` | 列出订阅 |
| `list_topics` | 列出主题对象 |
| `browse_message` | 浏览队列上的第一条消息,不移除它 |
### 会改变状态的工具(5 个,`--read-only` 模式下不注册)
| 工具 | 用途 |
| --- | --- |
| `run_mqsc` | 对指定队列管理器执行一条纯文本 MQSC 命令(可执行任意命令,包括修改/删除) |
| `run_mqsc_json` | 以结构化形式执行 MQSC 命令,返回 JSON(同样可执行修改/删除) |
| `get_message` | 取走队列上的第一条消息,消息会从队列中被移除,无法撤销 |
| `put_message` | 向队列发送一条文本消息 |
| `publish_message` | 向主题发布一条文本消息 |
对象查询(`list_queues`/`get_queue`/`list_channels`/`get_channel`/
`list_subscriptions`/`list_topics`)统一通过 MQSC 的 `runCommandJSON` 实现,
而不是走 REST 资源路径,原因见下文「已知限制」第 3 条。
## 7. 安全提示
- **默认忽略服务器证书校验**(`MQ_VERIFY_SSL=false`)。这是为了适配开发镜像的自签名
证书,开箱即用。生产环境应显式开启 `--verify-ssl`(或 `MQ_VERIFY_SSL=true`),
否则连接可能被中间人劫持。
- **默认凭据 `admin`/`admin` 属于 MQWebAdmin 角色,具备完整管理权限**。把本服务器接入
LLM 意味着模型可以调用 `run_mqsc` / `run_mqsc_json` 执行任意 MQSC 命令,包括
`DELETE QLOCAL`、`STOP CHANNEL` 等破坏性操作——这不是理论风险,是这两个工具的
设计使然。
- **生产环境建议改用只读账号,并在启动时加 `--read-only`**(或
`MQ_READ_ONLY=true`)。只读模式下服务器只注册上述 10 个只读工具,`run_mqsc`、
`run_mqsc_json`、`get_message`、`put_message`、`publish_message` 根本不会出现
在 LLM 可见的工具列表里,而不是注册后再依赖权限报错——即便 LLM 尝试调用也无从
下手。
## 8. 已知限制
以下结论均对真实 MQ 9.4.5.1 实例逐条实测得出,不是查文档推测的:
1. **`browse_message` 只能看到队首一条消息**。MQ 的消息 REST API 没有游标分页,
实测连续 3 次 GET 请求返回的是同一条消息(`messageId` 相同、队列深度不变)。
需要遍历整个队列的场景,请改用原生 MQ 客户端(如 `pymqi`),本项目不支持。
2. **对象查询统一走 MQSC,而非 REST 资源路径**。`/admin/qmgr/{qm}/queue`、
`/channel`、`/subscription` 这几个 REST 资源在 MQ 9.4 的 v3 管理 API 下已被
移除(实测返回 `404 MQWB0116E`),仅在 v1 API 保留。本项目改用
`runCommandJSON` 统一实现对象查询,覆盖面更广,对 MQ 版本差异也更不敏感——
这一实现方式对 z/OS 队列管理器同样适用。
3. **GET 是浏览、DELETE 才是取走**,这是 MQ 消息 REST API 最反直觉的一点:GET
请求消息但不会从队列移除它,只有 DELETE 才会真正取走并从队列删除。本项目用
`browse_` / `get_` 两种工具命名前缀加以区分,避免 LLM 误用。
4. **MQSC 命令失败时 HTTP 状态码仍是 200**,失败信息藏在响应体的
`overallCompletionCode` 与错误详情字段(`runCommandJSON` 用 `message`
字段、`runCommand` 用 `text` 字段,两者不一致)里。本项目在客户端内部统一解析
为结构化错误,工具返回的文本会包含具体的 MQSC 错误原因。
5. **队列为空时 DELETE/GET 返回 204 且无响应体**,这不是错误。本项目按状态码而非
响应体是否为空来判断队列是否有消息,避免把「取到一条正文为空的消息」误判为
「队列没有消息」。
## 9. 开发与测试
```bash
uv run ruff check .
uv run ruff format --check .
uv run pytest -v # 单元测试,默认跳过需要真实 MQ 实例的集成测试
uv run pytest -m integration -v # 集成测试,需要本机有可访问的真实 MQ 实例
```
TDQS
Scored across 15 tools
Most tools cleanly separate list/get operations for each resource and message actions are clearly distinct. The main overlap is run_mqsc vs run_mqsc_json, which both execute MQSC but differ by output format and argument style; their descriptions mitigate but do not eliminate potential misselection.
All tools use consistent snake_case verb_noun naming: list_* for enumerations, get_* for single-object retrievals, and action_message for messaging operations. The run_mqsc / run_mqsc_json pair follows a clear pattern with a format suffix.
15 tools form a well-scoped administration and messaging surface for IBM MQ: resource queries, message operations, and a generic MQSC escape hatch. Every tool serves a plausible purpose without bloat.
The domain is broadly covered: queue managers, queues, channels, subscriptions, topics, messages, and installation info all have query tools, with message put/get/publish/browse operations present. Minor gaps exist—no dedicated get_subscription or get_topic, no direct create/update/delete tools, and browse_message lacks pagination—but run_mqsc fills most gaps.