workspaces-euc-mcp-server 0.1.1__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.1/.github/workflows/ci.yml +34 -0
- workspaces_euc_mcp_server-0.1.1/.github/workflows/publish.yml +35 -0
- workspaces_euc_mcp_server-0.1.1/.gitignore +33 -0
- workspaces_euc_mcp_server-0.1.1/.pre-commit-config.yaml +21 -0
- workspaces_euc_mcp_server-0.1.1/CHANGELOG.md +141 -0
- workspaces_euc_mcp_server-0.1.1/DESIGN.md +203 -0
- workspaces_euc_mcp_server-0.1.1/LICENSE +201 -0
- workspaces_euc_mcp_server-0.1.1/PKG-INFO +270 -0
- workspaces_euc_mcp_server-0.1.1/README.md +240 -0
- workspaces_euc_mcp_server-0.1.1/iam/README.md +29 -0
- workspaces_euc_mcp_server-0.1.1/iam/tier0-diagnostics.json +68 -0
- workspaces_euc_mcp_server-0.1.1/iam/tier1-cost.json +78 -0
- workspaces_euc_mcp_server-0.1.1/iam/tier2-lifecycle.json +109 -0
- workspaces_euc_mcp_server-0.1.1/iam/tier3-destructive.json +119 -0
- workspaces_euc_mcp_server-0.1.1/pyproject.toml +78 -0
- workspaces_euc_mcp_server-0.1.1/scripts/smoke_readonly.py +77 -0
- workspaces_euc_mcp_server-0.1.1/tests/__init__.py +0 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_clients.py +74 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_cost.py +147 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_destructive.py +189 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_diagnostics.py +342 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_inventory.py +160 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_lifecycle.py +231 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_naming.py +69 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_no_embedded_secrets.py +44 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_performance.py +256 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_pricing.py +97 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_reporting.py +306 -0
- workspaces_euc_mcp_server-0.1.1/tests/test_secure_browser.py +85 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/__init__.py +6 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/clients.py +101 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/consts.py +154 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/models.py +333 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/server.py +129 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/__init__.py +4 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/_common.py +87 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/cost.py +314 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/destructive.py +307 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/diagnostics.py +799 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/inventory.py +158 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/lifecycle.py +564 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/performance.py +620 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/pricing.py +152 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/reporting.py +529 -0
- workspaces_euc_mcp_server-0.1.1/workspaces_euc_mcp_server/tools/secure_browser.py +190 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install (into .venv to match the pyright config)
|
|
20
|
+
run: |
|
|
21
|
+
python -m venv .venv
|
|
22
|
+
. .venv/bin/activate
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
- name: Lint
|
|
26
|
+
run: . .venv/bin/activate && ruff check .
|
|
27
|
+
- name: Format check
|
|
28
|
+
run: . .venv/bin/activate && ruff format --check .
|
|
29
|
+
- name: Type check
|
|
30
|
+
run: . .venv/bin/activate && pyright
|
|
31
|
+
- name: Security scan
|
|
32
|
+
run: . .venv/bin/activate && bandit -c pyproject.toml -r workspaces_euc_mcp_server
|
|
33
|
+
- name: Test (includes no-embedded-secrets guardrail)
|
|
34
|
+
run: . .venv/bin/activate && pytest -q
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes the package to PyPI using OIDC Trusted Publishing (no API token stored).
|
|
4
|
+
# Fires when a GitHub Release is published, or can be run manually from the Actions tab.
|
|
5
|
+
#
|
|
6
|
+
# One-time PyPI setup (project owner): add a "Trusted Publisher" (pending publisher is fine before
|
|
7
|
+
# the first release) at https://pypi.org/manage/account/publishing/ with:
|
|
8
|
+
# PyPI project name: workspaces-euc-mcp-server
|
|
9
|
+
# Owner: bengroeneveldsg
|
|
10
|
+
# Repository: aws-workspaces-euc-mcp
|
|
11
|
+
# Workflow name: publish.yml
|
|
12
|
+
# Environment: pypi
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
release:
|
|
16
|
+
types: [published]
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
publish:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
environment: pypi
|
|
23
|
+
permissions:
|
|
24
|
+
id-token: write # required for OIDC trusted publishing
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Build sdist + wheel
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade build
|
|
33
|
+
python -m build
|
|
34
|
+
- name: Publish to PyPI
|
|
35
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Tooling caches
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.pyright/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
|
|
18
|
+
# OS / editor / tooling
|
|
19
|
+
.DS_Store
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
.claude/
|
|
23
|
+
|
|
24
|
+
# AWS / secrets — never commit credentials
|
|
25
|
+
.aws/
|
|
26
|
+
*.pem
|
|
27
|
+
.env
|
|
28
|
+
|
|
29
|
+
# Generated, account-specific report artifacts — these contain tenant data
|
|
30
|
+
# (real WorkSpace IDs, metrics, costs) and must never be committed.
|
|
31
|
+
*-perf.html
|
|
32
|
+
ws-*.html
|
|
33
|
+
/reports/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/PyCQA/bandit
|
|
9
|
+
rev: 1.7.10
|
|
10
|
+
hooks:
|
|
11
|
+
- id: bandit
|
|
12
|
+
args: ["-c", "pyproject.toml"]
|
|
13
|
+
additional_dependencies: ["bandit[toml]"]
|
|
14
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
15
|
+
rev: v5.0.0
|
|
16
|
+
hooks:
|
|
17
|
+
- id: end-of-file-fixer
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
- id: check-yaml
|
|
20
|
+
- id: check-added-large-files
|
|
21
|
+
- id: detect-private-key
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.1] - 2026-06-01
|
|
9
|
+
|
|
10
|
+
Best-practice alignment pass (audited against the awslabs MCP design guidelines and the MCP
|
|
11
|
+
tool-annotations spec).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- MCP **tool annotations** on every tool (`readOnlyHint` / `destructiveHint` / `idempotentHint` /
|
|
15
|
+
`openWorldHint` / `title`), so clients can show appropriate consent UX — read-only tools are
|
|
16
|
+
marked read-only, lifecycle writes non-destructive (start/stop/modify idempotent), and
|
|
17
|
+
terminate/rebuild/restore destructive. Aligns with the awslabs MCP design guidelines and the
|
|
18
|
+
MCP tool-annotations spec.
|
|
19
|
+
- `FASTMCP_LOG_LEVEL` env var to control the server log level (default `INFO`).
|
|
20
|
+
- `Literal` types on fixed-value parameters (`running_mode`, cost `granularity`) for clearer
|
|
21
|
+
client-side validation.
|
|
22
|
+
|
|
23
|
+
## [0.1.0] - 2026-06-01
|
|
24
|
+
|
|
25
|
+
First tagged release. Admin-focused MCP server for the Amazon WorkSpaces EUC portfolio —
|
|
26
|
+
18 read-only tools (inventory, diagnostics, performance/usage history, cost & right-sizing with
|
|
27
|
+
$ estimates, audit, reporting) and 10 guarded write/destructive tools, across WorkSpaces Personal,
|
|
28
|
+
Pools, Applications, Secure Browser, and Core Managed Instances. Four additive IAM tiers,
|
|
29
|
+
multi-account/MSP via assume-role, no embedded tenant data, CI (ruff/format/pyright/bandit/pytest
|
|
30
|
+
on Py 3.11–3.13).
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- CI now also runs `pyright` (basic type-checking); the codebase type-checks clean.
|
|
34
|
+
- Multi-account / MSP support: `--assume-role-arn` (+ optional `--external-id`) transparently
|
|
35
|
+
`sts:AssumeRole`s into another account, with auto-refreshing credentials and no tool-code
|
|
36
|
+
changes. The launching identity needs `sts:AssumeRole` on the target role; the role needs the
|
|
37
|
+
matching tier policy.
|
|
38
|
+
- Estimated monthly $ savings on recommendations, via the AWS Price List API (new
|
|
39
|
+
`tools/pricing.py`, needs `pricing:GetProducts` / Tier 1): `recommend_running_mode` now fills
|
|
40
|
+
`estimated_monthly_savings_usd` for AlwaysOn→AutoStop candidates (AlwaysOn monthly − AutoStop
|
|
41
|
+
base − hourly×estimated-usage), and `recommend_bundle_rightsizing` fills the AlwaysOn
|
|
42
|
+
compute-tier monthly difference. Best-effort and conservative: matches the canonical
|
|
43
|
+
Included-license SKU on region/OS/compute/volume sizes and returns **null** (never a wrong
|
|
44
|
+
number) when it can't match. Validated live (Standard 80/50 → $35/mo, Power 175/100 → $98/mo).
|
|
45
|
+
- `diagnose_pool` (Tier 0): a WorkSpaces Pool health diagnostic correlating pool state, pool
|
|
46
|
+
errors, user-session capacity, backing directory health, and CloudWatch session utilization —
|
|
47
|
+
bringing Pools to parity with the WorkSpace and fleet diagnostics.
|
|
48
|
+
- WorkSpaces Core Managed Instances coverage (the `workspaces-instances` API, previously not
|
|
49
|
+
covered at all): `get_euc_inventory_summary` and `generate_inventory_report` now include managed
|
|
50
|
+
instances, enriched with the backing EC2 instance's type / state / launch time / private IP /
|
|
51
|
+
platform (via `ec2:DescribeInstances`). Adds the
|
|
52
|
+
`workspaces-instances:ListWorkspaceInstances`/`GetWorkspaceInstance`/`ListInstanceTypes`/`ListRegions`
|
|
53
|
+
and `ec2:DescribeInstances` IAM actions to all tiers.
|
|
54
|
+
- WorkSpaces Secure Browser parity: `get_secure_browser_portal_details` (resolves user/network
|
|
55
|
+
settings — clipboard/print/download controls) and `get_secure_browser_portal_usage`
|
|
56
|
+
(`AWS/WorkSpacesWeb` session metrics; note Secure Browser only emits these during sessions, so
|
|
57
|
+
idle portals return nothing). Adds `workspaces-web:GetUserSettings`/`GetNetworkSettings` to the
|
|
58
|
+
IAM tiers.
|
|
59
|
+
- `audit_security_posture` is now **cross-service**: in addition to WorkSpace volume encryption and
|
|
60
|
+
directory IP access control groups, it flags **Secure Browser portals** and **Applications
|
|
61
|
+
stacks** that permit data egress (clipboard-to-local / download / print).
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
- `generate_inventory_report` Pools capacity was always `null` — it read the wrong key (`Capacity`
|
|
65
|
+
instead of `CapacityStatus`). Now populated with the real session capacity.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- WorkSpaces Personal **user mapping** + many previously-dropped fields in `generate_inventory_report`
|
|
69
|
+
(found by auditing the raw API responses): each desktop now exposes assigned `UserName` (as the
|
|
70
|
+
record label), `ComputerName`, `IpAddress`, plus `OperatingSystemName`, `Protocols`, root/user
|
|
71
|
+
volume sizes, AutoStop timeout, root/user encryption flags, and subnet. `diagnose_workspace_connectivity`
|
|
72
|
+
signals now include `user_name`/`computer_name`. Pools also expose bundle/directory/running-mode/
|
|
73
|
+
description; Applications fleets expose image name and session/idle/disconnect timeouts; stacks
|
|
74
|
+
expose `UserSettings` (clipboard/print/file-transfer controls) and storage connectors; Secure
|
|
75
|
+
Browser portals expose authentication type, max concurrent sessions, instance and renderer type.
|
|
76
|
+
- `get_workspace_connection_history` and `get_pool_session_history` (Tier 0): time-series usage
|
|
77
|
+
history for WorkSpaces Personal desktops (UserConnected + connection attempts/failures) and
|
|
78
|
+
WorkSpaces Pools (Active/Actual/Available/Desired/Pending user-session capacity +
|
|
79
|
+
utilization), each with a plain-language summary that flags unused desktops / idle pool
|
|
80
|
+
capacity. Generalized the CloudWatch series fetch and added a generic `UsageHistory` model.
|
|
81
|
+
Note: the Pools CloudWatch dimension is literally `"WorkSpaces pool ID"` (with spaces).
|
|
82
|
+
- `get_application_fleet_usage` (Tier 0): time-series **usage history** for a WorkSpaces
|
|
83
|
+
Applications fleet from the `AWS/AppStream` namespace (InUseCapacity, CapacityUtilization,
|
|
84
|
+
Running/Available/Actual/Desired/Pending capacity) — per-bucket points plus latest/average/peak
|
|
85
|
+
and a plain-language summary that flags idle running capacity.
|
|
86
|
+
- WorkSpaces Applications **stacks**: `get_euc_inventory_summary` now counts stacks (alongside
|
|
87
|
+
fleets) and `generate_inventory_report` lists each stack with its associated fleets
|
|
88
|
+
(`ListAssociatedFleets`). IAM policies updated to use `appstream:ListAssociatedFleets` /
|
|
89
|
+
`ListAssociatedStacks` (replacing a non-existent `DescribeFleetAssociations`).
|
|
90
|
+
- Legacy service-name acceptance: the server instructions and application-fleet tool descriptions
|
|
91
|
+
now teach the model that "AppStream" / "AppStream 2.0" means Amazon WorkSpaces Applications (and
|
|
92
|
+
"WorkSpaces Web" means Secure Browser), so queries using former names route correctly while
|
|
93
|
+
output keeps the current name. Added a `LEGACY_NAME_ALIASES` map and guardrail tests.
|
|
94
|
+
- `get_workspace_performance` (Tier 0): native per-desktop CPU/memory/GPU/FPS/disk/latency/uptime
|
|
95
|
+
metrics from the `AWS/WorkSpaces` namespace (latest/average/peak), no CloudWatch agent required.
|
|
96
|
+
- `recommend_bundle_rightsizing` (Tier 0): now implemented on the native CPU/memory metrics —
|
|
97
|
+
suggests smaller/larger compute types from window-peak headroom (general families; graphics
|
|
98
|
+
excluded). This corrects an earlier wrong assumption that these metrics required the CloudWatch
|
|
99
|
+
agent; verified against a live account.
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
- Internal: consolidated the per-module `try_call` / `paginate` / `count_by` helpers onto the
|
|
103
|
+
shared `tools/_common.py` (no behaviour change). Refreshed `DESIGN.md` to reflect shipped state
|
|
104
|
+
and added an "Example admin questions" section to the README.
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
- Phase 3 destructive tools (Tier 3), registered only with both `--enable-writes` and
|
|
108
|
+
`--enable-destructive`: `terminate_workspaces`, `rebuild_workspaces`, and `restore_workspace`.
|
|
109
|
+
On top of dry-run + blast-radius cap, each execution requires an exact typed acknowledgement
|
|
110
|
+
phrase (`"TERMINATE"` / `"REBUILD"` / `"RESTORE"`). Added the Tier 3 IAM policy
|
|
111
|
+
(`iam/tier3-destructive.json`) and an optional `acknowledgement_required` field on `WriteOutcome`.
|
|
112
|
+
- Phase 2 guarded lifecycle tools for WorkSpaces Pools and Applications (writes, Tier 2):
|
|
113
|
+
`start_workspaces_pool`, `stop_workspaces_pool`, `update_workspaces_pool_capacity`,
|
|
114
|
+
`start_application_fleet`, `stop_application_fleet`, and `update_application_fleet_capacity`.
|
|
115
|
+
Same safety model (dry-run default, opt-in registration); Tier 2 policy extended with the Pools
|
|
116
|
+
and AppStream start/stop/update actions.
|
|
117
|
+
- Phase 2 guarded lifecycle tools (writes, Tier 2), registered only with `--enable-writes`:
|
|
118
|
+
`start_workspaces`, `stop_workspaces`, `reboot_workspaces`, and `modify_workspace_running_mode`.
|
|
119
|
+
Mutations are dry-run unless `confirm=true`, and confirmed bulk actions are refused above
|
|
120
|
+
`--max-bulk-targets`. Added the Tier 2 IAM policy (`iam/tier2-lifecycle.json`) and
|
|
121
|
+
`WriteOutcome`/`TargetResult` models.
|
|
122
|
+
- Phase 1 reporting & audit tools (read-only, Tier 0): `generate_inventory_report`,
|
|
123
|
+
`audit_security_posture` (volume encryption + directory IP access control groups), and
|
|
124
|
+
`list_unused_resources`. Added a shared `tools/_common.py` (best-effort call + pagination
|
|
125
|
+
helpers) and inventory/audit/unused-resource models; `Finding` gained an optional `resource_id`.
|
|
126
|
+
- Phase 1 cost & utilization tools (read-only, Tier 1): `analyze_workspace_utilization`,
|
|
127
|
+
`recommend_running_mode`, and `get_euc_cost_summary`, plus the Tier 1 IAM policy
|
|
128
|
+
(`iam/tier1-cost.json`) adding Cost Explorer and Pricing access.
|
|
129
|
+
- Utilization/recommendation/cost models (`WorkspaceUtilization`, `UtilizationReport`,
|
|
130
|
+
`Recommendation`, `RecommendationReport`, `CostSummary`).
|
|
131
|
+
- Phase 1 diagnostics tools (read-only, Tier 0): `diagnose_workspace_connectivity`,
|
|
132
|
+
`diagnose_application_fleet`, and `check_directory_health` — each correlates resource state,
|
|
133
|
+
directory health, CloudWatch telemetry, and auto-scaling activity into a severity-ranked
|
|
134
|
+
diagnosis with recommendations.
|
|
135
|
+
- Generic `ServiceError`, `Finding`, `Diagnosis`, and `DirectoryHealthReport` models.
|
|
136
|
+
- Phase 0 scaffold: FastMCP server, region/profile-aware boto3 client factory, read-only safety
|
|
137
|
+
defaults, and the `get_euc_inventory_summary` tool spanning WorkSpaces Personal, Pools,
|
|
138
|
+
Applications, and Secure Browser.
|
|
139
|
+
- Tier 0 (read-only) IAM policy and per-tier IAM documentation.
|
|
140
|
+
- Test suite (fake-client based) and tooling config (ruff, pyright, bandit, pre-commit).
|
|
141
|
+
- `DESIGN.md` with the full tool inventory and phased roadmap.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Amazon WorkSpaces EUC Admin MCP Server — Design & Build Plan
|
|
2
|
+
|
|
3
|
+
> Status: **Phases 0–3 shipped** (2026-05-31). An MCP server giving administrators AI-assisted
|
|
4
|
+
> inventory, troubleshooting, cost/utilization optimization, and guarded lifecycle management
|
|
5
|
+
> across the Amazon WorkSpaces family of End User Computing services.
|
|
6
|
+
>
|
|
7
|
+
> **Shipped:** 10 read-only tools (Tier 0/1) + 10 guarded write tools (Tier 2) + 3 destructive
|
|
8
|
+
> tools (Tier 3), all behind opt-in flags with dry-run/confirm/blast-radius/typed-acknowledgement
|
|
9
|
+
> guards. All four IAM policy tiers ship in `iam/`. CI (ruff/format/bandit/pytest on Py 3.11–3.13)
|
|
10
|
+
> is green. See `README.md` for the live tool catalog and `CHANGELOG.md` for per-phase detail.
|
|
11
|
+
> **Still deferred:** `recommend_bundle_rightsizing` (needs the WorkSpaces CloudWatch agent for
|
|
12
|
+
> CPU/memory) — see §10.
|
|
13
|
+
|
|
14
|
+
## 1. Principles
|
|
15
|
+
|
|
16
|
+
1. **Admin persona only.** Operators managing fleets — not end users.
|
|
17
|
+
2. **Domain intelligence over API mirroring.** Generic servers (AWS MCP Server, Cloud Control,
|
|
18
|
+
AWS API) already call EUC APIs raw. Our value is cross-service diagnosis, fleet-level
|
|
19
|
+
optimization, and EUC-aware guardrails — not 1:1 API wrappers.
|
|
20
|
+
3. **Security-first, least privilege.** Read-only by default. Writes are opt-in, separately
|
|
21
|
+
permissioned, dry-run-able, confirmation-gated, and blast-radius-limited.
|
|
22
|
+
4. **No embedded tenant data — ever.** This is redistributable software run by many parties.
|
|
23
|
+
Credentials, account IDs, ARNs, profile names, regions, and any other consumer-specific data
|
|
24
|
+
are supplied **only at runtime** (AWS credential chain, CLI flags, env vars) and are **never**
|
|
25
|
+
hardcoded, persisted to disk, or committed. The codebase ships with zero account-specific data.
|
|
26
|
+
5. **Official AWS naming everywhere a human reads.** Legacy identifiers only where the SDK/API
|
|
27
|
+
literally requires them (and labelled as such).
|
|
28
|
+
6. **Build on awslabs conventions** so we match patterns customers already trust.
|
|
29
|
+
|
|
30
|
+
## 2. Services in scope (official naming → API identifier)
|
|
31
|
+
|
|
32
|
+
| Product name | Underlying API (boto3 client) | Notes |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Amazon WorkSpaces Personal | `workspaces` | Persistent desktops |
|
|
35
|
+
| Amazon WorkSpaces Pools | `workspaces` (Pools operations) | Non-persistent pooled |
|
|
36
|
+
| Amazon WorkSpaces Applications | `appstream` | App streaming (formerly AppStream 2.0) |
|
|
37
|
+
| Amazon WorkSpaces Secure Browser | `workspaces-web` | Managed browser (formerly WorkSpaces Web) |
|
|
38
|
+
| Amazon WorkSpaces Core | `workspaces` | Partner/BYO-VDI integration |
|
|
39
|
+
|
|
40
|
+
**Excluded:** Amazon WorkSpaces Thin Client.
|
|
41
|
+
|
|
42
|
+
## 3. Architecture & stack (per awslabs DESIGN_GUIDELINES)
|
|
43
|
+
|
|
44
|
+
- **Language/framework:** Python 3.11+, FastMCP, Pydantic models, boto3, async/await.
|
|
45
|
+
- **Auth:** standard boto3 credential chain via `AWS_PROFILE` / `AWS_REGION`; no secrets stored
|
|
46
|
+
in the server. Assumed-role / Identity Center friendly. **Single-account v1**, with a client
|
|
47
|
+
factory designed to add cross-account `sts:AssumeRole` later without touching tool code.
|
|
48
|
+
- **Transport:** local `uvx`/stdio first; tool logic kept transport-agnostic so a remote
|
|
49
|
+
(AgentCore Runtime) deployment can be added later.
|
|
50
|
+
- **Distribution:** `uvx awslabs.workspaces-euc-mcp-server@latest` and a Docker image.
|
|
51
|
+
- **Observability:** Loguru with env-controlled log level; structured tool errors via `ctx.error`.
|
|
52
|
+
- **Repo layout:**
|
|
53
|
+
```
|
|
54
|
+
awslabs/workspaces_euc_mcp_server/
|
|
55
|
+
__init__.py # version
|
|
56
|
+
server.py # FastMCP app + tool registration
|
|
57
|
+
consts.py # service/API constants, region maps
|
|
58
|
+
models.py # Pydantic request/response models
|
|
59
|
+
clients.py # boto3 client factory (region/profile aware)
|
|
60
|
+
tools/
|
|
61
|
+
inventory.py
|
|
62
|
+
diagnostics.py
|
|
63
|
+
cost.py
|
|
64
|
+
reporting.py
|
|
65
|
+
lifecycle.py # Phase 2 (guarded writes)
|
|
66
|
+
iam/ # shippable least-privilege policy docs per tier
|
|
67
|
+
tests/ # pytest + pytest-asyncio + moto
|
|
68
|
+
pyproject.toml, .pre-commit-config.yaml, README.md, CHANGELOG.md, LICENSE
|
|
69
|
+
```
|
|
70
|
+
- **Quality gates:** ruff (format/lint), pyright (types), bandit (security), moto (AWS mocks),
|
|
71
|
+
pre-commit, Apache-2.0 headers.
|
|
72
|
+
|
|
73
|
+
## 4. Configuration / safety flags
|
|
74
|
+
|
|
75
|
+
- `--readonly` (default **on**): only Describe/Get/List tools are registered.
|
|
76
|
+
- `--enable-writes`: registers Phase-2 lifecycle tools (still dry-run/confirm gated).
|
|
77
|
+
- `--enable-destructive`: separately gates terminate/rebuild/restore.
|
|
78
|
+
- `--max-bulk-targets N`: blast-radius cap for any bulk mutation.
|
|
79
|
+
- `AWS_REGION` / `AWS_PROFILE`: standard.
|
|
80
|
+
|
|
81
|
+
## 5. Tool inventory
|
|
82
|
+
|
|
83
|
+
Each tool lists the IAM actions it needs. Tools are *workflows* that compose several API calls
|
|
84
|
+
and return a synthesized result, not raw API passthroughs.
|
|
85
|
+
|
|
86
|
+
### Phase 1 — Read / Diagnose / Optimize (read-only, Tiers 0–1)
|
|
87
|
+
|
|
88
|
+
**Inventory & discovery**
|
|
89
|
+
| Tool | Purpose | IAM actions |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `list_workspaces_personal` | Personal desktops + live connection status | `workspaces:DescribeWorkspaces`, `workspaces:DescribeWorkspacesConnectionStatus`, `workspaces:DescribeWorkspaceDirectories` |
|
|
92
|
+
| `list_workspaces_pools` | Pools + active sessions | `workspaces:DescribeWorkspacesPools`, `workspaces:DescribeWorkspacesPoolSessions` |
|
|
93
|
+
| `list_application_fleets` | WorkSpaces Applications fleets/stacks/associations | `appstream:DescribeFleets`, `appstream:DescribeStacks`, `appstream:DescribeFleetAssociations` |
|
|
94
|
+
| `list_secure_browser_portals` | Secure Browser portals + settings | `workspaces-web:ListPortals`, `workspaces-web:GetPortal`, `workspaces-web:List*Settings` |
|
|
95
|
+
| `get_euc_inventory_summary` | Cross-service rollup (counts, states, regions) | union of the above describes |
|
|
96
|
+
|
|
97
|
+
**Troubleshooting & triage** (the flagship value)
|
|
98
|
+
| Tool | Purpose | IAM actions |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `diagnose_workspace_connectivity` | Correlate a Personal WorkSpace's state + connection status + directory health + CloudWatch into a root-cause narrative | `workspaces:Describe*`, `ds:DescribeDirectories`, `cloudwatch:GetMetricData` |
|
|
101
|
+
| `diagnose_pool_session` | Why a Pools session failed/queued — capacity, errors, scaling | `workspaces:DescribeWorkspacesPool*`, `cloudwatch:GetMetricData` |
|
|
102
|
+
| `diagnose_application_fleet` | Fleet state, capacity, scaling activity, fleet errors | `appstream:DescribeFleets`, `appstream:DescribeFleetAssociations`, `cloudwatch:GetMetricData`, `application-autoscaling:DescribeScalingActivities` |
|
|
103
|
+
| `check_directory_health` | Shared dependency: directory reachability/registration | `ds:DescribeDirectories`, `workspaces:DescribeWorkspaceDirectories` |
|
|
104
|
+
|
|
105
|
+
**Cost & utilization optimization**
|
|
106
|
+
| Tool | Purpose | IAM actions |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `analyze_workspace_utilization` | Find idle/unused Personal WorkSpaces from connection metrics | `workspaces:DescribeWorkspaces*`, `cloudwatch:GetMetricData` |
|
|
109
|
+
| `recommend_running_mode` | AlwaysOn → AutoStop candidates with $ estimate | `workspaces:DescribeWorkspaces`, `cloudwatch:GetMetricData`, `pricing:GetProducts` |
|
|
110
|
+
| `recommend_bundle_rightsizing` | Over/under-sized bundles from CPU/mem metrics | `workspaces:DescribeWorkspaces`, `workspaces:DescribeWorkspaceBundles`, `cloudwatch:GetMetricData` |
|
|
111
|
+
| `analyze_pool_capacity` | Pools over/under-provisioning | `workspaces:DescribeWorkspacesPool*`, `cloudwatch:GetMetricData` |
|
|
112
|
+
| `analyze_fleet_capacity` | Applications fleet capacity vs demand | `appstream:DescribeFleets`, `cloudwatch:GetMetricData` |
|
|
113
|
+
| `get_euc_cost_summary` | Spend rollup filtered to EUC services | `ce:GetCostAndUsage`, `ce:GetDimensionValues` |
|
|
114
|
+
|
|
115
|
+
**Reporting & audit**
|
|
116
|
+
| Tool | Purpose | IAM actions |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `generate_inventory_report` | Structured inventory across all in-scope services | Phase-1 describes |
|
|
119
|
+
| `audit_security_posture` | Encryption at rest, IP access control groups, directory config, portal policies | `workspaces:Describe*`, `workspaces-web:Get*/List*`, `appstream:Describe*` |
|
|
120
|
+
| `list_unused_resources` | Idle desktops / empty fleets / orphaned associations | describes + `cloudwatch:GetMetricData` |
|
|
121
|
+
|
|
122
|
+
### Phase 2 — Guarded lifecycle (writes; Tier 2, `--enable-writes`)
|
|
123
|
+
All support `dry_run`, return a plan + blast-radius before acting, and honor `--max-bulk-targets`.
|
|
124
|
+
|
|
125
|
+
| Tool | Purpose | IAM actions |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `start_workspaces` / `stop_workspaces` / `reboot_workspaces` | Power ops | `workspaces:Start/Stop/RebootWorkspaces` |
|
|
128
|
+
| `modify_workspace_running_mode` | Apply AutoStop/AlwaysOn recommendation | `workspaces:ModifyWorkspaceProperties` |
|
|
129
|
+
| `modify_workspace_compute_type` | Apply right-sizing recommendation | `workspaces:ModifyWorkspaceProperties` |
|
|
130
|
+
| `update_pool_capacity` | Resize a Pool | `workspaces:UpdateWorkspacesPool` |
|
|
131
|
+
| `start_application_fleet` / `stop_application_fleet` / `update_fleet_capacity` | Applications fleet ops | `appstream:Start/Stop/UpdateFleet` |
|
|
132
|
+
|
|
133
|
+
### Phase 3 — Destructive (Tier 3, `--enable-destructive`, hardest gating)
|
|
134
|
+
| Tool | Purpose | IAM actions |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `rebuild_workspaces` / `restore_workspace` | Recover a desktop (data impact) | `workspaces:Rebuild/RestoreWorkspace` |
|
|
137
|
+
| `terminate_workspaces` | Decommission (irreversible) | `workspaces:TerminateWorkspaces` |
|
|
138
|
+
|
|
139
|
+
## 6. IAM policy tiers (shipped with the server)
|
|
140
|
+
|
|
141
|
+
We ship a managed policy document per tier so customers grant exactly what they enable.
|
|
142
|
+
|
|
143
|
+
- **Tier 0 — Diagnostics (read-only):** `workspaces:Describe*`, `appstream:Describe*`,
|
|
144
|
+
`workspaces-web:Get*`/`List*`, `ds:DescribeDirectories`, `cloudwatch:GetMetricData`,
|
|
145
|
+
`application-autoscaling:DescribeScalingActivities`.
|
|
146
|
+
- **Tier 1 — Cost/optimization (read-only):** Tier 0 + `ce:GetCostAndUsage`,
|
|
147
|
+
`ce:GetDimensionValues`, `pricing:GetProducts`.
|
|
148
|
+
- **Tier 2 — Lifecycle (writes):** Tier 1 + `workspaces:Start/Stop/Reboot/ModifyWorkspace*`,
|
|
149
|
+
`workspaces:UpdateWorkspacesPool`, `appstream:Start/Stop/UpdateFleet`.
|
|
150
|
+
- **Tier 3 — Destructive:** Tier 2 + `workspaces:Rebuild/Restore/TerminateWorkspaces`.
|
|
151
|
+
|
|
152
|
+
Each tier is additive; default install = Tier 0. Recommend scoping by resource tag / directory
|
|
153
|
+
where the API supports it, and using **IAM context keys to separate the agent identity from the
|
|
154
|
+
human operator** (the pattern the AWS MCP Server uses).
|
|
155
|
+
|
|
156
|
+
## 7. Security model
|
|
157
|
+
|
|
158
|
+
- Read-only default; writes require an explicit launch flag *and* the matching IAM tier.
|
|
159
|
+
- Every mutation: `dry_run` plan → explicit confirmation → blast-radius cap.
|
|
160
|
+
- **No credentials or tenant data in the server.** Credentials are resolved solely from the
|
|
161
|
+
standard AWS chain (`AWS_PROFILE` / `AWS_REGION` / SSO / assumed role) at runtime. The server
|
|
162
|
+
stores nothing to disk — boto3 clients are cached in memory only; there is no config/state file,
|
|
163
|
+
no credential cache, no account ID, ARN, or profile name baked into source.
|
|
164
|
+
- **Redistributable-safe repo:** nothing account-specific is ever committed. `.gitignore` blocks
|
|
165
|
+
`.aws/`, `.env`, `*.pem`; pre-commit runs `detect-private-key`; CI greps for hardcoded
|
|
166
|
+
account IDs/ARNs/secrets. Examples in docs use placeholders only.
|
|
167
|
+
- Full auditability via CloudTrail (every underlying API call is attributable).
|
|
168
|
+
- Optional `audit_security_posture` self-check against EUC best practices.
|
|
169
|
+
- Input validation with Pydantic; bandit + AST checks in CI.
|
|
170
|
+
|
|
171
|
+
## 8. Phased roadmap
|
|
172
|
+
|
|
173
|
+
- **Phase 0 — Scaffold:** repo per awslabs layout, client factory, auth, `--readonly` flag,
|
|
174
|
+
one end-to-end tool (`get_euc_inventory_summary`), tests with moto, CI/pre-commit. Ship to
|
|
175
|
+
internal users.
|
|
176
|
+
- **Phase 1 — Read/Diagnose/Optimize:** full inventory + diagnostics + cost tools (Tiers 0–1).
|
|
177
|
+
This is the demonstrable-value milestone.
|
|
178
|
+
- **Phase 2 — Guarded lifecycle:** writes behind `--enable-writes`, dry-run + confirm + caps.
|
|
179
|
+
- **Phase 3 — Destructive ops:** terminate/rebuild/restore behind `--enable-destructive`.
|
|
180
|
+
- **Phase 4 — Polish:** packaging (uvx/Docker), docs, optional remote/AgentCore deployment.
|
|
181
|
+
|
|
182
|
+
## 9. Decisions
|
|
183
|
+
|
|
184
|
+
1. **Distribution:** standalone **public repo on the author's personal GitHub**, consumable and
|
|
185
|
+
self-deployable by others, with full documentation (README, install, IAM setup, examples).
|
|
186
|
+
Follows awslabs conventions so it feels familiar, but lives independently (not in `awslabs/mcp`).
|
|
187
|
+
2. **Account model:** **single-account first** (standard boto3 credential chain). Architect the
|
|
188
|
+
client factory so cross-account role assumption (MSP/multi-account) can be added later without
|
|
189
|
+
refactoring tools.
|
|
190
|
+
3. **Deployment:** **local `uvx` first**, but design transport/config so the same server can be
|
|
191
|
+
deployed remotely (e.g. Bedrock AgentCore Runtime) later — keep tool logic transport-agnostic.
|
|
192
|
+
|
|
193
|
+
## 10. Still open (defer)
|
|
194
|
+
|
|
195
|
+
- **CORRECTED & SHIPPED:** `recommend_bundle_rightsizing` was previously deferred on the
|
|
196
|
+
(incorrect) assumption that `AWS/WorkSpaces` doesn't publish CPU/memory. It does — the namespace
|
|
197
|
+
natively emits `CPUUsage`, `MemoryUsage`, `GPUUsage`, `FramesPerSecond`, disk usage, latency,
|
|
198
|
+
uptime, etc., keyed by `WorkspaceId`, with **no CloudWatch agent required** (verified against a
|
|
199
|
+
live account). `get_workspace_performance` and `recommend_bundle_rightsizing` now ship on these
|
|
200
|
+
native metrics (Tier 0).
|
|
201
|
+
- Cost tools: Cost Explorer (`ce`) only for v1, or add CUR for finer granularity later?
|
|
202
|
+
- Resource-tag / directory-scoped IAM conditions — which services support them well enough to
|
|
203
|
+
recommend by default.
|