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.
Files changed (72) hide show
  1. {hyperping-1.4.0 → hyperping-1.5.0}/CHANGELOG.md +116 -0
  2. {hyperping-1.4.0 → hyperping-1.5.0}/PKG-INFO +50 -1
  3. {hyperping-1.4.0 → hyperping-1.5.0}/README.md +49 -0
  4. {hyperping-1.4.0 → hyperping-1.5.0}/pyproject.toml +1 -1
  5. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/__init__.py +19 -3
  6. hyperping-1.5.0/src/hyperping/_async_mcp_client.py +249 -0
  7. hyperping-1.5.0/src/hyperping/_async_mcp_transport.py +199 -0
  8. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_mcp_transport.py +68 -12
  9. hyperping-1.5.0/src/hyperping/_version.py +1 -0
  10. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/mcp_client.py +65 -42
  11. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/__init__.py +21 -4
  12. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_healthcheck_models.py +3 -3
  13. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_incident_models.py +2 -2
  14. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_integration_models.py +1 -1
  15. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_maintenance_models.py +1 -1
  16. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_monitor_models.py +5 -5
  17. hyperping-1.5.0/src/hyperping/models/_observability_models.py +56 -0
  18. hyperping-1.5.0/src/hyperping/models/_oncall_models.py +42 -0
  19. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_outage_models.py +30 -19
  20. hyperping-1.5.0/src/hyperping/models/_reporting_models.py +94 -0
  21. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/models/_statuspage_models.py +2 -2
  22. hyperping-1.5.0/tests/unit/test_async_mcp_client.py +284 -0
  23. hyperping-1.5.0/tests/unit/test_async_mcp_transport.py +302 -0
  24. hyperping-1.5.0/tests/unit/test_async_preexisting.py +716 -0
  25. hyperping-1.5.0/tests/unit/test_client_coverage.py +277 -0
  26. hyperping-1.5.0/tests/unit/test_mcp_client.py +259 -0
  27. hyperping-1.5.0/tests/unit/test_mcp_transport.py +333 -0
  28. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_outages.py +85 -0
  29. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_sdk_surface.py +62 -0
  30. hyperping-1.4.0/src/hyperping/_version.py +0 -1
  31. hyperping-1.4.0/src/hyperping/models/_observability_models.py +0 -46
  32. hyperping-1.4.0/src/hyperping/models/_oncall_models.py +0 -27
  33. hyperping-1.4.0/src/hyperping/models/_reporting_models.py +0 -17
  34. hyperping-1.4.0/tests/unit/test_async_preexisting.py +0 -325
  35. hyperping-1.4.0/tests/unit/test_mcp_client.py +0 -161
  36. hyperping-1.4.0/tests/unit/test_mcp_transport.py +0 -161
  37. {hyperping-1.4.0 → hyperping-1.5.0}/.gitignore +0 -0
  38. {hyperping-1.4.0 → hyperping-1.5.0}/CONTRIBUTING.md +0 -0
  39. {hyperping-1.4.0 → hyperping-1.5.0}/LICENSE +0 -0
  40. {hyperping-1.4.0 → hyperping-1.5.0}/scripts/verify_endpoints.py +0 -0
  41. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_client.py +0 -0
  42. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_healthchecks_mixin.py +0 -0
  43. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_incidents_mixin.py +0 -0
  44. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_maintenance_mixin.py +0 -0
  45. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_monitors_mixin.py +0 -0
  46. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_outages_mixin.py +0 -0
  47. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_async_statuspages_mixin.py +0 -0
  48. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_circuit_breaker.py +0 -0
  49. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_healthchecks_mixin.py +0 -0
  50. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_incidents_mixin.py +0 -0
  51. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_internals.py +0 -0
  52. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_maintenance_mixin.py +0 -0
  53. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_monitor_constants.py +0 -0
  54. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_monitors_mixin.py +0 -0
  55. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_outages_mixin.py +0 -0
  56. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_protocols.py +0 -0
  57. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_statuspages_mixin.py +0 -0
  58. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/_utils.py +0 -0
  59. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/client.py +0 -0
  60. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/endpoints.py +0 -0
  61. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/exceptions.py +0 -0
  62. {hyperping-1.4.0 → hyperping-1.5.0}/src/hyperping/py.typed +0 -0
  63. {hyperping-1.4.0 → hyperping-1.5.0}/tests/__init__.py +0 -0
  64. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/__init__.py +0 -0
  65. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/conftest.py +0 -0
  66. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_async_client.py +0 -0
  67. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_healthchecks.py +0 -0
  68. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_incidents.py +0 -0
  69. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_maintenance.py +0 -0
  70. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_monitors.py +0 -0
  71. {hyperping-1.4.0 → hyperping-1.5.0}/tests/unit/test_pagination.py +0 -0
  72. {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.4.0
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.4.0"
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._mcp_transport import MCP_URL
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
- AlertNotification,
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
- "AlertNotification",
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]