mcp-server-splunk-oncall 0.1.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.
- mcp_server_splunk_oncall-0.1.0/.gitignore +7 -0
- mcp_server_splunk_oncall-0.1.0/LICENSE +21 -0
- mcp_server_splunk_oncall-0.1.0/PKG-INFO +103 -0
- mcp_server_splunk_oncall-0.1.0/README.md +82 -0
- mcp_server_splunk_oncall-0.1.0/pyproject.toml +32 -0
- mcp_server_splunk_oncall-0.1.0/src/mcp_server_splunk_oncall/__init__.py +0 -0
- mcp_server_splunk_oncall-0.1.0/src/mcp_server_splunk_oncall/client.py +158 -0
- mcp_server_splunk_oncall-0.1.0/src/mcp_server_splunk_oncall/server.py +316 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 amendezsap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-server-splunk-oncall
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for Splunk On-Call (VictorOps) incident management
|
|
5
|
+
Project-URL: Homepage, https://github.com/amendezsap/mcp-server-splunk-oncall
|
|
6
|
+
Project-URL: Repository, https://github.com/amendezsap/mcp-server-splunk-oncall
|
|
7
|
+
Project-URL: Issues, https://github.com/amendezsap/mcp-server-splunk-oncall/issues
|
|
8
|
+
Author: amendezsap
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: incident-management,mcp,oncall,splunk,victorops
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: System :: Monitoring
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: httpx>=0.27.0
|
|
19
|
+
Requires-Dist: mcp>=1.0.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# mcp-server-splunk-oncall
|
|
23
|
+
|
|
24
|
+
MCP server for Splunk On-Call (VictorOps) incident management. Provides tools for managing incidents, on-call schedules, maintenance windows, and team operations from any MCP client.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uvx mcp-server-splunk-oncall
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or install from PyPI:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install mcp-server-splunk-oncall
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
The server requires two environment variables:
|
|
41
|
+
|
|
42
|
+
- `SPLUNK_ONCALL_API_ID` - Your Splunk On-Call API ID
|
|
43
|
+
- `SPLUNK_ONCALL_API_KEY` - Your Splunk On-Call API key
|
|
44
|
+
|
|
45
|
+
### Claude Code
|
|
46
|
+
|
|
47
|
+
Add to your Claude Code MCP settings:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"splunk-oncall": {
|
|
53
|
+
"command": "uvx",
|
|
54
|
+
"args": ["mcp-server-splunk-oncall"],
|
|
55
|
+
"env": {
|
|
56
|
+
"SPLUNK_ONCALL_API_ID": "your-api-id",
|
|
57
|
+
"SPLUNK_ONCALL_API_KEY": "your-api-key"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For read-only access, use a read-only API key. For full access (acknowledge, resolve, reroute incidents), use a full-access API key.
|
|
65
|
+
|
|
66
|
+
## Available Tools
|
|
67
|
+
|
|
68
|
+
### Incidents
|
|
69
|
+
- `list_incidents` - List all current incidents
|
|
70
|
+
- `acknowledge_incidents` - Acknowledge incidents by number
|
|
71
|
+
- `resolve_incidents` - Resolve incidents by number
|
|
72
|
+
- `reroute_incidents` - Reroute incidents to another user or policy
|
|
73
|
+
- `get_incident_timeline` - Get event timeline for an incident
|
|
74
|
+
- `get_incident_history` - Query historical incident data
|
|
75
|
+
|
|
76
|
+
### On-Call
|
|
77
|
+
- `get_oncall` - Who is currently on call across all teams
|
|
78
|
+
- `get_team_oncall_schedule` - On-call schedule for a team
|
|
79
|
+
- `get_user_oncall_schedule` - On-call schedule for a user
|
|
80
|
+
|
|
81
|
+
### Teams and Users
|
|
82
|
+
- `list_teams` - List all teams
|
|
83
|
+
- `get_team_members` - List members of a team
|
|
84
|
+
- `get_team_policies` - List escalation policies for a team
|
|
85
|
+
- `list_users` - List all users
|
|
86
|
+
- `get_user` - Get user details
|
|
87
|
+
|
|
88
|
+
### Routing and Policies
|
|
89
|
+
- `list_routing_keys` - List routing keys and their policies
|
|
90
|
+
- `list_policies` - List all escalation policies
|
|
91
|
+
- `get_policy` - Get escalation policy details
|
|
92
|
+
|
|
93
|
+
### Maintenance
|
|
94
|
+
- `list_maintenance` - List active and scheduled maintenance windows
|
|
95
|
+
- `create_maintenance` - Create a maintenance window
|
|
96
|
+
- `end_maintenance` - End a maintenance window early
|
|
97
|
+
|
|
98
|
+
### Organization
|
|
99
|
+
- `get_org_info` - Get organization information
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# mcp-server-splunk-oncall
|
|
2
|
+
|
|
3
|
+
MCP server for Splunk On-Call (VictorOps) incident management. Provides tools for managing incidents, on-call schedules, maintenance windows, and team operations from any MCP client.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uvx mcp-server-splunk-oncall
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install from PyPI:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install mcp-server-splunk-oncall
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
The server requires two environment variables:
|
|
20
|
+
|
|
21
|
+
- `SPLUNK_ONCALL_API_ID` - Your Splunk On-Call API ID
|
|
22
|
+
- `SPLUNK_ONCALL_API_KEY` - Your Splunk On-Call API key
|
|
23
|
+
|
|
24
|
+
### Claude Code
|
|
25
|
+
|
|
26
|
+
Add to your Claude Code MCP settings:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"splunk-oncall": {
|
|
32
|
+
"command": "uvx",
|
|
33
|
+
"args": ["mcp-server-splunk-oncall"],
|
|
34
|
+
"env": {
|
|
35
|
+
"SPLUNK_ONCALL_API_ID": "your-api-id",
|
|
36
|
+
"SPLUNK_ONCALL_API_KEY": "your-api-key"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For read-only access, use a read-only API key. For full access (acknowledge, resolve, reroute incidents), use a full-access API key.
|
|
44
|
+
|
|
45
|
+
## Available Tools
|
|
46
|
+
|
|
47
|
+
### Incidents
|
|
48
|
+
- `list_incidents` - List all current incidents
|
|
49
|
+
- `acknowledge_incidents` - Acknowledge incidents by number
|
|
50
|
+
- `resolve_incidents` - Resolve incidents by number
|
|
51
|
+
- `reroute_incidents` - Reroute incidents to another user or policy
|
|
52
|
+
- `get_incident_timeline` - Get event timeline for an incident
|
|
53
|
+
- `get_incident_history` - Query historical incident data
|
|
54
|
+
|
|
55
|
+
### On-Call
|
|
56
|
+
- `get_oncall` - Who is currently on call across all teams
|
|
57
|
+
- `get_team_oncall_schedule` - On-call schedule for a team
|
|
58
|
+
- `get_user_oncall_schedule` - On-call schedule for a user
|
|
59
|
+
|
|
60
|
+
### Teams and Users
|
|
61
|
+
- `list_teams` - List all teams
|
|
62
|
+
- `get_team_members` - List members of a team
|
|
63
|
+
- `get_team_policies` - List escalation policies for a team
|
|
64
|
+
- `list_users` - List all users
|
|
65
|
+
- `get_user` - Get user details
|
|
66
|
+
|
|
67
|
+
### Routing and Policies
|
|
68
|
+
- `list_routing_keys` - List routing keys and their policies
|
|
69
|
+
- `list_policies` - List all escalation policies
|
|
70
|
+
- `get_policy` - Get escalation policy details
|
|
71
|
+
|
|
72
|
+
### Maintenance
|
|
73
|
+
- `list_maintenance` - List active and scheduled maintenance windows
|
|
74
|
+
- `create_maintenance` - Create a maintenance window
|
|
75
|
+
- `end_maintenance` - End a maintenance window early
|
|
76
|
+
|
|
77
|
+
### Organization
|
|
78
|
+
- `get_org_info` - Get organization information
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mcp-server-splunk-oncall"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for Splunk On-Call (VictorOps) incident management"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "amendezsap" }]
|
|
13
|
+
keywords = ["mcp", "splunk", "victorops", "oncall", "incident-management"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: System Administrators",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: System :: Monitoring",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"mcp>=1.0.0",
|
|
23
|
+
"httpx>=0.27.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
mcp-server-splunk-oncall = "mcp_server_splunk_oncall.server:main"
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/amendezsap/mcp-server-splunk-oncall"
|
|
31
|
+
Repository = "https://github.com/amendezsap/mcp-server-splunk-oncall"
|
|
32
|
+
Issues = "https://github.com/amendezsap/mcp-server-splunk-oncall/issues"
|
|
File without changes
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""HTTP client for the Splunk On-Call (VictorOps) REST API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
API_BASE = "https://api.victorops.com/api-public"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SplunkOnCallClient:
|
|
12
|
+
"""Thin wrapper around the Splunk On-Call public REST API."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, api_id: str, api_key: str) -> None:
|
|
15
|
+
self._headers = {
|
|
16
|
+
"X-VO-Api-Id": api_id,
|
|
17
|
+
"X-VO-Api-Key": api_key,
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"Accept": "application/json",
|
|
20
|
+
}
|
|
21
|
+
self._client = httpx.AsyncClient(
|
|
22
|
+
base_url=API_BASE,
|
|
23
|
+
headers=self._headers,
|
|
24
|
+
timeout=30.0,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
async def close(self) -> None:
|
|
28
|
+
await self._client.aclose()
|
|
29
|
+
|
|
30
|
+
async def _request(self, method: str, path: str, **kwargs) -> dict:
|
|
31
|
+
resp = await self._client.request(method, path, **kwargs)
|
|
32
|
+
resp.raise_for_status()
|
|
33
|
+
return resp.json()
|
|
34
|
+
|
|
35
|
+
# -- Incidents --
|
|
36
|
+
|
|
37
|
+
async def list_incidents(self) -> dict:
|
|
38
|
+
return await self._request("GET", "/v2/incidents")
|
|
39
|
+
|
|
40
|
+
async def acknowledge_incidents(
|
|
41
|
+
self, user_name: str, incident_names: list[str], message: str | None = None,
|
|
42
|
+
) -> dict:
|
|
43
|
+
body: dict = {"userName": user_name, "incidentNames": incident_names}
|
|
44
|
+
if message:
|
|
45
|
+
body["message"] = message
|
|
46
|
+
return await self._request("POST", "/v1/incidents/acknowledge", json=body)
|
|
47
|
+
|
|
48
|
+
async def resolve_incidents(
|
|
49
|
+
self, user_name: str, incident_names: list[str], message: str | None = None,
|
|
50
|
+
) -> dict:
|
|
51
|
+
body: dict = {"userName": user_name, "incidentNames": incident_names}
|
|
52
|
+
if message:
|
|
53
|
+
body["message"] = message
|
|
54
|
+
return await self._request("POST", "/v1/incidents/resolve", json=body)
|
|
55
|
+
|
|
56
|
+
async def reroute_incidents(
|
|
57
|
+
self, user_name: str, incident_names: list[str], targets: list[dict],
|
|
58
|
+
) -> dict:
|
|
59
|
+
body = {
|
|
60
|
+
"userName": user_name,
|
|
61
|
+
"incidentNames": incident_names,
|
|
62
|
+
"targets": targets,
|
|
63
|
+
}
|
|
64
|
+
return await self._request("POST", "/v1/incidents/reroute", json=body)
|
|
65
|
+
|
|
66
|
+
async def get_incident_timeline(self, incident_number: str) -> dict:
|
|
67
|
+
return await self._request("GET", f"/v1/incidents/{incident_number}/timeline")
|
|
68
|
+
|
|
69
|
+
# -- On-Call --
|
|
70
|
+
|
|
71
|
+
async def get_oncall(self) -> dict:
|
|
72
|
+
return await self._request("GET", "/v2/oncall/current")
|
|
73
|
+
|
|
74
|
+
async def get_team_oncall_schedule(
|
|
75
|
+
self, team_slug: str, days_forward: int = 14, days_skip: int = 0,
|
|
76
|
+
) -> dict:
|
|
77
|
+
params = {"daysForward": days_forward, "daysSkip": days_skip}
|
|
78
|
+
return await self._request(
|
|
79
|
+
"GET", f"/v2/team/{team_slug}/oncall/schedule", params=params,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# -- Teams --
|
|
83
|
+
|
|
84
|
+
async def list_teams(self) -> dict:
|
|
85
|
+
return await self._request("GET", "/v1/team")
|
|
86
|
+
|
|
87
|
+
async def get_team(self, team_slug: str) -> dict:
|
|
88
|
+
return await self._request("GET", f"/v1/team/{team_slug}")
|
|
89
|
+
|
|
90
|
+
async def get_team_members(self, team_slug: str) -> dict:
|
|
91
|
+
return await self._request("GET", f"/v1/team/{team_slug}/members")
|
|
92
|
+
|
|
93
|
+
async def get_team_policies(self, team_slug: str) -> dict:
|
|
94
|
+
return await self._request("GET", f"/v1/team/{team_slug}/policies")
|
|
95
|
+
|
|
96
|
+
# -- Users --
|
|
97
|
+
|
|
98
|
+
async def list_users(self) -> dict:
|
|
99
|
+
return await self._request("GET", "/v1/user")
|
|
100
|
+
|
|
101
|
+
async def get_user(self, user_name: str) -> dict:
|
|
102
|
+
return await self._request("GET", f"/v1/user/{user_name}")
|
|
103
|
+
|
|
104
|
+
async def get_user_oncall_schedule(self, user_name: str) -> dict:
|
|
105
|
+
return await self._request("GET", f"/v1/user/{user_name}/oncall/schedule")
|
|
106
|
+
|
|
107
|
+
# -- Routing Keys --
|
|
108
|
+
|
|
109
|
+
async def list_routing_keys(self) -> dict:
|
|
110
|
+
return await self._request("GET", "/v1/org/routing-keys")
|
|
111
|
+
|
|
112
|
+
# -- Escalation Policies --
|
|
113
|
+
|
|
114
|
+
async def list_policies(self) -> dict:
|
|
115
|
+
return await self._request("GET", "/v1/policies")
|
|
116
|
+
|
|
117
|
+
async def get_policy(self, policy_slug: str) -> dict:
|
|
118
|
+
return await self._request("GET", f"/v1/policies/{policy_slug}")
|
|
119
|
+
|
|
120
|
+
# -- Maintenance Mode --
|
|
121
|
+
|
|
122
|
+
async def list_maintenance(self) -> dict:
|
|
123
|
+
return await self._request("GET", "/v1/maintenancemode")
|
|
124
|
+
|
|
125
|
+
async def create_maintenance(
|
|
126
|
+
self,
|
|
127
|
+
names: list[str],
|
|
128
|
+
purpose: str,
|
|
129
|
+
start_date: str | None = None,
|
|
130
|
+
end_date: str | None = None,
|
|
131
|
+
is_global: bool = False,
|
|
132
|
+
) -> dict:
|
|
133
|
+
body: dict = {"names": names, "purpose": purpose, "isGlobal": is_global}
|
|
134
|
+
if start_date:
|
|
135
|
+
body["startDate"] = start_date
|
|
136
|
+
if end_date:
|
|
137
|
+
body["endDate"] = end_date
|
|
138
|
+
return await self._request("POST", "/v1/maintenancemode", json=body)
|
|
139
|
+
|
|
140
|
+
async def end_maintenance(self, maintenance_id: str) -> dict:
|
|
141
|
+
return await self._request("DELETE", f"/v1/maintenancemode/{maintenance_id}")
|
|
142
|
+
|
|
143
|
+
# -- Organization --
|
|
144
|
+
|
|
145
|
+
async def get_org(self) -> dict:
|
|
146
|
+
return await self._request("GET", "/v1/org")
|
|
147
|
+
|
|
148
|
+
# -- Reporting --
|
|
149
|
+
|
|
150
|
+
async def get_incident_history(
|
|
151
|
+
self, start: str | None = None, end: str | None = None,
|
|
152
|
+
) -> dict:
|
|
153
|
+
params: dict = {}
|
|
154
|
+
if start:
|
|
155
|
+
params["startedAfter"] = start
|
|
156
|
+
if end:
|
|
157
|
+
params["startedBefore"] = end
|
|
158
|
+
return await self._request("GET", "/v2/reporting/incidents", params=params)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""MCP server for Splunk On-Call (VictorOps) incident management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from mcp.server.fastmcp import FastMCP
|
|
9
|
+
|
|
10
|
+
from .client import SplunkOnCallClient
|
|
11
|
+
|
|
12
|
+
mcp = FastMCP(
|
|
13
|
+
"splunk-oncall",
|
|
14
|
+
instructions="Manage Splunk On-Call (VictorOps) incidents, on-call schedules, and maintenance windows",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
_client: SplunkOnCallClient | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_client() -> SplunkOnCallClient:
|
|
21
|
+
global _client
|
|
22
|
+
if _client is None:
|
|
23
|
+
api_id = os.environ.get("SPLUNK_ONCALL_API_ID")
|
|
24
|
+
api_key = os.environ.get("SPLUNK_ONCALL_API_KEY")
|
|
25
|
+
if not api_id or not api_key:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"SPLUNK_ONCALL_API_ID and SPLUNK_ONCALL_API_KEY environment variables are required"
|
|
28
|
+
)
|
|
29
|
+
_client = SplunkOnCallClient(api_id, api_key)
|
|
30
|
+
return _client
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _fmt(data: dict) -> str:
|
|
34
|
+
return json.dumps(data, indent=2, default=str)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# -- Incidents --
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@mcp.tool()
|
|
41
|
+
async def list_incidents() -> str:
|
|
42
|
+
"""List all current incidents (triggered, acknowledged, resolved)."""
|
|
43
|
+
result = await _get_client().list_incidents()
|
|
44
|
+
return _fmt(result)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@mcp.tool()
|
|
48
|
+
async def acknowledge_incidents(
|
|
49
|
+
user_name: str, incident_numbers: list[str], message: str = "",
|
|
50
|
+
) -> str:
|
|
51
|
+
"""Acknowledge one or more incidents.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
user_name: Your Splunk On-Call username
|
|
55
|
+
incident_numbers: List of incident numbers to acknowledge (e.g. ["123", "456"])
|
|
56
|
+
message: Optional message to attach to the acknowledgment
|
|
57
|
+
"""
|
|
58
|
+
result = await _get_client().acknowledge_incidents(
|
|
59
|
+
user_name, incident_numbers, message or None,
|
|
60
|
+
)
|
|
61
|
+
return _fmt(result)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@mcp.tool()
|
|
65
|
+
async def resolve_incidents(
|
|
66
|
+
user_name: str, incident_numbers: list[str], message: str = "",
|
|
67
|
+
) -> str:
|
|
68
|
+
"""Resolve one or more incidents.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
user_name: Your Splunk On-Call username
|
|
72
|
+
incident_numbers: List of incident numbers to resolve (e.g. ["123", "456"])
|
|
73
|
+
message: Optional message to attach to the resolution
|
|
74
|
+
"""
|
|
75
|
+
result = await _get_client().resolve_incidents(
|
|
76
|
+
user_name, incident_numbers, message or None,
|
|
77
|
+
)
|
|
78
|
+
return _fmt(result)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@mcp.tool()
|
|
82
|
+
async def reroute_incidents(
|
|
83
|
+
user_name: str,
|
|
84
|
+
incident_numbers: list[str],
|
|
85
|
+
target_user: str = "",
|
|
86
|
+
target_policy: str = "",
|
|
87
|
+
) -> str:
|
|
88
|
+
"""Reroute incidents to another user or escalation policy.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
user_name: Your Splunk On-Call username
|
|
92
|
+
incident_numbers: List of incident numbers to reroute
|
|
93
|
+
target_user: Username to reroute to (provide this or target_policy)
|
|
94
|
+
target_policy: Escalation policy slug to reroute to (provide this or target_user)
|
|
95
|
+
"""
|
|
96
|
+
targets = []
|
|
97
|
+
if target_user:
|
|
98
|
+
targets.append({"type": "User", "slug": target_user})
|
|
99
|
+
if target_policy:
|
|
100
|
+
targets.append({"type": "EscalationPolicy", "slug": target_policy})
|
|
101
|
+
if not targets:
|
|
102
|
+
return "Error: provide either target_user or target_policy"
|
|
103
|
+
result = await _get_client().reroute_incidents(user_name, incident_numbers, targets)
|
|
104
|
+
return _fmt(result)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@mcp.tool()
|
|
108
|
+
async def get_incident_timeline(incident_number: str) -> str:
|
|
109
|
+
"""Get the event timeline for a specific incident.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
incident_number: The incident number to get the timeline for
|
|
113
|
+
"""
|
|
114
|
+
result = await _get_client().get_incident_timeline(incident_number)
|
|
115
|
+
return _fmt(result)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# -- On-Call --
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@mcp.tool()
|
|
122
|
+
async def get_oncall() -> str:
|
|
123
|
+
"""Get who is currently on call across all teams and policies."""
|
|
124
|
+
result = await _get_client().get_oncall()
|
|
125
|
+
return _fmt(result)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@mcp.tool()
|
|
129
|
+
async def get_team_oncall_schedule(
|
|
130
|
+
team_slug: str, days_forward: int = 14,
|
|
131
|
+
) -> str:
|
|
132
|
+
"""Get the on-call schedule for a specific team.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
team_slug: The team slug identifier
|
|
136
|
+
days_forward: Number of days to look ahead (default 14)
|
|
137
|
+
"""
|
|
138
|
+
result = await _get_client().get_team_oncall_schedule(team_slug, days_forward)
|
|
139
|
+
return _fmt(result)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@mcp.tool()
|
|
143
|
+
async def get_user_oncall_schedule(user_name: str) -> str:
|
|
144
|
+
"""Get the on-call schedule for a specific user.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
user_name: The username to look up
|
|
148
|
+
"""
|
|
149
|
+
result = await _get_client().get_user_oncall_schedule(user_name)
|
|
150
|
+
return _fmt(result)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# -- Teams --
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@mcp.tool()
|
|
157
|
+
async def list_teams() -> str:
|
|
158
|
+
"""List all teams in the organization."""
|
|
159
|
+
result = await _get_client().list_teams()
|
|
160
|
+
return _fmt(result)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@mcp.tool()
|
|
164
|
+
async def get_team_members(team_slug: str) -> str:
|
|
165
|
+
"""List members of a specific team.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
team_slug: The team slug identifier
|
|
169
|
+
"""
|
|
170
|
+
result = await _get_client().get_team_members(team_slug)
|
|
171
|
+
return _fmt(result)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@mcp.tool()
|
|
175
|
+
async def get_team_policies(team_slug: str) -> str:
|
|
176
|
+
"""List escalation policies for a specific team.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
team_slug: The team slug identifier
|
|
180
|
+
"""
|
|
181
|
+
result = await _get_client().get_team_policies(team_slug)
|
|
182
|
+
return _fmt(result)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# -- Users --
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@mcp.tool()
|
|
189
|
+
async def list_users() -> str:
|
|
190
|
+
"""List all users in the organization."""
|
|
191
|
+
result = await _get_client().list_users()
|
|
192
|
+
return _fmt(result)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@mcp.tool()
|
|
196
|
+
async def get_user(user_name: str) -> str:
|
|
197
|
+
"""Get details for a specific user.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
user_name: The username to look up
|
|
201
|
+
"""
|
|
202
|
+
result = await _get_client().get_user(user_name)
|
|
203
|
+
return _fmt(result)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# -- Routing Keys --
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@mcp.tool()
|
|
210
|
+
async def list_routing_keys() -> str:
|
|
211
|
+
"""List all routing keys and their associated escalation policies."""
|
|
212
|
+
result = await _get_client().list_routing_keys()
|
|
213
|
+
return _fmt(result)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# -- Escalation Policies --
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@mcp.tool()
|
|
220
|
+
async def list_policies() -> str:
|
|
221
|
+
"""List all escalation policies in the organization."""
|
|
222
|
+
result = await _get_client().list_policies()
|
|
223
|
+
return _fmt(result)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@mcp.tool()
|
|
227
|
+
async def get_policy(policy_slug: str) -> str:
|
|
228
|
+
"""Get details for a specific escalation policy.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
policy_slug: The escalation policy slug
|
|
232
|
+
"""
|
|
233
|
+
result = await _get_client().get_policy(policy_slug)
|
|
234
|
+
return _fmt(result)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# -- Maintenance Mode --
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@mcp.tool()
|
|
241
|
+
async def list_maintenance() -> str:
|
|
242
|
+
"""List all active and scheduled maintenance windows."""
|
|
243
|
+
result = await _get_client().list_maintenance()
|
|
244
|
+
return _fmt(result)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@mcp.tool()
|
|
248
|
+
async def create_maintenance(
|
|
249
|
+
routing_keys: list[str],
|
|
250
|
+
purpose: str,
|
|
251
|
+
end_date: str = "",
|
|
252
|
+
start_date: str = "",
|
|
253
|
+
) -> str:
|
|
254
|
+
"""Create a maintenance window to suppress alerts.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
routing_keys: List of routing key names to suppress alerts for
|
|
258
|
+
purpose: Description of why maintenance is being performed
|
|
259
|
+
end_date: ISO 8601 end time (e.g. "2026-04-09T06:00:00Z"). Required.
|
|
260
|
+
start_date: ISO 8601 start time. Defaults to now if omitted.
|
|
261
|
+
"""
|
|
262
|
+
if not end_date:
|
|
263
|
+
return "Error: end_date is required"
|
|
264
|
+
result = await _get_client().create_maintenance(
|
|
265
|
+
names=routing_keys,
|
|
266
|
+
purpose=purpose,
|
|
267
|
+
start_date=start_date or None,
|
|
268
|
+
end_date=end_date,
|
|
269
|
+
)
|
|
270
|
+
return _fmt(result)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@mcp.tool()
|
|
274
|
+
async def end_maintenance(maintenance_id: str) -> str:
|
|
275
|
+
"""End a maintenance window early.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
maintenance_id: The maintenance window ID to end
|
|
279
|
+
"""
|
|
280
|
+
result = await _get_client().end_maintenance(maintenance_id)
|
|
281
|
+
return _fmt(result)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# -- Organization --
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@mcp.tool()
|
|
288
|
+
async def get_org_info() -> str:
|
|
289
|
+
"""Get organization information."""
|
|
290
|
+
result = await _get_client().get_org()
|
|
291
|
+
return _fmt(result)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# -- Reporting --
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@mcp.tool()
|
|
298
|
+
async def get_incident_history(start: str = "", end: str = "") -> str:
|
|
299
|
+
"""Get historical incident data for reporting.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
start: ISO 8601 start time filter (e.g. "2026-04-01T00:00:00Z")
|
|
303
|
+
end: ISO 8601 end time filter (e.g. "2026-04-08T00:00:00Z")
|
|
304
|
+
"""
|
|
305
|
+
result = await _get_client().get_incident_history(
|
|
306
|
+
start=start or None, end=end or None,
|
|
307
|
+
)
|
|
308
|
+
return _fmt(result)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def main():
|
|
312
|
+
mcp.run()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
if __name__ == "__main__":
|
|
316
|
+
main()
|