zapi-mcp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zapi_mcp-0.2.0/PKG-INFO +242 -0
- zapi_mcp-0.2.0/README.md +219 -0
- zapi_mcp-0.2.0/pyproject.toml +62 -0
- zapi_mcp-0.2.0/setup.cfg +4 -0
- zapi_mcp-0.2.0/tests/test_categories.py +72 -0
- zapi_mcp-0.2.0/tests/test_client.py +270 -0
- zapi_mcp-0.2.0/tests/test_main.py +55 -0
- zapi_mcp-0.2.0/tests/test_server.py +458 -0
- zapi_mcp-0.2.0/tests/test_stdio_smoke.py +23 -0
- zapi_mcp-0.2.0/zapi_mcp/__init__.py +3 -0
- zapi_mcp-0.2.0/zapi_mcp/__main__.py +68 -0
- zapi_mcp-0.2.0/zapi_mcp/categories.py +82 -0
- zapi_mcp-0.2.0/zapi_mcp/client.py +286 -0
- zapi_mcp-0.2.0/zapi_mcp/server.py +502 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/PKG-INFO +242 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/SOURCES.txt +18 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/dependency_links.txt +1 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/entry_points.txt +2 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/requires.txt +2 -0
- zapi_mcp-0.2.0/zapi_mcp.egg-info/top_level.txt +1 -0
zapi_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zapi-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for Zabbix API — daily brief, problems, hosts, items
|
|
5
|
+
Author: AIKAWA Shigechika
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shigechika/zapi-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/shigechika/zapi-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/shigechika/zapi-mcp/issues
|
|
10
|
+
Keywords: zabbix,mcp,model-context-protocol,monitoring,network
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: mcp>=1.0
|
|
22
|
+
Requires-Dist: httpx
|
|
23
|
+
|
|
24
|
+
<!-- mcp-name: io.github.shigechika/zapi-mcp -->
|
|
25
|
+
|
|
26
|
+
# zapi-mcp
|
|
27
|
+
|
|
28
|
+
English | [日本語](README.ja.md)
|
|
29
|
+
|
|
30
|
+
MCP (Model Context Protocol) server for the [Zabbix](https://www.zabbix.com/) API.
|
|
31
|
+
|
|
32
|
+
Built for network operations: a single `daily_brief` call summarizes active
|
|
33
|
+
problems plus site-specific categories (DHCP pool usage, SNAT session usage,
|
|
34
|
+
core-network problems, …), and individual tools query problems, hosts, and item
|
|
35
|
+
values. Organization-specific tags live in a config file, not the code, so the
|
|
36
|
+
server stays generic.
|
|
37
|
+
|
|
38
|
+
Version-adaptive auth: works against Zabbix 6.0 LTS (`user` + `auth` field) and
|
|
39
|
+
forward-compatible with 6.4 / 7.0 (`username` + `Authorization: Bearer`).
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
| Tool | Description |
|
|
44
|
+
|------|-------------|
|
|
45
|
+
| `daily_brief` | Morning patrol: active problems (Warning+) plus one section per configured category |
|
|
46
|
+
| `get_problems` | Active problems by severity and tag, newest-first with age; header shows the true total (`showing N of TOTAL` when capped); output includes `eventid` |
|
|
47
|
+
| `get_hosts` | List hosts filtered by role/tag/group, with IP and tags |
|
|
48
|
+
| `get_host_items` | Current item values for a host (server-side host filter) |
|
|
49
|
+
| `acknowledge_problem` | Acknowledge problems and add a message (does not close them) |
|
|
50
|
+
|
|
51
|
+
## Setup
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# uv
|
|
55
|
+
uv pip install zapi-mcp
|
|
56
|
+
|
|
57
|
+
# pip
|
|
58
|
+
pip install zapi-mcp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or from source:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/shigechika/zapi-mcp.git
|
|
65
|
+
cd zapi-mcp
|
|
66
|
+
|
|
67
|
+
# uv
|
|
68
|
+
uv sync
|
|
69
|
+
|
|
70
|
+
# pip
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
Set the following environment variables:
|
|
77
|
+
|
|
78
|
+
| Variable | Description | Default |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `ZABBIX_URL` | Zabbix base URL (e.g. `https://zabbix.example.com`); `/api_jsonrpc.php` is appended if absent | *required* |
|
|
81
|
+
| `ZABBIX_USER` | Zabbix API user | *required* |
|
|
82
|
+
| `ZABBIX_PASSWORD` | Zabbix API password | *required* |
|
|
83
|
+
| `ZABBIX_CATEGORIES_INI` | Path to a categories INI file for `daily_brief` (optional) | — |
|
|
84
|
+
| `ZABBIX_BRIEF_RECENT_HOURS` | `daily_brief` "recent" window in hours; problems older than this are folded to a count | `24` |
|
|
85
|
+
| `ZABBIX_BRIEF_PROBLEM_LIMIT` | Max active problems `daily_brief` fetches per call before counting the rest | `1000` |
|
|
86
|
+
|
|
87
|
+
The API user needs read permission for the host groups you query, plus
|
|
88
|
+
acknowledge permission if you use `acknowledge_problem`.
|
|
89
|
+
|
|
90
|
+
### Active problems in `daily_brief`
|
|
91
|
+
|
|
92
|
+
Problems are grouped by severity and listed **newest-first**, each annotated with
|
|
93
|
+
its age (e.g. `3h ago`). Problems older than the recent window
|
|
94
|
+
(`ZABBIX_BRIEF_RECENT_HOURS`, default 24h) are folded to a single
|
|
95
|
+
`… and N older (stale; oldest …)` line — so a backlog of alerts that Zabbix
|
|
96
|
+
keeps active because their recovery is never auto-confirmed (ICMP ping down, RDP
|
|
97
|
+
down, …) doesn't bury what just happened. Section headers carry the true total
|
|
98
|
+
and show `showing N of TOTAL` when the fetch is capped, never a silent truncation.
|
|
99
|
+
|
|
100
|
+
### Categories for `daily_brief` (optional)
|
|
101
|
+
|
|
102
|
+
`daily_brief` always lists active problems. To add site-specific sections —
|
|
103
|
+
DHCP pool exhaustion, SNAT session usage, core-network problems — point
|
|
104
|
+
`ZABBIX_CATEGORIES_INI` at an INI file. Each `[section]` is one category:
|
|
105
|
+
|
|
106
|
+
```ini
|
|
107
|
+
[dhcp]
|
|
108
|
+
name = DHCP Pool Usage
|
|
109
|
+
tag = dhcp-pool-usage ; Zabbix host tag identifying the group
|
|
110
|
+
item_key = usage ; report current values for this exact item key
|
|
111
|
+
threshold = 80 ; flag values >= this
|
|
112
|
+
|
|
113
|
+
[snat]
|
|
114
|
+
name = SNAT Session Pool
|
|
115
|
+
tag = snat-pool-usage
|
|
116
|
+
item_key_search = .usage ; substring match (catches pool.node0.usage etc.)
|
|
117
|
+
threshold = 80
|
|
118
|
+
|
|
119
|
+
[core]
|
|
120
|
+
name = Core Network
|
|
121
|
+
tag = role
|
|
122
|
+
tag_value = main ; tag must equal this value
|
|
123
|
+
; no item key -> report active problems instead
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `tag` (required): host tag identifying the category. With `tag_value`, the tag
|
|
127
|
+
must equal it (Equal); without, any host carrying the tag matches (Exists).
|
|
128
|
+
- `item_key` / `item_key_search`: when either is set, the section reports current
|
|
129
|
+
item values sorted high-to-low. `item_key` matches the key exactly; use
|
|
130
|
+
`item_key_search` for keys that embed an id (e.g. `.usage` catches
|
|
131
|
+
`pool.node0.usage`). When neither is set, it reports active problems for the tag.
|
|
132
|
+
- `threshold`: optional; values at or above it are flagged.
|
|
133
|
+
|
|
134
|
+
See [`categories.ini.example`](categories.ini.example). When the variable is
|
|
135
|
+
unset or the file is missing, `daily_brief` reports active problems only.
|
|
136
|
+
|
|
137
|
+
## Usage
|
|
138
|
+
|
|
139
|
+
### Claude Code
|
|
140
|
+
|
|
141
|
+
Add to `.mcp.json`:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"mcpServers": {
|
|
146
|
+
"zapi-mcp": {
|
|
147
|
+
"type": "stdio",
|
|
148
|
+
"command": "zapi-mcp",
|
|
149
|
+
"env": {
|
|
150
|
+
"ZABBIX_URL": "https://zabbix.example.com",
|
|
151
|
+
"ZABBIX_USER": "api-user",
|
|
152
|
+
"ZABBIX_PASSWORD": "",
|
|
153
|
+
"ZABBIX_CATEGORIES_INI": "/path/to/categories.ini"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Claude Desktop
|
|
161
|
+
|
|
162
|
+
Add to `claude_desktop_config.json`:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"mcpServers": {
|
|
167
|
+
"zapi-mcp": {
|
|
168
|
+
"command": "zapi-mcp",
|
|
169
|
+
"env": {
|
|
170
|
+
"ZABBIX_URL": "https://zabbix.example.com",
|
|
171
|
+
"ZABBIX_USER": "api-user",
|
|
172
|
+
"ZABBIX_PASSWORD": ""
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Direct Execution
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
export ZABBIX_URL=https://zabbix.example.com
|
|
183
|
+
export ZABBIX_USER=api-user
|
|
184
|
+
export ZABBIX_PASSWORD=your-password
|
|
185
|
+
zapi-mcp
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### CLI Options
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
zapi-mcp --version # Print version and exit
|
|
192
|
+
zapi-mcp --check # Verify environment variables and authentication, then exit
|
|
193
|
+
zapi-mcp --brief # Print the daily_brief to stdout and exit (handy for cron)
|
|
194
|
+
zapi-mcp # Start MCP server (STDIO, default)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`--check` exit codes: `0` success, `1` config error, `2` auth/connection error.
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git clone https://github.com/shigechika/zapi-mcp.git
|
|
203
|
+
cd zapi-mcp
|
|
204
|
+
|
|
205
|
+
# uv
|
|
206
|
+
uv sync --dev
|
|
207
|
+
uv run pytest -v
|
|
208
|
+
uv run ruff check .
|
|
209
|
+
|
|
210
|
+
# pip
|
|
211
|
+
python3 -m venv .venv
|
|
212
|
+
.venv/bin/pip install -e . && .venv/bin/pip install pytest pytest-cov respx ruff
|
|
213
|
+
.venv/bin/pytest -v
|
|
214
|
+
.venv/bin/ruff check .
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Releasing
|
|
218
|
+
|
|
219
|
+
Releases are automated with [release-please](https://github.com/googleapis/release-please).
|
|
220
|
+
Merging [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, …)
|
|
221
|
+
to `main` keeps a release PR open with the next version and changelog. Merging
|
|
222
|
+
that PR tags `vX.Y.Z` and publishes a GitHub Release, whose `release: published`
|
|
223
|
+
event triggers the `release` workflow to build and publish to PyPI and the MCP
|
|
224
|
+
Registry. release-please owns the version in `zapi_mcp/__init__.py` and
|
|
225
|
+
`server.json` (do not bump them by hand).
|
|
226
|
+
|
|
227
|
+
> [!IMPORTANT]
|
|
228
|
+
> The release-please workflow should be given a repository secret
|
|
229
|
+
> `RELEASE_PLEASE_TOKEN` (a PAT with `contents: write` + `pull-requests: write`).
|
|
230
|
+
> The default `GITHUB_TOKEN` cannot create the Release that triggers the
|
|
231
|
+
> downstream `release` workflow (GitHub blocks workflow runs triggered by
|
|
232
|
+
> `GITHUB_TOKEN`), so without the PAT nothing gets published. The workflow falls
|
|
233
|
+
> back to `GITHUB_TOKEN` when the secret is unset so PR CI keeps working on forks.
|
|
234
|
+
|
|
235
|
+
## Roadmap
|
|
236
|
+
|
|
237
|
+
- Streamable HTTP transport + OAuth2 for remote / mobile use
|
|
238
|
+
- Visual rendering of key metrics
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT
|
zapi_mcp-0.2.0/README.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<!-- mcp-name: io.github.shigechika/zapi-mcp -->
|
|
2
|
+
|
|
3
|
+
# zapi-mcp
|
|
4
|
+
|
|
5
|
+
English | [日本語](README.ja.md)
|
|
6
|
+
|
|
7
|
+
MCP (Model Context Protocol) server for the [Zabbix](https://www.zabbix.com/) API.
|
|
8
|
+
|
|
9
|
+
Built for network operations: a single `daily_brief` call summarizes active
|
|
10
|
+
problems plus site-specific categories (DHCP pool usage, SNAT session usage,
|
|
11
|
+
core-network problems, …), and individual tools query problems, hosts, and item
|
|
12
|
+
values. Organization-specific tags live in a config file, not the code, so the
|
|
13
|
+
server stays generic.
|
|
14
|
+
|
|
15
|
+
Version-adaptive auth: works against Zabbix 6.0 LTS (`user` + `auth` field) and
|
|
16
|
+
forward-compatible with 6.4 / 7.0 (`username` + `Authorization: Bearer`).
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
| Tool | Description |
|
|
21
|
+
|------|-------------|
|
|
22
|
+
| `daily_brief` | Morning patrol: active problems (Warning+) plus one section per configured category |
|
|
23
|
+
| `get_problems` | Active problems by severity and tag, newest-first with age; header shows the true total (`showing N of TOTAL` when capped); output includes `eventid` |
|
|
24
|
+
| `get_hosts` | List hosts filtered by role/tag/group, with IP and tags |
|
|
25
|
+
| `get_host_items` | Current item values for a host (server-side host filter) |
|
|
26
|
+
| `acknowledge_problem` | Acknowledge problems and add a message (does not close them) |
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# uv
|
|
32
|
+
uv pip install zapi-mcp
|
|
33
|
+
|
|
34
|
+
# pip
|
|
35
|
+
pip install zapi-mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or from source:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/shigechika/zapi-mcp.git
|
|
42
|
+
cd zapi-mcp
|
|
43
|
+
|
|
44
|
+
# uv
|
|
45
|
+
uv sync
|
|
46
|
+
|
|
47
|
+
# pip
|
|
48
|
+
pip install -e .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
Set the following environment variables:
|
|
54
|
+
|
|
55
|
+
| Variable | Description | Default |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `ZABBIX_URL` | Zabbix base URL (e.g. `https://zabbix.example.com`); `/api_jsonrpc.php` is appended if absent | *required* |
|
|
58
|
+
| `ZABBIX_USER` | Zabbix API user | *required* |
|
|
59
|
+
| `ZABBIX_PASSWORD` | Zabbix API password | *required* |
|
|
60
|
+
| `ZABBIX_CATEGORIES_INI` | Path to a categories INI file for `daily_brief` (optional) | — |
|
|
61
|
+
| `ZABBIX_BRIEF_RECENT_HOURS` | `daily_brief` "recent" window in hours; problems older than this are folded to a count | `24` |
|
|
62
|
+
| `ZABBIX_BRIEF_PROBLEM_LIMIT` | Max active problems `daily_brief` fetches per call before counting the rest | `1000` |
|
|
63
|
+
|
|
64
|
+
The API user needs read permission for the host groups you query, plus
|
|
65
|
+
acknowledge permission if you use `acknowledge_problem`.
|
|
66
|
+
|
|
67
|
+
### Active problems in `daily_brief`
|
|
68
|
+
|
|
69
|
+
Problems are grouped by severity and listed **newest-first**, each annotated with
|
|
70
|
+
its age (e.g. `3h ago`). Problems older than the recent window
|
|
71
|
+
(`ZABBIX_BRIEF_RECENT_HOURS`, default 24h) are folded to a single
|
|
72
|
+
`… and N older (stale; oldest …)` line — so a backlog of alerts that Zabbix
|
|
73
|
+
keeps active because their recovery is never auto-confirmed (ICMP ping down, RDP
|
|
74
|
+
down, …) doesn't bury what just happened. Section headers carry the true total
|
|
75
|
+
and show `showing N of TOTAL` when the fetch is capped, never a silent truncation.
|
|
76
|
+
|
|
77
|
+
### Categories for `daily_brief` (optional)
|
|
78
|
+
|
|
79
|
+
`daily_brief` always lists active problems. To add site-specific sections —
|
|
80
|
+
DHCP pool exhaustion, SNAT session usage, core-network problems — point
|
|
81
|
+
`ZABBIX_CATEGORIES_INI` at an INI file. Each `[section]` is one category:
|
|
82
|
+
|
|
83
|
+
```ini
|
|
84
|
+
[dhcp]
|
|
85
|
+
name = DHCP Pool Usage
|
|
86
|
+
tag = dhcp-pool-usage ; Zabbix host tag identifying the group
|
|
87
|
+
item_key = usage ; report current values for this exact item key
|
|
88
|
+
threshold = 80 ; flag values >= this
|
|
89
|
+
|
|
90
|
+
[snat]
|
|
91
|
+
name = SNAT Session Pool
|
|
92
|
+
tag = snat-pool-usage
|
|
93
|
+
item_key_search = .usage ; substring match (catches pool.node0.usage etc.)
|
|
94
|
+
threshold = 80
|
|
95
|
+
|
|
96
|
+
[core]
|
|
97
|
+
name = Core Network
|
|
98
|
+
tag = role
|
|
99
|
+
tag_value = main ; tag must equal this value
|
|
100
|
+
; no item key -> report active problems instead
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `tag` (required): host tag identifying the category. With `tag_value`, the tag
|
|
104
|
+
must equal it (Equal); without, any host carrying the tag matches (Exists).
|
|
105
|
+
- `item_key` / `item_key_search`: when either is set, the section reports current
|
|
106
|
+
item values sorted high-to-low. `item_key` matches the key exactly; use
|
|
107
|
+
`item_key_search` for keys that embed an id (e.g. `.usage` catches
|
|
108
|
+
`pool.node0.usage`). When neither is set, it reports active problems for the tag.
|
|
109
|
+
- `threshold`: optional; values at or above it are flagged.
|
|
110
|
+
|
|
111
|
+
See [`categories.ini.example`](categories.ini.example). When the variable is
|
|
112
|
+
unset or the file is missing, `daily_brief` reports active problems only.
|
|
113
|
+
|
|
114
|
+
## Usage
|
|
115
|
+
|
|
116
|
+
### Claude Code
|
|
117
|
+
|
|
118
|
+
Add to `.mcp.json`:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"zapi-mcp": {
|
|
124
|
+
"type": "stdio",
|
|
125
|
+
"command": "zapi-mcp",
|
|
126
|
+
"env": {
|
|
127
|
+
"ZABBIX_URL": "https://zabbix.example.com",
|
|
128
|
+
"ZABBIX_USER": "api-user",
|
|
129
|
+
"ZABBIX_PASSWORD": "",
|
|
130
|
+
"ZABBIX_CATEGORIES_INI": "/path/to/categories.ini"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Claude Desktop
|
|
138
|
+
|
|
139
|
+
Add to `claude_desktop_config.json`:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"mcpServers": {
|
|
144
|
+
"zapi-mcp": {
|
|
145
|
+
"command": "zapi-mcp",
|
|
146
|
+
"env": {
|
|
147
|
+
"ZABBIX_URL": "https://zabbix.example.com",
|
|
148
|
+
"ZABBIX_USER": "api-user",
|
|
149
|
+
"ZABBIX_PASSWORD": ""
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Direct Execution
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
export ZABBIX_URL=https://zabbix.example.com
|
|
160
|
+
export ZABBIX_USER=api-user
|
|
161
|
+
export ZABBIX_PASSWORD=your-password
|
|
162
|
+
zapi-mcp
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### CLI Options
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
zapi-mcp --version # Print version and exit
|
|
169
|
+
zapi-mcp --check # Verify environment variables and authentication, then exit
|
|
170
|
+
zapi-mcp --brief # Print the daily_brief to stdout and exit (handy for cron)
|
|
171
|
+
zapi-mcp # Start MCP server (STDIO, default)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`--check` exit codes: `0` success, `1` config error, `2` auth/connection error.
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
git clone https://github.com/shigechika/zapi-mcp.git
|
|
180
|
+
cd zapi-mcp
|
|
181
|
+
|
|
182
|
+
# uv
|
|
183
|
+
uv sync --dev
|
|
184
|
+
uv run pytest -v
|
|
185
|
+
uv run ruff check .
|
|
186
|
+
|
|
187
|
+
# pip
|
|
188
|
+
python3 -m venv .venv
|
|
189
|
+
.venv/bin/pip install -e . && .venv/bin/pip install pytest pytest-cov respx ruff
|
|
190
|
+
.venv/bin/pytest -v
|
|
191
|
+
.venv/bin/ruff check .
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Releasing
|
|
195
|
+
|
|
196
|
+
Releases are automated with [release-please](https://github.com/googleapis/release-please).
|
|
197
|
+
Merging [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, …)
|
|
198
|
+
to `main` keeps a release PR open with the next version and changelog. Merging
|
|
199
|
+
that PR tags `vX.Y.Z` and publishes a GitHub Release, whose `release: published`
|
|
200
|
+
event triggers the `release` workflow to build and publish to PyPI and the MCP
|
|
201
|
+
Registry. release-please owns the version in `zapi_mcp/__init__.py` and
|
|
202
|
+
`server.json` (do not bump them by hand).
|
|
203
|
+
|
|
204
|
+
> [!IMPORTANT]
|
|
205
|
+
> The release-please workflow should be given a repository secret
|
|
206
|
+
> `RELEASE_PLEASE_TOKEN` (a PAT with `contents: write` + `pull-requests: write`).
|
|
207
|
+
> The default `GITHUB_TOKEN` cannot create the Release that triggers the
|
|
208
|
+
> downstream `release` workflow (GitHub blocks workflow runs triggered by
|
|
209
|
+
> `GITHUB_TOKEN`), so without the PAT nothing gets published. The workflow falls
|
|
210
|
+
> back to `GITHUB_TOKEN` when the secret is unset so PR CI keeps working on forks.
|
|
211
|
+
|
|
212
|
+
## Roadmap
|
|
213
|
+
|
|
214
|
+
- Streamable HTTP transport + OAuth2 for remote / mobile use
|
|
215
|
+
- Visual rendering of key metrics
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zapi-mcp"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "MCP server for Zabbix API — daily brief, problems, hosts, items"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "AIKAWA Shigechika"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["zabbix", "mcp", "model-context-protocol", "monitoring", "network"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: System Administrators",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: System :: Monitoring",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"mcp>=1.0",
|
|
28
|
+
"httpx",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
zapi-mcp = "zapi_mcp.__main__:main"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/shigechika/zapi-mcp"
|
|
36
|
+
Repository = "https://github.com/shigechika/zapi-mcp"
|
|
37
|
+
Issues = "https://github.com/shigechika/zapi-mcp/issues"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest",
|
|
42
|
+
"pytest-cov",
|
|
43
|
+
"respx",
|
|
44
|
+
"ruff",
|
|
45
|
+
"freezegun",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools]
|
|
49
|
+
packages = ["zapi_mcp"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.dynamic]
|
|
52
|
+
version = {attr = "zapi_mcp.__version__"}
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
target-version = "py310"
|
|
56
|
+
line-length = 120
|
|
57
|
+
|
|
58
|
+
[tool.ruff.lint]
|
|
59
|
+
select = ["E", "F", "I", "W", "UP"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
zapi_mcp-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Tests for category config loading."""
|
|
2
|
+
|
|
3
|
+
from zapi_mcp.categories import load_categories
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_no_path_returns_empty(monkeypatch):
|
|
7
|
+
monkeypatch.delenv("ZABBIX_CATEGORIES_INI", raising=False)
|
|
8
|
+
assert load_categories() == []
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_missing_file_returns_empty(tmp_path):
|
|
12
|
+
assert load_categories(str(tmp_path / "nope.ini")) == []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_item_category(tmp_path):
|
|
16
|
+
p = tmp_path / "cats.ini"
|
|
17
|
+
p.write_text("[dhcp]\nname = DHCP Pool Usage\ntag = dhcp-pool-usage\nitem_key = usage\nthreshold = 80\n")
|
|
18
|
+
cats = load_categories(str(p))
|
|
19
|
+
assert len(cats) == 1
|
|
20
|
+
c = cats[0]
|
|
21
|
+
assert c.key == "dhcp"
|
|
22
|
+
assert c.name == "DHCP Pool Usage"
|
|
23
|
+
assert c.tag == "dhcp-pool-usage"
|
|
24
|
+
assert c.item_key == "usage"
|
|
25
|
+
assert c.threshold == 80.0
|
|
26
|
+
assert c.kind == "items"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_problem_category_without_item_key(tmp_path):
|
|
30
|
+
p = tmp_path / "cats.ini"
|
|
31
|
+
p.write_text("[core]\nname = Core Network\ntag = role\ntag_value = main\n")
|
|
32
|
+
c = load_categories(str(p))[0]
|
|
33
|
+
assert c.tag_value == "main"
|
|
34
|
+
assert c.item_key is None
|
|
35
|
+
assert c.kind == "problems"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_section_without_tag_is_skipped(tmp_path):
|
|
39
|
+
p = tmp_path / "cats.ini"
|
|
40
|
+
p.write_text("[bad]\nname = No Tag\n\n[good]\ntag = role\n")
|
|
41
|
+
cats = load_categories(str(p))
|
|
42
|
+
assert [c.key for c in cats] == ["good"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_name_defaults_to_section(tmp_path):
|
|
46
|
+
p = tmp_path / "cats.ini"
|
|
47
|
+
p.write_text("[edge]\ntag = role\n")
|
|
48
|
+
assert load_categories(str(p))[0].name == "edge"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_env_var_used(tmp_path, monkeypatch):
|
|
52
|
+
p = tmp_path / "cats.ini"
|
|
53
|
+
p.write_text("[dhcp]\ntag = dhcp\nitem_key = usage\n")
|
|
54
|
+
monkeypatch.setenv("ZABBIX_CATEGORIES_INI", str(p))
|
|
55
|
+
cats = load_categories()
|
|
56
|
+
assert cats[0].key == "dhcp"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_non_numeric_threshold_does_not_crash(tmp_path):
|
|
60
|
+
p = tmp_path / "cats.ini"
|
|
61
|
+
p.write_text("[dhcp]\ntag = dhcp\nitem_key = usage\nthreshold = high\n")
|
|
62
|
+
cats = load_categories(str(p))
|
|
63
|
+
assert cats[0].threshold is None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_item_key_search_makes_items_category(tmp_path):
|
|
67
|
+
p = tmp_path / "cats.ini"
|
|
68
|
+
p.write_text("[snat]\ntag = snat\nitem_key_search = .usage\n")
|
|
69
|
+
c = load_categories(str(p))[0]
|
|
70
|
+
assert c.item_key is None
|
|
71
|
+
assert c.item_key_search == ".usage"
|
|
72
|
+
assert c.kind == "items"
|