workspaces-euc-mcp-server 0.1.6__tar.gz → 0.1.7__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.
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/CHANGELOG.md +10 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/DESIGN.md +1 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/PKG-INFO +2 -2
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/README.md +1 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier0-diagnostics.json +2 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier1-cost.json +2 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier2-lifecycle.json +2 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier3-destructive.json +2 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/pyproject.toml +1 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_secure_browser.py +24 -8
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/__init__.py +1 -1
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/models.py +29 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/secure_browser.py +67 -16
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.dockerignore +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.github/workflows/ci.yml +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.github/workflows/docker-publish.yml +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.github/workflows/publish.yml +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.gitignore +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.pre-commit-config.yaml +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/Dockerfile +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/LICENSE +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/README.md +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/scripts/smoke_readonly.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/__init__.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_clients.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_cost.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_destructive.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_diagnostics.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_governance.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_images.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_inventory.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_lifecycle.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_naming.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_no_embedded_secrets.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_performance.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_pricing.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_reporting.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/clients.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/consts.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/server.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/__init__.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/_common.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/cost.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/destructive.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/diagnostics.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/governance.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/images.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/inventory.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/lifecycle.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/performance.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/pricing.py +0 -0
- {workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/workspaces_euc_mcp_server/tools/reporting.py +0 -0
|
@@ -5,6 +5,16 @@ All notable changes to this project are documented here. The format is based on
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.1.7] - 2026-06-02
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `get_secure_browser_portal_usage` now reports **current active sessions live via
|
|
12
|
+
`workspaces-web:ListSessions`** (the same source as the console's active-sessions view) instead
|
|
13
|
+
of inferring "active" from CloudWatch. CloudWatch (`AWS/WorkSpacesWeb`) is now used **only for
|
|
14
|
+
historic** metrics, and the summary clearly separates live vs historic. Adds
|
|
15
|
+
`workspaces-web:ListSessions` to every IAM tier. The tool now returns a `SecureBrowserPortalUsage`
|
|
16
|
+
result (`active_session_count`, `active_sessions`, `historic_metrics`).
|
|
17
|
+
|
|
8
18
|
## [0.1.6] - 2026-06-02
|
|
9
19
|
|
|
10
20
|
### Added
|
|
@@ -135,7 +135,7 @@ and return a synthesized result, not raw API passthroughs.
|
|
|
135
135
|
| Tool | Purpose | IAM actions |
|
|
136
136
|
|---|---|---|
|
|
137
137
|
| `get_secure_browser_portal_details` | Portal config + associated settings (browser/network/user/IP-access) + resolved data-protection redaction config | `workspaces-web:GetPortal`, `workspaces-web:List*`, `workspaces-web:Get*Settings`, `workspaces-web:GetDataProtectionSettings` |
|
|
138
|
-
| `get_secure_browser_portal_usage` |
|
|
138
|
+
| `get_secure_browser_portal_usage` | Current active sessions (live, `ListSessions`) + historic CloudWatch metrics | `workspaces-web:ListPortals`, `workspaces-web:ListSessions`, `cloudwatch:GetMetricData` |
|
|
139
139
|
|
|
140
140
|
**Reporting & audit**
|
|
141
141
|
| Tool | Purpose | IAM actions |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: workspaces-euc-mcp-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: MCP server for administering the Amazon WorkSpaces family of End User Computing services (Personal, Pools, Applications, Secure Browser, Core).
|
|
5
5
|
Project-URL: Homepage, https://github.com/bengroeneveldsg/aws-workspaces-euc-mcp
|
|
6
6
|
Project-URL: Repository, https://github.com/bengroeneveldsg/aws-workspaces-euc-mcp
|
|
@@ -95,7 +95,7 @@ audit, and governance tools:
|
|
|
95
95
|
| `get_euc_audit_trail` | **"Who changed what"** — recent EUC management events from CloudTrail (last 90 days, no trail required) across all services; mutations-only by default, flags destructive actions and errors (e.g. AccessDenied) (Tier 0). |
|
|
96
96
|
| `get_euc_service_quotas` | **Service-quota limits + usage headroom** per EUC service; pairs limits with current usage (where AWS publishes a usage metric) to flag quotas approaching their limit — capacity planning (Tier 0). |
|
|
97
97
|
| `get_secure_browser_portal_details` | Resolves a Secure Browser portal's user settings (clipboard/print/download controls + timeouts), network, attached policies, and — when configured — the **data-protection redaction config** (which built-in/custom patterns are redacted, confidence level, enforced/exempt URLs) (Tier 0). |
|
|
98
|
-
| `get_secure_browser_portal_usage` | A Secure Browser portal's `AWS/WorkSpacesWeb`
|
|
98
|
+
| `get_secure_browser_portal_usage` | A Secure Browser portal's **current active sessions** (live, via `ListSessions` — same as the console) plus **historic** `AWS/WorkSpacesWeb` metrics over a window (CloudWatch is historic-only; idle portals publish none) (Tier 0). |
|
|
99
99
|
| `list_unused_resources` | Unused WorkSpaces desktops and stopped/zero-capacity fleets worth reclaiming (Tier 0). |
|
|
100
100
|
|
|
101
101
|
Cost/utilization tools need the **Tier 1** IAM policy ([`iam/tier1-cost.json`](iam/tier1-cost.json));
|
|
@@ -65,7 +65,7 @@ audit, and governance tools:
|
|
|
65
65
|
| `get_euc_audit_trail` | **"Who changed what"** — recent EUC management events from CloudTrail (last 90 days, no trail required) across all services; mutations-only by default, flags destructive actions and errors (e.g. AccessDenied) (Tier 0). |
|
|
66
66
|
| `get_euc_service_quotas` | **Service-quota limits + usage headroom** per EUC service; pairs limits with current usage (where AWS publishes a usage metric) to flag quotas approaching their limit — capacity planning (Tier 0). |
|
|
67
67
|
| `get_secure_browser_portal_details` | Resolves a Secure Browser portal's user settings (clipboard/print/download controls + timeouts), network, attached policies, and — when configured — the **data-protection redaction config** (which built-in/custom patterns are redacted, confidence level, enforced/exempt URLs) (Tier 0). |
|
|
68
|
-
| `get_secure_browser_portal_usage` | A Secure Browser portal's `AWS/WorkSpacesWeb`
|
|
68
|
+
| `get_secure_browser_portal_usage` | A Secure Browser portal's **current active sessions** (live, via `ListSessions` — same as the console) plus **historic** `AWS/WorkSpacesWeb` metrics over a window (CloudWatch is historic-only; idle portals publish none) (Tier 0). |
|
|
69
69
|
| `list_unused_resources` | Unused WorkSpaces desktops and stopped/zero-capacity fleets worth reclaiming (Tier 0). |
|
|
70
70
|
|
|
71
71
|
Cost/utilization tools need the **Tier 1** IAM policy ([`iam/tier1-cost.json`](iam/tier1-cost.json));
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier0-diagnostics.json
RENAMED
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"workspaces-web:ListNetworkSettings",
|
|
41
41
|
"workspaces-web:GetUserSettings",
|
|
42
42
|
"workspaces-web:GetNetworkSettings",
|
|
43
|
-
"workspaces-web:GetDataProtectionSettings"
|
|
43
|
+
"workspaces-web:GetDataProtectionSettings",
|
|
44
|
+
"workspaces-web:ListSessions"
|
|
44
45
|
],
|
|
45
46
|
"Resource": "*"
|
|
46
47
|
},
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"workspaces-web:ListNetworkSettings",
|
|
41
41
|
"workspaces-web:GetUserSettings",
|
|
42
42
|
"workspaces-web:GetNetworkSettings",
|
|
43
|
-
"workspaces-web:GetDataProtectionSettings"
|
|
43
|
+
"workspaces-web:GetDataProtectionSettings",
|
|
44
|
+
"workspaces-web:ListSessions"
|
|
44
45
|
],
|
|
45
46
|
"Resource": "*"
|
|
46
47
|
},
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier2-lifecycle.json
RENAMED
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"workspaces-web:ListNetworkSettings",
|
|
41
41
|
"workspaces-web:GetUserSettings",
|
|
42
42
|
"workspaces-web:GetNetworkSettings",
|
|
43
|
-
"workspaces-web:GetDataProtectionSettings"
|
|
43
|
+
"workspaces-web:GetDataProtectionSettings",
|
|
44
|
+
"workspaces-web:ListSessions"
|
|
44
45
|
],
|
|
45
46
|
"Resource": "*"
|
|
46
47
|
},
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/iam/tier3-destructive.json
RENAMED
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"workspaces-web:ListNetworkSettings",
|
|
41
41
|
"workspaces-web:GetUserSettings",
|
|
42
42
|
"workspaces-web:GetNetworkSettings",
|
|
43
|
-
"workspaces-web:GetDataProtectionSettings"
|
|
43
|
+
"workspaces-web:GetDataProtectionSettings",
|
|
44
|
+
"workspaces-web:ListSessions"
|
|
44
45
|
],
|
|
45
46
|
"Resource": "*"
|
|
46
47
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "workspaces-euc-mcp-server"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.7"
|
|
4
4
|
description = "MCP server for administering the Amazon WorkSpaces family of End User Computing services (Personal, Pools, Applications, Secure Browser, Core)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_secure_browser.py
RENAMED
|
@@ -108,19 +108,35 @@ def test_portal_details_resolves_data_protection_config():
|
|
|
108
108
|
assert dp["global_enforced_urls"] == ["*"]
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
def test_portal_usage_returns_live_active_sessions_and_historic():
|
|
112
|
+
# Active sessions must come LIVE from ListSessions (status=Active), not from CloudWatch.
|
|
113
|
+
captured = {}
|
|
114
|
+
|
|
115
|
+
def list_sessions(**kwargs):
|
|
116
|
+
captured.update(kwargs)
|
|
117
|
+
return {
|
|
118
|
+
"sessions": [
|
|
119
|
+
{"sessionId": "s-1", "username": "alice", "status": "Active"},
|
|
120
|
+
{"sessionId": "s-2", "username": "bob", "status": "Active"},
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
web = types.SimpleNamespace(list_sessions=list_sessions)
|
|
125
|
+
cw = types.SimpleNamespace(get_metric_data=lambda **_: {"MetricDataResults": []}) # no historic
|
|
126
|
+
factory = FakeFactory({consts.SECURE_BROWSER_API: web, consts.CLOUDWATCH_API: cw})
|
|
116
127
|
|
|
117
128
|
usage = secure_browser.get_secure_browser_portal_usage_core(
|
|
118
129
|
factory, "arn:aws:workspaces-web:r:a:portal/abc123", "us-east-1"
|
|
119
130
|
)
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
assert
|
|
123
|
-
assert "
|
|
132
|
+
# Queried ListSessions for the portal id with status=Active.
|
|
133
|
+
assert captured["portalId"] == "abc123"
|
|
134
|
+
assert captured["status"] == "Active"
|
|
135
|
+
# Live active sessions populated; CloudWatch reserved for (empty) historic.
|
|
136
|
+
assert usage.active_session_count == 2
|
|
137
|
+
assert {s.username for s in usage.active_sessions} == {"alice", "bob"}
|
|
138
|
+
assert usage.historic_metrics == {}
|
|
139
|
+
assert "active session(s) right now" in (usage.summary or "")
|
|
124
140
|
|
|
125
141
|
|
|
126
142
|
def test_portal_id_extracted_from_arn():
|
|
@@ -166,6 +166,35 @@ class SecureBrowserPortalDetails(BaseModel):
|
|
|
166
166
|
errors: list[ServiceError] = Field(default_factory=list)
|
|
167
167
|
|
|
168
168
|
|
|
169
|
+
class SecureBrowserSession(BaseModel):
|
|
170
|
+
"""A single Secure Browser portal session (from ListSessions)."""
|
|
171
|
+
|
|
172
|
+
session_id: str
|
|
173
|
+
username: str | None = None
|
|
174
|
+
status: str | None = None
|
|
175
|
+
start_time: str | None = None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class SecureBrowserPortalUsage(BaseModel):
|
|
179
|
+
"""Live active sessions (ListSessions) plus historic metrics (CloudWatch) for a portal."""
|
|
180
|
+
|
|
181
|
+
portal: str
|
|
182
|
+
active_session_count: int = 0
|
|
183
|
+
active_sessions: list[SecureBrowserSession] = Field(
|
|
184
|
+
default_factory=list,
|
|
185
|
+
description="Sessions currently Active, retrieved live from ListSessions (like the "
|
|
186
|
+
"console).",
|
|
187
|
+
)
|
|
188
|
+
lookback_days: int
|
|
189
|
+
period_hours: int
|
|
190
|
+
historic_metrics: dict[str, FleetMetricSeries] = Field(
|
|
191
|
+
default_factory=dict,
|
|
192
|
+
description="Historic session metrics from CloudWatch (AWS/WorkSpacesWeb) over the window.",
|
|
193
|
+
)
|
|
194
|
+
summary: str | None = None
|
|
195
|
+
errors: list[ServiceError] = Field(default_factory=list)
|
|
196
|
+
|
|
197
|
+
|
|
169
198
|
class UsageHistory(BaseModel):
|
|
170
199
|
"""Generic metric time-series history for a single EUC resource over a window."""
|
|
171
200
|
|
|
@@ -18,8 +18,13 @@ from typing import Any
|
|
|
18
18
|
|
|
19
19
|
from .. import consts
|
|
20
20
|
from ..clients import ClientFactory
|
|
21
|
-
from ..models import
|
|
22
|
-
|
|
21
|
+
from ..models import (
|
|
22
|
+
SecureBrowserPortalDetails,
|
|
23
|
+
SecureBrowserPortalUsage,
|
|
24
|
+
SecureBrowserSession,
|
|
25
|
+
ServiceError,
|
|
26
|
+
)
|
|
27
|
+
from ._common import paginate, read_only, try_call
|
|
23
28
|
from .performance import _fetch_metric_series
|
|
24
29
|
|
|
25
30
|
|
|
@@ -139,14 +144,54 @@ def _portal_id(portal: str) -> str:
|
|
|
139
144
|
return portal.rsplit("/", 1)[-1] if "/" in portal else portal
|
|
140
145
|
|
|
141
146
|
|
|
147
|
+
def _list_active_sessions(
|
|
148
|
+
web: Any, portal_id: str, errors: list[ServiceError]
|
|
149
|
+
) -> list[SecureBrowserSession]:
|
|
150
|
+
"""Current Active sessions for a portal, live from ListSessions (what the console shows)."""
|
|
151
|
+
raw = try_call(
|
|
152
|
+
errors,
|
|
153
|
+
consts.PRODUCT_SECURE_BROWSER,
|
|
154
|
+
"ListSessions",
|
|
155
|
+
lambda: paginate(
|
|
156
|
+
web.list_sessions,
|
|
157
|
+
"sessions",
|
|
158
|
+
pagination_in="nextToken",
|
|
159
|
+
pagination_out="nextToken",
|
|
160
|
+
portalId=portal_id,
|
|
161
|
+
status="Active",
|
|
162
|
+
),
|
|
163
|
+
default=[],
|
|
164
|
+
)
|
|
165
|
+
sessions: list[SecureBrowserSession] = []
|
|
166
|
+
for s in raw or []:
|
|
167
|
+
start = s.get("startTime")
|
|
168
|
+
sessions.append(
|
|
169
|
+
SecureBrowserSession(
|
|
170
|
+
session_id=s.get("sessionId", ""),
|
|
171
|
+
username=s.get("username"),
|
|
172
|
+
status=s.get("status"),
|
|
173
|
+
start_time=start.isoformat() if hasattr(start, "isoformat") else None,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
return sessions
|
|
177
|
+
|
|
178
|
+
|
|
142
179
|
def get_secure_browser_portal_usage_core(
|
|
143
180
|
factory: ClientFactory,
|
|
144
181
|
portal: str,
|
|
145
182
|
region: str | None,
|
|
146
183
|
lookback_days: int = 7,
|
|
147
184
|
period_hours: int = 24,
|
|
148
|
-
) ->
|
|
185
|
+
) -> SecureBrowserPortalUsage:
|
|
149
186
|
errors: list[ServiceError] = []
|
|
187
|
+
portal_id = _portal_id(portal)
|
|
188
|
+
web = factory.client(consts.SECURE_BROWSER_API, region=region)
|
|
189
|
+
|
|
190
|
+
# LIVE: current active sessions come from ListSessions (the real-time source the console uses),
|
|
191
|
+
# NOT from CloudWatch.
|
|
192
|
+
active = _list_active_sessions(web, portal_id, errors)
|
|
193
|
+
|
|
194
|
+
# HISTORIC: CloudWatch (AWS/WorkSpacesWeb) over the window — only meaningful for past activity.
|
|
150
195
|
cloudwatch = factory.client(consts.CLOUDWATCH_API, region=region)
|
|
151
196
|
metrics = try_call(
|
|
152
197
|
errors,
|
|
@@ -156,26 +201,30 @@ def get_secure_browser_portal_usage_core(
|
|
|
156
201
|
cloudwatch,
|
|
157
202
|
consts.SECURE_BROWSER_NAMESPACE,
|
|
158
203
|
consts.SECURE_BROWSER_PORTAL_DIMENSION,
|
|
159
|
-
|
|
204
|
+
portal_id,
|
|
160
205
|
consts.SECURE_BROWSER_SESSION_METRICS,
|
|
161
206
|
lookback_days,
|
|
162
207
|
period_hours,
|
|
163
208
|
),
|
|
164
209
|
default={},
|
|
165
210
|
)
|
|
211
|
+
|
|
212
|
+
hist = (
|
|
213
|
+
f"historic metrics over {lookback_days}d available (see historic_metrics)"
|
|
214
|
+
if metrics
|
|
215
|
+
else f"no historic session metrics in the last {lookback_days}d (idle portals publish "
|
|
216
|
+
"none to CloudWatch; enable the portal's Session Logger for detail)"
|
|
217
|
+
)
|
|
166
218
|
summary = (
|
|
167
|
-
"
|
|
168
|
-
"sessions occur (idle portals publish nothing); for detailed usage enable the portal's "
|
|
169
|
-
"Session Logger."
|
|
170
|
-
if not metrics
|
|
171
|
-
else f"Session metrics over {lookback_days}d: see series."
|
|
219
|
+
f"{len(active)} active session(s) right now (live via ListSessions). Historic: {hist}."
|
|
172
220
|
)
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
221
|
+
return SecureBrowserPortalUsage(
|
|
222
|
+
portal=portal,
|
|
223
|
+
active_session_count=len(active),
|
|
224
|
+
active_sessions=active,
|
|
176
225
|
lookback_days=lookback_days,
|
|
177
226
|
period_hours=period_hours,
|
|
178
|
-
|
|
227
|
+
historic_metrics=metrics or {},
|
|
179
228
|
summary=summary,
|
|
180
229
|
errors=errors,
|
|
181
230
|
)
|
|
@@ -210,10 +259,12 @@ def register(mcp: Any, factory: ClientFactory) -> None:
|
|
|
210
259
|
lookback_days: int = 7,
|
|
211
260
|
period_hours: int = 24,
|
|
212
261
|
) -> dict[str, Any]:
|
|
213
|
-
"""Get a Secure Browser portal's
|
|
262
|
+
"""Get a Secure Browser portal's CURRENT active sessions plus historic session metrics.
|
|
214
263
|
|
|
215
|
-
|
|
216
|
-
|
|
264
|
+
Active sessions are retrieved **live** from ListSessions (the same source as the console's
|
|
265
|
+
active-sessions view). Historic usage comes from CloudWatch (AWS/WorkSpacesWeb) over the
|
|
266
|
+
window — CloudWatch is only meaningful for *past* activity, and idle portals publish none.
|
|
267
|
+
Read-only.
|
|
217
268
|
|
|
218
269
|
Args:
|
|
219
270
|
portal: The portal id or ARN.
|
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.github/workflows/ci.yml
RENAMED
|
File without changes
|
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/.github/workflows/publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/scripts/smoke_readonly.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_destructive.py
RENAMED
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_diagnostics.py
RENAMED
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_governance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{workspaces_euc_mcp_server-0.1.6 → workspaces_euc_mcp_server-0.1.7}/tests/test_performance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|