hyperping 1.4.0__tar.gz → 1.5.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.
- {hyperping-1.4.0 → hyperping-1.5.0}/CHANGELOG.md +116 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/PKG-INFO +50 -1
- {hyperping-1.4.0 → hyperping-1.5.0}/README.md +49 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/pyproject.toml +1 -1
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/__init__.py +19 -3
- hyperping-1.5.0/src/hyperping/_async_mcp_client.py +249 -0
- hyperping-1.5.0/src/hyperping/_async_mcp_transport.py +199 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_mcp_transport.py +68 -12
- hyperping-1.5.0/src/hyperping/_version.py +1 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/mcp_client.py +65 -42
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/__init__.py +21 -4
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_healthcheck_models.py +3 -3
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_incident_models.py +2 -2
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_integration_models.py +1 -1
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_maintenance_models.py +1 -1
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_monitor_models.py +5 -5
- hyperping-1.5.0/src/hyperping/models/_observability_models.py +56 -0
- hyperping-1.5.0/src/hyperping/models/_oncall_models.py +42 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_outage_models.py +30 -19
- hyperping-1.5.0/src/hyperping/models/_reporting_models.py +94 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_statuspage_models.py +2 -2
- hyperping-1.5.0/tests/unit/test_async_mcp_client.py +284 -0
- hyperping-1.5.0/tests/unit/test_async_mcp_transport.py +302 -0
- hyperping-1.5.0/tests/unit/test_async_preexisting.py +716 -0
- hyperping-1.5.0/tests/unit/test_client_coverage.py +277 -0
- hyperping-1.5.0/tests/unit/test_mcp_client.py +259 -0
- hyperping-1.5.0/tests/unit/test_mcp_transport.py +333 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_outages.py +85 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_sdk_surface.py +62 -0
- hyperping-1.4.0/src/hyperping/_version.py +0 -1
- hyperping-1.4.0/src/hyperping/models/_observability_models.py +0 -46
- hyperping-1.4.0/src/hyperping/models/_oncall_models.py +0 -27
- hyperping-1.4.0/src/hyperping/models/_reporting_models.py +0 -17
- hyperping-1.4.0/tests/unit/test_async_preexisting.py +0 -325
- hyperping-1.4.0/tests/unit/test_mcp_client.py +0 -161
- hyperping-1.4.0/tests/unit/test_mcp_transport.py +0 -161
- {hyperping-1.4.0 → hyperping-1.5.0}/.gitignore +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/CONTRIBUTING.md +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/LICENSE +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/scripts/verify_endpoints.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_client.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_healthchecks_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_incidents_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_maintenance_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_monitors_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_outages_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_statuspages_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_circuit_breaker.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_healthchecks_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_incidents_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_internals.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_maintenance_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_monitor_constants.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_monitors_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_outages_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_protocols.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_statuspages_mixin.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_utils.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/client.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/endpoints.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/exceptions.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/py.typed +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/__init__.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/__init__.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/conftest.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_async_client.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_healthchecks.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_incidents.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_maintenance.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_monitors.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_pagination.py +0 -0
- {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_statuspages.py +0 -0
|
@@ -5,6 +5,115 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`AsyncHyperpingMcpClient`** -- full async counterpart to `HyperpingMcpClient`. All 16
|
|
13
|
+
MCP methods available via `await`. Uses `httpx.AsyncClient`, `asyncio.Lock`, and async
|
|
14
|
+
retry with `asyncio.sleep`. Exported from `hyperping` top-level.
|
|
15
|
+
- **Typed MCP returns** -- all 16 MCP client methods now return Pydantic models instead of
|
|
16
|
+
raw `dict[str, Any]`. Models verified against the live API.
|
|
17
|
+
- **New models**: `TimeGroup`, `ResponseTimeReport`, `AlertHistory`, `MonitorMetricsSummary`,
|
|
18
|
+
`MttrReport`, `MttaReport`, `ProbeLogResponse`, `TeamMember`, `OutageMonitorSummary`.
|
|
19
|
+
- **MCP error handling parity** -- both sync and async transports now map HTTP 404, 429,
|
|
20
|
+
400/422 to the same exception types as the REST client (`HyperpingNotFoundError`,
|
|
21
|
+
`HyperpingRateLimitError`, `HyperpingValidationError`).
|
|
22
|
+
- **MCP retry logic** -- automatic retry with exponential backoff on transient server
|
|
23
|
+
errors (500, 502, 503, 504) up to `max_retries` times (default 2).
|
|
24
|
+
- **Thread safety** -- `threading.Lock` (sync) and `asyncio.Lock` (async) protect the
|
|
25
|
+
request ID counter and initialization flag.
|
|
26
|
+
- **83 new tests** covering async MCP transport, client coverage, sync transport error
|
|
27
|
+
paths, async maintenance/outages, and protocol base classes. Overall coverage: 96%.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **Forward-compatible models** -- all 28 response models changed from `extra="ignore"` to
|
|
32
|
+
`extra="allow"`. New API fields are preserved instead of silently dropped.
|
|
33
|
+
- **Typed sub-objects** -- `OutageTimeline.outage` is now typed `Outage` (was `dict`),
|
|
34
|
+
`.monitor` is `OutageMonitorSummary` (was `dict`).
|
|
35
|
+
|
|
36
|
+
## [1.4.1] - 2026-04-20
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- HTTP 403 from MCP server now correctly raises `HyperpingAuthError` (was
|
|
41
|
+
`HyperpingAPIError`). The Hyperping MCP server returns 403 for invalid API keys;
|
|
42
|
+
the transport only handled 401. Now matches REST client behavior.
|
|
43
|
+
- `MCP_URL` defined in single location (`endpoints.py`); removed duplicate in
|
|
44
|
+
`_mcp_transport.py`.
|
|
45
|
+
- MCP handshake version string uses `__version__` instead of hardcoded `"1.4.0"`.
|
|
46
|
+
|
|
47
|
+
## [1.4.0] - 2026-04-19
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **`HyperpingMcpClient`** -- new client for Hyperping MCP server features not available
|
|
52
|
+
via the REST API. Uses JSON-RPC 2.0 over HTTP at `/v1/mcp` with the same Bearer token
|
|
53
|
+
API key. Provides 16 typed methods: `get_status_summary`, `get_monitor_response_time`,
|
|
54
|
+
`get_monitor_mtta`, `get_monitor_mttr`, `get_monitor_anomalies`, `get_monitor_http_logs`,
|
|
55
|
+
`list_recent_alerts`, `list_on_call_schedules`, `get_on_call_schedule`,
|
|
56
|
+
`list_escalation_policies`, `get_escalation_policy`, `list_team_members`,
|
|
57
|
+
`list_integrations`, `get_integration`, `get_outage_timeline`, `search_monitors_by_name`.
|
|
58
|
+
- **`McpTransport`** -- low-level JSON-RPC 2.0 transport with auto-initialization handshake,
|
|
59
|
+
double-parse response extraction, and error mapping to existing SDK exception types.
|
|
60
|
+
- **`MCP_URL`** constant exported from `hyperping` top-level.
|
|
61
|
+
- **Sync outage methods** -- `create_outage`, `delete_outage`, `get_outage`,
|
|
62
|
+
`unacknowledge_outage` added to `OutagesMixin` (were only in async client).
|
|
63
|
+
- **Verification script** -- `scripts/verify_endpoints.py` for testing endpoints against
|
|
64
|
+
the live API.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **Maintenance update** uses `model_dump(include=...)` instead of hard-coded field list.
|
|
69
|
+
- **Incident update error handling** -- `add_incident_update` now provides context when
|
|
70
|
+
the POST succeeds but the follow-up GET fails.
|
|
71
|
+
|
|
72
|
+
### Removed
|
|
73
|
+
|
|
74
|
+
- **Speculative REST methods** -- 12 methods that called nonexistent REST endpoints
|
|
75
|
+
(on-call, alerts, anomalies, integrations, probe logs, response time, MTTA, status
|
|
76
|
+
summary, outage timeline, monitor search) removed from `HyperpingClient` and
|
|
77
|
+
`AsyncHyperpingClient`. These features are MCP-only; use `HyperpingMcpClient` instead.
|
|
78
|
+
- **8 speculative mixin files** (sync + async) deleted.
|
|
79
|
+
- **8 speculative Endpoint enum entries** removed from `endpoints.py`.
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- HTTP 403 from MCP server now correctly raises `HyperpingAuthError` (was
|
|
84
|
+
`HyperpingAPIError`). Matches REST client behavior.
|
|
85
|
+
- `MCP_URL` defined in single location (`endpoints.py`), not duplicated.
|
|
86
|
+
- MCP handshake version uses `__version__` instead of hardcoded string.
|
|
87
|
+
- `pytest` bumped to 9.0.3 (CVE-2025-71176).
|
|
88
|
+
|
|
89
|
+
## [1.3.0] - 2026-04-18 [YANKED]
|
|
90
|
+
|
|
91
|
+
v1.3.0 added 18 speculative REST methods for MCP-discovered features (reporting,
|
|
92
|
+
observability, on-call, integrations). All 12 endpoint paths were guessed from MCP
|
|
93
|
+
tool names and **none of them work via the REST API** (verified: 10x 404, 2x 401).
|
|
94
|
+
These features are only accessible through the MCP server (JSON-RPC 2.0). Superseded
|
|
95
|
+
by v1.4.0 which replaces the broken REST methods with a proper `HyperpingMcpClient`.
|
|
96
|
+
|
|
97
|
+
## [1.2.1] - 2026-04-17
|
|
98
|
+
|
|
99
|
+
### Fixed
|
|
100
|
+
|
|
101
|
+
- Bump `pytest` to 9.0.3 (CVE-2025-71176).
|
|
102
|
+
|
|
103
|
+
## [1.2.0] - 2026-04-17
|
|
104
|
+
|
|
105
|
+
### Added
|
|
106
|
+
|
|
107
|
+
- **Sync outage methods** -- `create_outage`, `delete_outage`, `get_outage`,
|
|
108
|
+
`unacknowledge_outage` added to `OutagesMixin` for feature parity with async client.
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
|
|
112
|
+
- **Maintenance update** uses `model_dump(include=...)` instead of hard-coded field
|
|
113
|
+
enumeration for robustness.
|
|
114
|
+
- **Incident update error handling** -- `add_incident_update` now provides context when
|
|
115
|
+
the POST succeeds but the follow-up GET fails.
|
|
116
|
+
|
|
8
117
|
## [1.1.0] - 2026-04-09
|
|
9
118
|
|
|
10
119
|
### Added
|
|
@@ -30,6 +139,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
30
139
|
- **`collect_all_pages` / `collect_all_pages_async`** helpers in `_utils.py` for
|
|
31
140
|
transparent multi-page result aggregation.
|
|
32
141
|
|
|
142
|
+
## [1.0.1] - 2026-04-05
|
|
143
|
+
|
|
144
|
+
### Fixed
|
|
145
|
+
|
|
146
|
+
- Fix version string in `pyproject.toml` (was out of sync with `_version.py`).
|
|
147
|
+
- Fix package metadata: incorrect email address in project config.
|
|
148
|
+
|
|
33
149
|
## [1.0.0] - 2026-04-05
|
|
34
150
|
|
|
35
151
|
First stable release. The public API is production-ready and covered by semver
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperping
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Python SDK for the Hyperping uptime monitoring and incident management API
|
|
5
5
|
Project-URL: Homepage, https://github.com/develeap/hyperping-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/develeap/hyperping-python#readme
|
|
@@ -98,6 +98,18 @@ async def main():
|
|
|
98
98
|
|
|
99
99
|
The async client supports all the same resources, retry behaviour, and circuit breaker as the sync client. Use `RetryConfig` and `CircuitBreakerConfig` in exactly the same way.
|
|
100
100
|
|
|
101
|
+
An async MCP client is also available:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from hyperping import AsyncHyperpingMcpClient
|
|
105
|
+
|
|
106
|
+
async def main():
|
|
107
|
+
async with AsyncHyperpingMcpClient(api_key="sk_...") as mcp:
|
|
108
|
+
summary = await mcp.get_status_summary()
|
|
109
|
+
members = await mcp.list_team_members()
|
|
110
|
+
anomalies = await mcp.get_monitor_anomalies("mon_uuid")
|
|
111
|
+
```
|
|
112
|
+
|
|
101
113
|
## Authentication
|
|
102
114
|
|
|
103
115
|
Pass your API key directly or via environment variable:
|
|
@@ -181,6 +193,43 @@ sub = client.add_subscriber("sp_uuid", "user@example.com")
|
|
|
181
193
|
client.remove_subscriber("sp_uuid", sub.id)
|
|
182
194
|
```
|
|
183
195
|
|
|
196
|
+
### MCP Client (on-call, alerts, anomalies, integrations)
|
|
197
|
+
|
|
198
|
+
Some Hyperping features are only available via the MCP server (JSON-RPC 2.0),
|
|
199
|
+
not the REST API. Use `HyperpingMcpClient` for these:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from hyperping import HyperpingMcpClient
|
|
203
|
+
|
|
204
|
+
with HyperpingMcpClient(api_key="sk_...") as mcp:
|
|
205
|
+
# Status & reporting
|
|
206
|
+
summary = mcp.get_status_summary()
|
|
207
|
+
mtta = mcp.get_monitor_mtta("mon_uuid")
|
|
208
|
+
mttr = mcp.get_monitor_mttr("mon_uuid")
|
|
209
|
+
response_time = mcp.get_monitor_response_time("mon_uuid")
|
|
210
|
+
|
|
211
|
+
# On-call & escalation
|
|
212
|
+
schedules = mcp.list_on_call_schedules()
|
|
213
|
+
policies = mcp.list_escalation_policies()
|
|
214
|
+
members = mcp.list_team_members()
|
|
215
|
+
|
|
216
|
+
# Observability
|
|
217
|
+
anomalies = mcp.get_monitor_anomalies("mon_uuid")
|
|
218
|
+
logs = mcp.get_monitor_http_logs("mon_uuid")
|
|
219
|
+
alerts = mcp.list_recent_alerts()
|
|
220
|
+
|
|
221
|
+
# Integrations
|
|
222
|
+
integrations = mcp.list_integrations()
|
|
223
|
+
|
|
224
|
+
# Outage timeline & monitor search
|
|
225
|
+
timeline = mcp.get_outage_timeline("out_uuid")
|
|
226
|
+
results = mcp.search_monitors_by_name("api")
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The MCP client uses the same API key as `HyperpingClient`. All methods return
|
|
230
|
+
plain dicts/lists; use the exported Pydantic models (e.g., `OnCallSchedule`,
|
|
231
|
+
`EscalationPolicy`) for validation if needed.
|
|
232
|
+
|
|
184
233
|
### Healthchecks
|
|
185
234
|
|
|
186
235
|
```python
|
|
@@ -61,6 +61,18 @@ async def main():
|
|
|
61
61
|
|
|
62
62
|
The async client supports all the same resources, retry behaviour, and circuit breaker as the sync client. Use `RetryConfig` and `CircuitBreakerConfig` in exactly the same way.
|
|
63
63
|
|
|
64
|
+
An async MCP client is also available:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from hyperping import AsyncHyperpingMcpClient
|
|
68
|
+
|
|
69
|
+
async def main():
|
|
70
|
+
async with AsyncHyperpingMcpClient(api_key="sk_...") as mcp:
|
|
71
|
+
summary = await mcp.get_status_summary()
|
|
72
|
+
members = await mcp.list_team_members()
|
|
73
|
+
anomalies = await mcp.get_monitor_anomalies("mon_uuid")
|
|
74
|
+
```
|
|
75
|
+
|
|
64
76
|
## Authentication
|
|
65
77
|
|
|
66
78
|
Pass your API key directly or via environment variable:
|
|
@@ -144,6 +156,43 @@ sub = client.add_subscriber("sp_uuid", "user@example.com")
|
|
|
144
156
|
client.remove_subscriber("sp_uuid", sub.id)
|
|
145
157
|
```
|
|
146
158
|
|
|
159
|
+
### MCP Client (on-call, alerts, anomalies, integrations)
|
|
160
|
+
|
|
161
|
+
Some Hyperping features are only available via the MCP server (JSON-RPC 2.0),
|
|
162
|
+
not the REST API. Use `HyperpingMcpClient` for these:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from hyperping import HyperpingMcpClient
|
|
166
|
+
|
|
167
|
+
with HyperpingMcpClient(api_key="sk_...") as mcp:
|
|
168
|
+
# Status & reporting
|
|
169
|
+
summary = mcp.get_status_summary()
|
|
170
|
+
mtta = mcp.get_monitor_mtta("mon_uuid")
|
|
171
|
+
mttr = mcp.get_monitor_mttr("mon_uuid")
|
|
172
|
+
response_time = mcp.get_monitor_response_time("mon_uuid")
|
|
173
|
+
|
|
174
|
+
# On-call & escalation
|
|
175
|
+
schedules = mcp.list_on_call_schedules()
|
|
176
|
+
policies = mcp.list_escalation_policies()
|
|
177
|
+
members = mcp.list_team_members()
|
|
178
|
+
|
|
179
|
+
# Observability
|
|
180
|
+
anomalies = mcp.get_monitor_anomalies("mon_uuid")
|
|
181
|
+
logs = mcp.get_monitor_http_logs("mon_uuid")
|
|
182
|
+
alerts = mcp.list_recent_alerts()
|
|
183
|
+
|
|
184
|
+
# Integrations
|
|
185
|
+
integrations = mcp.list_integrations()
|
|
186
|
+
|
|
187
|
+
# Outage timeline & monitor search
|
|
188
|
+
timeline = mcp.get_outage_timeline("out_uuid")
|
|
189
|
+
results = mcp.search_monitors_by_name("api")
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The MCP client uses the same API key as `HyperpingClient`. All methods return
|
|
193
|
+
plain dicts/lists; use the exported Pydantic models (e.g., `OnCallSchedule`,
|
|
194
|
+
`EscalationPolicy`) for validation if needed.
|
|
195
|
+
|
|
147
196
|
### Healthchecks
|
|
148
197
|
|
|
149
198
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyperping"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.5.0"
|
|
8
8
|
description = "Python SDK for the Hyperping uptime monitoring and incident management API"
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -15,7 +15,7 @@ Quick start::
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from hyperping._async_client import AsyncHyperpingClient
|
|
18
|
-
from hyperping.
|
|
18
|
+
from hyperping._async_mcp_client import AsyncHyperpingMcpClient
|
|
19
19
|
from hyperping._version import __version__
|
|
20
20
|
from hyperping.client import (
|
|
21
21
|
CircuitBreaker,
|
|
@@ -26,6 +26,7 @@ from hyperping.client import (
|
|
|
26
26
|
)
|
|
27
27
|
from hyperping.endpoints import (
|
|
28
28
|
API_BASE,
|
|
29
|
+
MCP_URL,
|
|
29
30
|
APIVersion,
|
|
30
31
|
Endpoint,
|
|
31
32
|
)
|
|
@@ -40,7 +41,7 @@ from hyperping.mcp_client import HyperpingMcpClient
|
|
|
40
41
|
from hyperping.models import (
|
|
41
42
|
DEFAULT_REGIONS,
|
|
42
43
|
AddIncidentUpdateRequest,
|
|
43
|
-
|
|
44
|
+
AlertHistory,
|
|
44
45
|
DnsRecordType,
|
|
45
46
|
EscalationPolicy,
|
|
46
47
|
Healthcheck,
|
|
@@ -64,10 +65,13 @@ from hyperping.models import (
|
|
|
64
65
|
MonitorCreate,
|
|
65
66
|
MonitorFrequency,
|
|
66
67
|
MonitorListResponse,
|
|
68
|
+
MonitorMetricsSummary,
|
|
67
69
|
MonitorProtocol,
|
|
68
70
|
MonitorReport,
|
|
69
71
|
MonitorTimeout,
|
|
70
72
|
MonitorUpdate,
|
|
73
|
+
MttaReport,
|
|
74
|
+
MttrReport,
|
|
71
75
|
NotificationOption,
|
|
72
76
|
OnCallSchedule,
|
|
73
77
|
Outage,
|
|
@@ -77,14 +81,18 @@ from hyperping.models import (
|
|
|
77
81
|
OutageTimeline,
|
|
78
82
|
OutageTimelineEvent,
|
|
79
83
|
ProbeLog,
|
|
84
|
+
ProbeLogResponse,
|
|
80
85
|
Region,
|
|
81
86
|
ReportPeriod,
|
|
82
87
|
RequestHeader,
|
|
88
|
+
ResponseTimeReport,
|
|
83
89
|
StatusPage,
|
|
84
90
|
StatusPageCreate,
|
|
85
91
|
StatusPageSubscriber,
|
|
86
92
|
StatusPageUpdate,
|
|
87
93
|
StatusSummary,
|
|
94
|
+
TeamMember,
|
|
95
|
+
TimeGroup,
|
|
88
96
|
)
|
|
89
97
|
|
|
90
98
|
__all__ = [
|
|
@@ -92,6 +100,7 @@ __all__ = [
|
|
|
92
100
|
"__version__",
|
|
93
101
|
# Clients
|
|
94
102
|
"AsyncHyperpingClient",
|
|
103
|
+
"AsyncHyperpingMcpClient",
|
|
95
104
|
"HyperpingClient",
|
|
96
105
|
"HyperpingMcpClient",
|
|
97
106
|
# MCP
|
|
@@ -156,14 +165,21 @@ __all__ = [
|
|
|
156
165
|
# Observability
|
|
157
166
|
"MonitorAnomaly",
|
|
158
167
|
"ProbeLog",
|
|
159
|
-
"
|
|
168
|
+
"ProbeLogResponse",
|
|
160
169
|
# On-call
|
|
161
170
|
"OnCallSchedule",
|
|
162
171
|
"EscalationPolicy",
|
|
172
|
+
"TeamMember",
|
|
163
173
|
# Integrations
|
|
164
174
|
"Integration",
|
|
165
175
|
# Reporting
|
|
166
176
|
"StatusSummary",
|
|
177
|
+
"TimeGroup",
|
|
178
|
+
"ResponseTimeReport",
|
|
179
|
+
"AlertHistory",
|
|
180
|
+
"MonitorMetricsSummary",
|
|
181
|
+
"MttrReport",
|
|
182
|
+
"MttaReport",
|
|
167
183
|
# Healthchecks
|
|
168
184
|
"Healthcheck",
|
|
169
185
|
"HealthcheckCreate",
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Async high-level typed MCP client for the Hyperping MCP server.
|
|
2
|
+
|
|
3
|
+
Wraps :class:`~hyperping._async_mcp_transport.AsyncMcpTransport` with typed
|
|
4
|
+
convenience methods that mirror the MCP tool names exposed by the server.
|
|
5
|
+
|
|
6
|
+
Example::
|
|
7
|
+
|
|
8
|
+
from hyperping import AsyncHyperpingMcpClient
|
|
9
|
+
|
|
10
|
+
async with AsyncHyperpingMcpClient(api_key="sk_...") as mcp:
|
|
11
|
+
summary = await mcp.get_status_summary()
|
|
12
|
+
print(summary.total, summary.up, summary.down)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from pydantic import SecretStr
|
|
20
|
+
|
|
21
|
+
from hyperping._async_mcp_transport import AsyncMcpTransport
|
|
22
|
+
from hyperping.endpoints import MCP_URL
|
|
23
|
+
from hyperping.models._integration_models import Integration
|
|
24
|
+
from hyperping.models._monitor_models import Monitor
|
|
25
|
+
from hyperping.models._observability_models import MonitorAnomaly, ProbeLogResponse
|
|
26
|
+
from hyperping.models._oncall_models import EscalationPolicy, OnCallSchedule, TeamMember
|
|
27
|
+
from hyperping.models._outage_models import OutageTimeline
|
|
28
|
+
from hyperping.models._reporting_models import (
|
|
29
|
+
AlertHistory,
|
|
30
|
+
MttaReport,
|
|
31
|
+
MttrReport,
|
|
32
|
+
ResponseTimeReport,
|
|
33
|
+
StatusSummary,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AsyncHyperpingMcpClient:
|
|
38
|
+
"""Async high-level client for Hyperping MCP server tools.
|
|
39
|
+
|
|
40
|
+
Provides typed convenience methods for every MCP tool. Methods return
|
|
41
|
+
Pydantic models matching the verified API response shapes.
|
|
42
|
+
|
|
43
|
+
Supports the same ``api_key`` formats (``str`` or ``SecretStr``) and
|
|
44
|
+
async context-manager pattern as
|
|
45
|
+
:class:`~hyperping._async_client.AsyncHyperpingClient`.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
api_key: str | SecretStr,
|
|
51
|
+
base_url: str = MCP_URL,
|
|
52
|
+
timeout: float = 30.0,
|
|
53
|
+
) -> None:
|
|
54
|
+
self._transport = AsyncMcpTransport(
|
|
55
|
+
api_key=api_key,
|
|
56
|
+
base_url=base_url,
|
|
57
|
+
timeout=timeout,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# ==================== Internal ====================
|
|
61
|
+
|
|
62
|
+
async def _call(self, tool: str, args: dict[str, Any] | None = None) -> Any:
|
|
63
|
+
"""Call an MCP tool via the transport."""
|
|
64
|
+
return await self._transport.call_tool(tool, args or {})
|
|
65
|
+
|
|
66
|
+
# ==================== Context Manager ====================
|
|
67
|
+
|
|
68
|
+
async def close(self) -> None:
|
|
69
|
+
"""Close the underlying HTTP transport."""
|
|
70
|
+
await self._transport.close()
|
|
71
|
+
|
|
72
|
+
async def __aenter__(self) -> AsyncHyperpingMcpClient:
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
async def __aexit__(self, *args: object) -> None:
|
|
76
|
+
await self.close()
|
|
77
|
+
|
|
78
|
+
# ==================== Status & Reporting ====================
|
|
79
|
+
|
|
80
|
+
async def get_status_summary(self) -> StatusSummary:
|
|
81
|
+
"""Get aggregate monitor status counts."""
|
|
82
|
+
return StatusSummary.model_validate(await self._call("get_status_summary"))
|
|
83
|
+
|
|
84
|
+
async def get_monitor_response_time(
|
|
85
|
+
self,
|
|
86
|
+
monitor_uuid: str,
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> ResponseTimeReport:
|
|
89
|
+
"""Get response time metrics for a monitor.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
monitor_uuid: Monitor UUID.
|
|
93
|
+
**kwargs: Additional arguments forwarded to the MCP tool.
|
|
94
|
+
"""
|
|
95
|
+
return ResponseTimeReport.model_validate(
|
|
96
|
+
await self._call("get_monitor_response_time", {"uuid": monitor_uuid, **kwargs})
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
async def get_monitor_mtta(
|
|
100
|
+
self,
|
|
101
|
+
monitor_uuid: str | None = None,
|
|
102
|
+
**kwargs: Any,
|
|
103
|
+
) -> MttaReport:
|
|
104
|
+
"""Get mean time to acknowledge metrics.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
monitor_uuid: Optional monitor UUID to scope the query.
|
|
108
|
+
**kwargs: Additional arguments forwarded to the MCP tool.
|
|
109
|
+
"""
|
|
110
|
+
args: dict[str, Any] = {**kwargs}
|
|
111
|
+
if monitor_uuid is not None:
|
|
112
|
+
args["uuid"] = monitor_uuid
|
|
113
|
+
return MttaReport.model_validate(await self._call("get_monitor_mtta", args))
|
|
114
|
+
|
|
115
|
+
async def get_monitor_mttr(
|
|
116
|
+
self,
|
|
117
|
+
monitor_uuid: str | None = None,
|
|
118
|
+
**kwargs: Any,
|
|
119
|
+
) -> MttrReport:
|
|
120
|
+
"""Get mean time to resolve metrics.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
monitor_uuid: Optional monitor UUID to scope the query.
|
|
124
|
+
**kwargs: Additional arguments forwarded to the MCP tool.
|
|
125
|
+
"""
|
|
126
|
+
args: dict[str, Any] = {**kwargs}
|
|
127
|
+
if monitor_uuid is not None:
|
|
128
|
+
args["uuid"] = monitor_uuid
|
|
129
|
+
return MttrReport.model_validate(await self._call("get_monitor_mttr", args))
|
|
130
|
+
|
|
131
|
+
# ==================== Observability ====================
|
|
132
|
+
|
|
133
|
+
async def get_monitor_anomalies(self, monitor_uuid: str) -> list[MonitorAnomaly]:
|
|
134
|
+
"""Get anomalies detected for a monitor.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
monitor_uuid: Monitor UUID.
|
|
138
|
+
"""
|
|
139
|
+
data = await self._call("get_monitor_anomalies", {"uuid": monitor_uuid})
|
|
140
|
+
raw = data.get("anomalies", []) if isinstance(data, dict) else []
|
|
141
|
+
return [MonitorAnomaly.model_validate(a) for a in raw]
|
|
142
|
+
|
|
143
|
+
async def get_monitor_http_logs(
|
|
144
|
+
self,
|
|
145
|
+
monitor_uuid: str,
|
|
146
|
+
**kwargs: Any,
|
|
147
|
+
) -> ProbeLogResponse:
|
|
148
|
+
"""Get HTTP probe logs for a monitor.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
monitor_uuid: Monitor UUID.
|
|
152
|
+
**kwargs: Additional arguments forwarded to the MCP tool.
|
|
153
|
+
"""
|
|
154
|
+
data = await self._call("get_monitor_http_logs", {"uuid": monitor_uuid, **kwargs})
|
|
155
|
+
return ProbeLogResponse.model_validate(data)
|
|
156
|
+
|
|
157
|
+
# ==================== Alerts ====================
|
|
158
|
+
|
|
159
|
+
async def list_recent_alerts(self, **kwargs: Any) -> AlertHistory:
|
|
160
|
+
"""List recent alert notifications.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
**kwargs: Additional arguments forwarded to the MCP tool.
|
|
164
|
+
"""
|
|
165
|
+
return AlertHistory.model_validate(await self._call("list_recent_alerts", {**kwargs}))
|
|
166
|
+
|
|
167
|
+
# ==================== On-Call ====================
|
|
168
|
+
|
|
169
|
+
async def list_on_call_schedules(self) -> list[OnCallSchedule]:
|
|
170
|
+
"""List all on-call schedules."""
|
|
171
|
+
data = await self._call("list_on_call_schedules")
|
|
172
|
+
raw = data.get("schedules", []) if isinstance(data, dict) else []
|
|
173
|
+
return [OnCallSchedule.model_validate(s) for s in raw]
|
|
174
|
+
|
|
175
|
+
async def get_on_call_schedule(self, uuid: str) -> OnCallSchedule:
|
|
176
|
+
"""Get a single on-call schedule by UUID.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
uuid: Schedule UUID.
|
|
180
|
+
"""
|
|
181
|
+
return OnCallSchedule.model_validate(
|
|
182
|
+
await self._call("get_on_call_schedule", {"uuid": uuid})
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# ==================== Escalation Policies ====================
|
|
186
|
+
|
|
187
|
+
async def list_escalation_policies(self) -> list[EscalationPolicy]:
|
|
188
|
+
"""List all escalation policies."""
|
|
189
|
+
data = await self._call("list_escalation_policies")
|
|
190
|
+
raw = data if isinstance(data, list) else []
|
|
191
|
+
return [EscalationPolicy.model_validate(p) for p in raw]
|
|
192
|
+
|
|
193
|
+
async def get_escalation_policy(self, uuid: str) -> EscalationPolicy:
|
|
194
|
+
"""Get a single escalation policy by UUID.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
uuid: Escalation policy UUID.
|
|
198
|
+
"""
|
|
199
|
+
return EscalationPolicy.model_validate(
|
|
200
|
+
await self._call("get_escalation_policy", {"uuid": uuid})
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# ==================== Team ====================
|
|
204
|
+
|
|
205
|
+
async def list_team_members(self) -> list[TeamMember]:
|
|
206
|
+
"""List all team members."""
|
|
207
|
+
data = await self._call("list_team_members")
|
|
208
|
+
raw = data if isinstance(data, list) else []
|
|
209
|
+
return [TeamMember.model_validate(m) for m in raw]
|
|
210
|
+
|
|
211
|
+
# ==================== Integrations ====================
|
|
212
|
+
|
|
213
|
+
async def list_integrations(self) -> list[Integration]:
|
|
214
|
+
"""List all notification channel integrations."""
|
|
215
|
+
data = await self._call("list_integrations")
|
|
216
|
+
raw = data if isinstance(data, list) else []
|
|
217
|
+
return [Integration.model_validate(i) for i in raw]
|
|
218
|
+
|
|
219
|
+
async def get_integration(self, uuid: str) -> Integration:
|
|
220
|
+
"""Get a single integration by UUID.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
uuid: Integration UUID.
|
|
224
|
+
"""
|
|
225
|
+
return Integration.model_validate(await self._call("get_integration", {"uuid": uuid}))
|
|
226
|
+
|
|
227
|
+
# ==================== Outages ====================
|
|
228
|
+
|
|
229
|
+
async def get_outage_timeline(self, outage_uuid: str) -> OutageTimeline:
|
|
230
|
+
"""Get the lifecycle timeline for an outage.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
outage_uuid: Outage UUID.
|
|
234
|
+
"""
|
|
235
|
+
return OutageTimeline.model_validate(
|
|
236
|
+
await self._call("get_outage_timeline", {"uuid": outage_uuid})
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# ==================== Monitors ====================
|
|
240
|
+
|
|
241
|
+
async def search_monitors_by_name(self, query: str) -> list[Monitor]:
|
|
242
|
+
"""Search monitors by name.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
query: Search string to match against monitor names.
|
|
246
|
+
"""
|
|
247
|
+
data = await self._call("search_monitors_by_name", {"query": query})
|
|
248
|
+
raw = data if isinstance(data, list) else []
|
|
249
|
+
return [Monitor.model_validate(m) for m in raw]
|