Scott Schreckengaust
90d · built 2026-09-10
Performance
What Scott Schreckengaust shipped in the selected window, measured in ETV, and how it compares with the 90 days before it.
Effective capacity
−0.1engineers
delivers like 0.9 (0.9x pre-AI)
Output (ETV)
6.7ETV
+207.9% vs 2.2 prior
Features share
9.6%
+9.6 pp vs prior window
Fixes share
0.5%
+0.5 pp vs prior window
Work mix
9.6% Features46% Maintenance36.5% Tests7.4% Docs0.5% Fixes
16 commits over 90 days, ending 2026-09-10.
Daily performance
Daily ETV, stacked by Features, Maintenance, Tests, Docs and Fixes.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 10 by ETV in the last 90 days.
- 3.1ETVfeat: migrate the server fleet to mcp SDK v2 and remove the <2.0.0 cap (43 servers) (#4452) * fix(lambda-tool-mcp-server): migrate to mcp SDK v2 and remove the <2.0.0 cap Ports server.py + 3 test files from the removed mcp.server.fastmcp module to mcp.server.mcpserver (FastMCP -> MCPServer), raises the cap to >=2.0.0,<3.0.0, and relocks onto mcp 2.0.0. Constructor kwargs, the tool decorator and run() are unchanged in v2, so stdio behavior is identical. Reverses the #4360 stopgap for this server only; the other 47 remain capped. Verified: import succeeds under v2 with instructions/dependencies preserved, 56 tests pass at 99% coverage, and a live stdio ClientSession completes initialize() and list_tools(). Refs #4448, #4354 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(scripts): add migrate-mcp-v2.py codemod for the SDK v2 rollout Automates the mechanical part of migrating the remaining 47 capped servers. Dry-run by default; --apply writes. Rewrites all seven v1 import forms found in src/, and renames FastMCP -> MCPServer only where it came from mcp.server.fastmcp, so the servers using the standalone fastmcp package (a different project that also exports FastMCP) are left alone. String monkeypatch targets get both halves renamed, since fixing only the module yields mcp.server.mcpserver.FastMCP, which does not exist and fails at patch time rather than import time. Dependency bumps preserve declared extras -- one server declares bare mcp, not mcp[cli]. Does not touch uv.lock (generated -- run uv lock per server) and does not move stateless_http= from the constructor to run(); those files are reported instead. Validated two ways: replaying it over pristine v1 lambda-tool-mcp-server reproduces the hand-migrated port byte-for-byte on all 4 files, inheriting that port's test evidence, and applying it to a fresh iam-mcp-server copy yields 174 passing tests under real mcp 2.0.0. Re-running is a no-op. Refs #4448 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: migrate the server fleet to mcp SDK v2 and remove the <2.0.0 cap Extends the lambda-tool-mcp-server port (9848a3c) to the whole fleet: 45 servers now declare `mcp>=2.0.0,<3.0.0`, reversing the temporary cap from #4360. SDK 2.0.0 removed `mcp.server.fastmcp` outright, renamed `FastMCP` -> `MCPServer` and `McpError` -> `MCPError`, flattened the `MCPError` constructor, and attached a camelCase alias generator to every `mcp.types` model. The alias asymmetry is what made this large: construction through the old spelling still works, but *reading* it raises AttributeError, so the breakage surfaces as ~964 test failures across 9 servers rather than as import errors. Most of the diff is mechanical, applied with scripts/migrate-mcp-v2.py. Four changes needed hands: - well-architected-security-mcp-server: v2 dropped `port` from the `Settings` model in favour of a `run()` keyword, so `mcp.settings.port = p` became `mcp.run(transport=..., port=p)`. Pydantic intercepts the old assignment, so the symptom was a ValueError, not an AttributeError. - amazon-qindex-mcp-server: `MCPServer.get_context()` is gone; the tests build `Context(mcp_server=mcp)` directly, which is what the helper did outside a live request. - aws-transform-mcp-server: a test asserting the PROFILE_SELECTION_REQUIRED fallback had been passing by accident -- under v1, `elicit_with_validation` raised ValueError on a bare AsyncMock and `except Exception` swallowed it. v2 correctly returns CancelledElicitation, exposing that the test never declared the non-elicitation client its assertion requires. Fixed the setup, not the assertion. - aws-api, billing-cost-management and dynamodb are deliberately left on v1: fastmcp 3.x pins mcp>=1.24.0,<2.0 through fastmcp-slim, so mcp>=2.0.0 is unsatisfiable for them until fastmcp 4.0 ships. The codemod refuses to touch them so they cannot end up half-migrated and broken against both SDKs. Also adds scripts/verify-mcp-v2-locks.py. `uv run` resolves from uv.lock, so a lock predating a cap change silently tests the *old* SDK -- a green suite that proves nothing. The script cross-checks every lock against its manifest. Every suite was classified against unmodified origin/main before being called a regression: 45 servers pass at their exact baseline counts, 9 have failures that reproduce identically on origin/main, 3 are blocked upstream, 0 unexplained. Refs #4448 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: clear the pyright wall the v2 decorator typing exposed SDK v1 typed `tool()` as `Callable[[AnyFunction], AnyFunction]` where `AnyFunction = Callable[..., Any]`, erasing every decorated tool's signature to `(...) -> Any`. v2 uses a TypeVar and preserves it. No call site changed --- v2 merely stopped hiding 371 pre-existing type errors, and CI surfaced all of them at once. Every one was a real defect: - 21 mock context classes across 8 servers were duck-typed and never structurally matched `Context`. They now subclass it. `Context` is a pydantic BaseModel, so stubs that accumulated state via `self.errors = []` would raise `ValueError: object has no field`; those became declared fields, which also gives each instance its own list. Each subclass needs an explicit no-arg `__init__`, because pyright otherwise re-synthesizes a pydantic `__init__` from `Context`'s private attrs and demands `_request_context`, `_mcp_server`, `_input_params` and `_subscriptions`. - `aws-location`'s integration `main()` passed the pytest fixture *function* as `ctx` to nine tools. Under v1 that type-checked; it was only ever "fine" because each tool hit an AWS error path before touching `ctx`. - 4 `iam` tests asserted nothing: `pytest.raises(Exception)` was catching `TypeError: missing 1 required positional argument: 'ctx'`, not the AWS error in the docstring. They now pass a context and assert the specific error type and message. `create_user` also needed `Context.initialize` --- without it the call short-circuits on the readonly guard and never reaches the mock. - `aurora-dsql` passed tuples where `List[str]` was required, and `transact((sql), ctx)` which is just `sql` --- parens without a comma are not a 1-tuple. - `oracle` subscripted a `str | dict` return with a string key; narrowed with `isinstance`. - `aws-documentation` read an `Optional[Dict]` without narrowing, and imported `Context` from the private `mcp.server.mcpserver.server` submodule. - The remaining diagnostics are tests deliberately passing invalid input to exercise validation; those get targeted `# pyright: ignore[reportArgumentType]` at the call site, matching the convention already used in `ccapi`'s own tests. Also extends the codemod to rewrite keyword construction (`isError=` -> `is_error=`), gated on the server actually being on v2. This is a type-checking fix, not a correctness one: the camelCase alias resolves at runtime and serializes identically, but pydantic's stubs synthesize `__init__` from field *names*. The gate matters --- in v1 `Tool.model_fields` literally contains `inputSchema` as the real field name, so `healthlake-mcp-server` (still on `mcp>=1.23.0`, constructing `Tool(inputSchema=...)` 11 times) is left alone. `BLOCKED_SERVERS` grows 3 -> 6. Beyond the three servers whose `fastmcp>=3` floor makes `mcp>=2.0.0` outright unsatisfiable, three more have a floor that *predates* the `fastmcp-slim` pin --- so the resolver does not fail, it walks backward to fastmcp 2.14.1, which carries 4 known advisories including one CRITICAL. A silent downgrade produces a green suite and a security regression at the same time. `amazon-keyspaces`, `amazon-translate` and `aws-iot-sitewise` are reverted to v1 with a comment explaining why. All 43 v2 servers are now at 0 pyright errors. `pyright` runs at pre-commit `stages: [pre-push]`, which is why `pre-commit run --files` never caught any of this; `scripts/README.md` now says so, along with the silent-downgrade lesson. Not included: `aws-dataprocessing-mcp-server`'s 354 alias-spelled keyword arguments. That server sets `reportCallIssue = false`, so they are not part of CI's 371, and they work at runtime. Renaming them would add 354 no-op changes to an already-large PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(mcp-v2): re-lock cryptography and correct a displaced pyright pragma Two CI failures on the v2 fleet migration. **`dependency-review`** flagged `cryptography` 45.0.7/46.0.0 as *added* to `src/lambda-tool-mcp-server/uv.lock` (7 advisories, 4 high). It was a genuine regression, not lock churn: `origin/main` resolves a single `cryptography 50.0.0`, and this branch reverted to the pre-#4433 pair. Nothing constrains the version -- `mcp` pulls `pyjwt[crypto]`, which requires `cryptography` unbounded -- so this is resolution vintage, not a new bound. The migration commit branched from a tree predating #4433 ("bump the uv-security-updates group"), which had raised it to 50.0.0. Re-locking `mcp` to 2.x recomputed the whole graph, and because nothing pinned it upward the resolver had no reason to keep the security bump; it re-derived the same marker-forked pair the group bump had collapsed. `uv lock --upgrade-package cryptography` restores a single 50.0.0. The lock diff touches no other package, and `mcp` stays at 2.0.0. Auditing all 43 changed locks against `origin/main` for versions that moved *backward* found this as the only one. This is the second silent downgrade this migration has surfaced (after the `fastmcp` 3.4.3 -> 2.14.1 walk on the blocked servers). A re-lock is a whole-graph recomputation: unpinned transitive dependencies can quietly lose security bumps landed by an earlier commit. Diff the lock; a green suite proves nothing about it. **`Build amazon-qindex-mcp-server`** failed at "Run pyright" because a `# pyright: ignore[reportArgumentType]` sat on the wrong argument -- ruff-format reflowed the call into one-arg-per-line *after* the pragma was added, landing it on `qbuiness_region` instead of the `application_id=None` it was suppressing. Inline pragmas are position-sensitive, so add them after formatting, not before. Placements re-verified across all three files that use them. `uv run --frozen pyright` reports 0 errors for all 43 v2 servers; lambda-tool's 56 tests pass against the re-locked graph. The 236 `invalid-license-changes` entries are not a failure and are not fixable here: `httpx2`, `httpcore2`, `mcp-types` and `opentelemetry-api` declare PEP 639 `License-Expression`, which GitHub's dependency graph does not yet read, so it reports `license: null`. All four resolve from PyPI with hashes and ship a license file -- `httpx2`/`httpcore2` are pydantic's BSD-3-Clause httpx fork and `mcp-types` is the official MIT SDK split-out, both pulled in by `mcp` 2.0.0 itself. `denied-changes` is empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(scripts): record the re-lock downgrade and pragma-placement traps Two failure modes the fleet migration hit that the guide did not warn about. `uv lock` is not incremental, so an unpinned transitive dependency can lose a version bump an earlier commit landed -- `cryptography` reverted from 50.0.0 to 45.0.7/46.0.0 purely because re-locking `mcp` to 2.x recomputed the graph with nothing recording why it had been raised. Documents the structural tell (a package splitting into marker-forked entries where the old lock had one) and the fix, and makes the point that tests cannot detect this. Also notes that inline `# pyright: ignore` pragmas bind to the line, so they must be added after `ruff format` -- a later reflow to one-argument-per-line strands the pragma on the wrong argument and silences nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(scripts): correct the cryptography root cause; it was a stale lock My previous commit (083549c1) blamed `uv lock` for discarding the cryptography 50.0.0 security bump when the `mcp` bound moved to v2. That is wrong, and the wrong lesson: it would have people distrusting re-locks. Tested directly. `uv lock` is conservative -- it keeps what the input lock pins and moves only what the changed constraint forces. Bumping the `mcp` cap against an up-to-date lock keeps 50.0.0, and locking from scratch also gives 50.0.0. The downgrade only reproduces when the *input* lock is old. The actual cause: the lambda-tool lock was generated 2026-07-20 in the earlier PoC worktree and committed onto a parent (015ec459) that already carried #4433's bump. Corroborating evidence, not a single suspicious version -- the same lock also pinned sse-starlette 3.4.6 while main had 3.4.8, and had dropped certifi entirely. One old generation date, not a resolver decision. A branch merge was also ruled out: the merge discarded nothing from main's side. So the guidance inverts: sync to origin/main *before* re-locking. Re-locking is the fix, not the hazard. Also corrects the marker-fork claim. I had presented it as the tell; it is a weak hint at best, since a fork can be legitimate. The real signal is several packages stale in the same direction. Separately, verified the fastmcp case is genuinely a resolver walk and not a stale-lock artifact -- it reproduces from a current lock (3.4.3 -> 2.14.1) and the advisories are confirmed against GitHub's database, including a CRITICAL SSRF/path traversal patched only in 3.2.0, which requires mcp 1.x. Documents the durable guardrail: raise the fastmcp floor to >=3.2.0 while still on mcp v1, which keeps 3.4.3 today and makes the silent walk structurally impossible. Notes that fastmcp 4.0 lifts the ceiling but has only alpha/beta releases so far. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(redshift): read ToolAnnotations hints by their v2 field names test_tool_annotations asserted on annotations.readOnlyHint, which v2 no longer exposes as an attribute. This is the same construct-vs-read asymmetry this PR documents elsewhere: v2 keeps the camelCase *alias*, so server.py's `readOnlyHint=True` construction still resolves, but the field is `read_only_hint` and attribute reads raise AttributeError. Only these 4 dot-access reads were affected. The other camelCase hits in src/ are dict literals and kwargs (construction), which v2 accepts unchanged via the alias. 197 passed. Verified the assertion is not vacuous by negating it. * fix(redshift): construct ToolAnnotations with v2 field names Fixes the `Build redshift-mcp-server` pyright failure: 4x `No parameter named "readOnlyHint"` at server.py:145-148. Verified by introspection that this is a v1/v2 field-vs-alias flip, not a pre-existing defect on main: mcp 1.29.0 fields=['readOnlyHint', ...] aliases=[None, ...] mcp 2.0.0 fields=['read_only_hint', ...] aliases=['readOnlyHint', ...] So main's camelCase is correct *for v1* and needs no change there; the rename is required only because this PR moves redshift to v2. Same field-vs-alias trap the codemod already gates on for Tool.inputSchema. Note this construction *does* work at runtime under v2 via the alias -- only pyright rejects it, since pydantic types the synthesized __init__ on field names (populate_by_name is off). That is why it surfaced in CI and not in the test run, and it is the counterpart to the previous commit's attribute *reads*, which failed at runtime instead. redshift: 0 pyright errors, 197 passed. * chore(mcp-v2): signal minor version bump on 43 migrated servers; exact-pin s3-tables mcp Addresses review feedback (pullrequestreview-4912250223): - Set version = "X.Y.9223372036854775807" on each of the 43 migrated servers (X.Y unchanged). release.py's patch-overflow rolls this to X.(Y+1).0 at the next release, signaling the minor bump the raised mcp floor (>=2.0.0) warrants — a changed co-installation contract, not a bug fix. The 6 blocked servers are comment-only and untouched. - s3-tables: pin mcp[cli]==2.0.0 (was >=2.0.0,<3.0.0) to match that server's exact-pin policy for every other runtime dependency (loguru, pydantic, boto3, pyiceberg, ...). uv.lock regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(security-agent-mcp-server): migrate clientInfo -> client_info in _ensure_client_ua The main merge brought in _ensure_client_ua(), which read the pre-v2 client_params.clientInfo. Under this server's v2 lock that attribute was renamed to client_info, so the read raised AttributeError, silently swallowed by the function's except into a debug log — user-agent enrichment never ran. The 9 matching test sites set clientInfo on a MagicMock, so auto-vivification hid the regression. Applied the repo codemod (scripts/migrate-mcp-v2.py) to migrate all 10 field reads (1 in server.py, 9 in tests). Suite green: 207 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>github.com-awslabs-mcp · 13b13095 · 2026-08-25
- 1.4ETVchore(deps): update uv: bump mcp to 1.29.0 across all packages (consolidates 57 Dependabot PRs) (#4258) * chore(deps): update uv: bump mcp to 1.28.1 across all packages Consolidates the 57 separate Dependabot PRs that each bumped modelcontextprotocol/python-sdk (mcp) to 1.28.1 in one package directory into a single reviewable/mergeable PR. Every change is disjoint (each package owns its own uv.lock / pyproject.toml), so they combine without conflict: - 54 src/*-mcp-server packages: mcp -> 1.28.1 in uv.lock - 2 samples/* packages: mcp>=1.28.1 in pyproject.toml + uv.lock - 1 cloudwatch-applicationsignals evals/requirements.txt: mcp>=1.28.1 Supersedes the individual Dependabot PRs (listed in the PR body). Retargeted from an earlier 1.27.2 consolidation after mcp 1.28.1 shipped upstream and Dependabot auto-closed the 1.27.2 batch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(deps): retarget mcp bump to 1.29.0 and repair 16 malformed lockfiles Retargets this consolidation from mcp 1.28.1 to 1.29.0 — the newest 1.x release, which is clear of all six published GHSAs for the MCP Python SDK (highest firstPatchedVersion is 1.28.1). Stays below 2.0.0 to avoid the major-version API break across 57 packages. Also fixes the CI failures blocking this PR. Sixteen lockfiles carried a bare `{ name = "typing-extensions", marker = ... }` reference while the same file forked typing-extensions into two versions, which uv cannot disambiguate: error: Failed to parse `uv.lock` Caused by: Dependency `typing-extensions` has missing `source` field but has more than one matching package These were regenerated from main via `uv lock --upgrade-package mcp` so the resolution is internally consistent. The remaining 41 lockfiles get a surgical version/sdist/wheel swap: mcp 1.29.0 declares dependency metadata byte-identical to 1.28.1, so no transitive closure changes. Both samples now pin `mcp>=1.29.0,<2.0.0`; without the upper bound the kb sample resolved to 2.0.0. Verified: `uv lock --check` passes for all 57 changed lockfiles (was 41/57); all 114 artifact references match PyPI hashes; tests pass for iam (174), cloudwatch (478), ecs (722), ccapi (274), aurora-dsql (238), finch (165), location (53), amazon-mq (18). Note: cloudwatch-applicationsignals (1.23.3), dynamodb (1.26.0) and s3-tables (1.23.0) remain on vulnerable versions — they were outside this PR's scope and pin mcp independently. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Abhijeet Patil <prajwalendra@users.noreply.github.com>github.com-awslabs-mcp · fe3e736b · 2026-08-10
- 0.8ETVfix(openapi-mcp-server): DNS-pin spec fetches to prevent rebinding SSRF (#4108) * fix(openapi-mcp-server): DNS-pin spec fetches to prevent rebinding SSRF The multi-spec (additional_specs) path validated spec_url via validate_url_for_spec() and then discarded the result, re-fetching with httpx.get(url) — a fresh DNS resolution. A rebinding server could answer a public IP during validation and 169.254.169.254 (or any internal IP) at fetch time (CWE-350 / CWE-367 TOCTOU SSRF). The primary spec URL was not validated at all. Fetch the spec by connecting only to the IP(s) validation already pinned, never re-resolving the hostname: - utils/openapi.py: add _pinned_fetch() — dials the pinned IP literal with Host + sni_hostname set to the validated hostname, follow_redirects=False with explicit 30x rejection, and a 10 MiB streamed size cap (the old loader read bodies unbounded). load_openapi_spec() gains a validated_url param and validates+pins a bare url in-function via _validate_url_sync(), so no code path fetches a spec un-pinned. SSRFError/SSRFFetchError are not retried away. - server.py: thread the ValidatedURL from validation into load_openapi_spec() for additional specs; the primary spec is validated+pinned by the loader with the --allow-* flags passed through. fastmcp's ssrf_safe_fetch is intentionally not reused: it re-resolves DNS, is HTTPS-only, caps at 5 KB, and is async (would crash the sync loader inside the running event loop). Tests cover rebinding (DNS resolved once; metadata IP never dialed), redirect refusal, mixed public/private rejection, size caps, http opt-in, and the newly-validated primary path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(openapi-mcp-server): document DNS pinning; add coverage for pinned fetch - README: replace the outdated "deploy behind an egress proxy for full DNS pinning" note with a DNS-pinning (rebinding protection) section; note the 10 MiB cap and redirect refusal; clarify --allow-insecure-http is still pinned. - CHANGELOG: add Unreleased Security entry for the DNS-rebinding TOCTOU fix. - tests: cover the new branches — scheme rejection, retry-across-pinned-IPs, all-IPs-fail, no-IPs, the running-loop sync bridge, http opt-in (no SNI), and that SSRF failures are not retried. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(openapi-mcp-server): cover _parse_spec_bytes paths for patch coverage Add unit tests for the prance-success, prance-failure→JSON, and JSON-failure→YAML branches of _parse_spec_bytes so codecov/patch reflects the new fetch/parse helpers (utils/openapi.py 89%→93%). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: pull request finding 'Explicit returns mixed with implicit (fall through) returns' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix(openapi-mcp-server): correct misplaced terminal raise in load_openapi_spec The autofix for the implicit-None fall-through inserted the terminal `raise ValueError` directly after the docstring, making it unconditional and killing every code path (url, path, validated_url) before any real logic ran. Move the terminal raise to the true fall-through point at the end of the function; the all-empty guard and the return/raise in each branch remain intact. Also fix test_path_invalid_validation, which patched validate_openapi_spec in the module where it is defined (openapi_validator) rather than where openapi.py binds it at import time. The mock never took effect, so the test failed independently of this change (fails on main too). Patch the used name to match the passing sibling test_url_invalid_spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>github.com-awslabs-mcp · bc5bf8f8 · 2026-07-12
- 0.5ETVfeat(openapi): migrate openapi-mcp-server to native FastMCP.from_openapi (RFC #4122) (#4124) * feat(openapi-mcp-server): migrate to native FastMCP.from_openapi (POC) Convert the wrapper's internals to the upstream high-level API to prove a user migration path off this package: - Primary server now built via FastMCP.from_openapi(...) instead of hand-constructing OpenAPIProvider + FastMCP(providers=[...]). - Description enrichment delegates to FastMCP's shipped format_description_with_responses instead of bespoke string building. - Additional specs composed via server.add_provider(...). - Enrichment tests updated to assert the native structured format. Adds docs/migration/ with the deprecation evaluation and three upstream contribution drafts. Tracking: awslabs/mcp#4122. 491 passed; 9 known failures are mock-coupled to the old OpenAPIProvider construction (documented as follow-ups in the PR). * docs(openapi-mcp-server): add MIGRATION.md Customer-facing migration guide mirroring the deprecation plan: feature mapping table, in-process + standalone (fastmcp run) recipes, SSRF-safe fetch and Prometheus middleware snippets, and OpenAPI 2.0 conversion note. Tracking: awslabs/mcp#4122. * test(openapi-mcp-server): prune redundant construction-mechanics tests After migrating to FastMCP.from_openapi, 9 tests asserted low-level OpenAPIProvider/FastMCP construction that upstream now owns: - Removed 6 redundant tests (basic/invalid_spec construction assertions, duplicate part1 copies, route-map call_args inspection, with-openapi-provider call-count). Behavioral coverage remains in test_new_features.py. - Repointed 3 tests that assert real wrapper logic (auth-provider registration, httpx-version fallback, prompt generation) to patch FastMCP.from_openapi instead of the old constructor. Full suite: 495 passed, 0 failed. * fix(openapi-mcp-server): preserve server instructions after migration from_openapi forwards instructions via **settings to FastMCP(); pass the wrapper's original instructions string through for behavior parity (it was dropped when moving off the explicit FastMCP(...) construction). Verified the value round-trips onto the server. 495 passed. * fix(openapi): address PR #4124 review — correct inverted Cognito mapping, restore SSRF-flag guard Cognito auth mapping was inverted in all three migration docs. The wrapper's cognito_auth.py is outbound (acquires a Cognito token, sets Authorization on the httpx client that calls the upstream API); FastMCP's AWSCognitoProvider is inbound (MRO ends in TokenVerifier; validates JWTs from MCP clients). A user following the old row would gate their MCP server behind Cognito while API calls go out unauthenticated. Reclassify Cognito as outbound-only glue (boto3 token + header on httpx.AsyncClient) across MIGRATION.md, docs/migration/README.md, and deprecation-evaluation.md (capability table + Appendix E row), and correct the glue-feature count from two to three (Cognito, SSRF-fetch, Prometheus). Restore the one non-mechanic assertion dropped with test_create_mcp_server_basic: a focused test that create_mcp_server forwards allow_insecure_http / allow_private_networks into load_openapi_spec. Verified via mutation — hardcoding the flags open now fails the test. References neither from_openapi nor OpenAPIProvider, so it survives upstream construction refactors. Correct the route-map justification comment in test_server_extended.py: the builder's forwarding into the provider is not asserted on either branch (DEFAULT_ROUTE_MAPPINGS already maps every operation to TOOL); only the builder itself is unit-tested. Hoist format_description_with_responses to a module-level import. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(openapi): address PR #4124 review round 2 — SSRF flag distinguishability, request_body coverage, $ref caveat Three remaining review findings from @theagenticguy and @prajwalendra. Finding 1 (both reviewers) — the SSRF-forwarding test set both flags to False, so `assert_called_once_with` constrained the multiset of values rather than the mapping: cross-wiring the two keyword arguments at server.py:104 still passed. Setting `allow_insecure_http = True` / `allow_private_networks = False` makes them differ, so each flag is pinned to its own parameter. Verified with the reviewer's own mutation — the cross-wire now fails where it previously passed, as does hardcoding both open. Also corrects the comment: False *is* the default for both (api/config.py:79-80), so the old "non-default" claim was wrong; it is now true for allow_insecure_http. Finding 2 — `enrich_component` forwards `route.request_body` to `format_description_with_responses`, but no spec reaching that path had a requestBody, so the branch never ran and dropping the argument was undetectable. Adds a requestBody to `createPet` in PETSTORE_SPEC plus a test asserting the Request Body and nested Request Properties sections. The body carries a `description` deliberately: the upstream formatter gates the entire section on that field being truthy (formatters.py:241), so a body without one enriches identically to no body at all. Verified by mutation — removing the argument fails exactly the new test and nothing else. Finding 3 — documents that `generate_example_from_schema` (formatters.py:100) has no `$ref` branch and falls through to the literal "unknown_type". The wrapper never hit this because it resolves specs through prance's ResolvingParser first, but the raw-dict recipe in the guide leaves $refs intact. Reproduced: an array-of-$ref response documents its example as ["unknown_type"] while the tool's own output_schema resolves the reference correctly, so the description contradicts the schema beside it. MIGRATION.md gains the caveat and a dereference recipe (verified to clear the placeholder); deprecation-evaluation.md Appendix E gains a short cross-reference, since it carries the same recipe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Abhijeet Patil <prajwalendra@users.noreply.github.com> Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-awslabs-mcp · 77eb7df0 · 2026-08-06
- 0.4ETVfix: fastmcp package to 3.2.0 (#4088) * fix: fastmcp package to 3.2.0 * fix: adapt tests to fastmcp 3.x tool API - aws-iac: @mcp.tool() returns the plain function (no .fn wrapper) - document-loader: FastMCP.get_tools() removed, use list_tools() - billing: decorated symbol has no .name, query server.get_tool() * fix: resolve pyright errors in billing-cost-management for fastmcp 3.x - update fastmcp imports to public paths (fastmcp.tools/fastmcp.prompts) since fastmcp.tools.tool / fastmcp.prompts.prompt no longer resolve - fix misplaced type:ignore for dict filters arg in aws_pricing testgithub.com-awslabs-mcp · 0e96fa1d · 2026-07-07
- 0.3ETVfix: add upper bound (<2.0.0) on mcp SDK for servers using mcp.server.fastmcp (#4360) The MCP Python SDK 2.0.0 (a pre-release, 2.0.0rc1) removed the vendored `mcp.server.fastmcp` module. 45 servers import `from mcp.server.fastmcp import ...` but declared `mcp[cli]>=1.23.0` with no upper bound, so `uvx ...@latest` resolves the v2 pre-release and the server crashes at import with `ModuleNotFoundError: No module named 'mcp.server.fastmcp'`. Cap the SDK at `<2.0.0` for every affected server so fresh resolutions stay on a 1.x that still ships the vendored module. This matches the upstream guidance for downstream packages (`mcp>=...,<2`). Each uv.lock is updated in lockstep (specifier line only; no resolved versions move, so `uv lock --check` passes). Servers already safe are untouched: dynamodb (==1.26.0) and s3-tables (==1.23.0) pin exactly; ecs-mcp-server only imports the module in a test and depends on fastmcp>=3.0.0 (which caps mcp<2). Note that fastmcp>=2.14.0 no longer caps mcp transitively, so keyspaces, translate and iot-sitewise are included despite declaring fastmcp. Fixes #4354 Fixes #4356 Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-awslabs-mcp · b1d755e2 · 2026-07-28
- 0.0ETVfix(deps): raise fastmcp floor to >=3.2.0 to exclude a CRITICAL advisory (#4454) Every server depending on standalone `fastmcp` declared a floor low enough to permit versions carrying known advisories, the worst being: GHSA-vv7q-7jx5-f767 CRITICAL SSRF + path traversal in the OpenAPI provider GHSA-rww4-4w9c-7733 high missing consent verification in OAuth proxy GHSA-m8x7-r2rg-vh5g moderate command injection (Gemini CLI) All three are patched in 3.2.0. A fourth, GHSA-5h2m-4q8j-pqpj (high, OAuth token reuse across servers), is patched in 2.14.2. Nothing is exposed *today* -- all 13 servers currently resolve fastmcp 3.4.3 -- so this changes no installed version. It is a guardrail: the floors permitted a resolver walk back into the vulnerable range, and the mcp SDK v2 migration (#4452) demonstrated that happening for real. On `amazon-translate-mcp-server`, raising only the `mcp` cap to v2 walks fastmcp 3.4.3 -> 2.14.1 from a fully current lock, because `fastmcp-slim` pins `mcp>=1.24.0,<2.0` and only older fastmcp accepts mcp 2.x. Tests stay green while 4 advisories are reintroduced. With the floor at 3.2.0 that walk is structurally impossible: the resolution now fails loudly instead of silently downgrading. Note that no fastmcp release is both mcp-v2-compatible and free of these advisories -- fastmcp 4.0 is the real unblock (`fastmcp-slim` 4.0.0b1 requires `mcp>=2.0.0,<3.0.0`) but has only alpha/beta releases so far, so v2 for these servers still waits on a stable 4.0. `openapi-mcp-server` already declared `>=3.3.1,<4` and needed no change. Verified: no resolved package version changes in any of the 12 relocked files, compared as per-package version sets rather than by reading the diff. The small lock diffs are `requires-dist` updates plus marker annotations uv adds when a narrower floor lets it prove a conditional edge is reachable only under a specific interpreter -- strictly more precise metadata for an identical graph. An unmodified re-lock is a byte-for-byte no-op, which rules out ambient uv drift as the source of that churn. 5,125 tests pass across all 12 servers. `roda-mcp-server` declares its dev tools under `[project.optional-dependencies]` rather than `[dependency-groups]`, so it needs `uv run --frozen --extra dev pytest`; that is pre-existing and unrelated. Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>github.com-awslabs-mcp · 048c8441 · 2026-08-07
- 0.0ETVfix(deps): repair uv.lock parse failure from pyjwt 2.13.0 bump (11 servers) (#4536) Dependabot PR #4503 (merged as a7b32633) bumped pyjwt 2.10.1 -> 2.13.0 across 30 directories. pyjwt 2.13.0 introduced a new conditional dependency on typing-extensions for `python_full_version < '3.11'`. Dependabot edited each uv.lock surgically rather than re-resolving, and in 11 of the 30 locks it wrote that new edge with an explicit version pin: { name = "typing-extensions", version = "4.14.0", source = { ... }, marker = ... } In uv.lock, a dependency edge carries `version`/`source` only when the lock holds multiple forked versions of that package. These 11 locks each contain a single typing-extensions entry (4.16.0), so the pinned edge (4.13.2 or 4.14.0) referenced a package that does not exist in the lock, and uv rejected the file at parse time: error: Failed to parse `uv.lock` Caused by: For package `pyjwt==2.13.0 @ registry+https://pypi.org/simple`, found dependency `typing-extensions==4.14.0` with no locked package This broke the `Install dependencies` step (`uv sync --frozen --all-extras --dev`) for all 11 servers. The other 19 locks received a bare edge and were unaffected. Fix: drop the bogus version/source pin so each edge binds to the single locked typing-extensions, which is the canonical form uv itself emits (and exactly what the 19 healthy locks contain). `uv lock` cannot regenerate these files because it fails on the same parse error before it can re-resolve. No resolved versions change - typing-extensions 4.16.0 was already the only locked version. This restores the lock to a parseable state. Verified for all 11 servers on Python 3.10 (the version where the marker is active): `uv sync --frozen --all-extras --dev` succeeds, pyjwt 2.13.0 imports against typing-extensions 4.16.0 and signs a token, and the full pytest suite passes. Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>github.com-awslabs-mcp · 7d93d983 · 2026-08-26
- 0.0ETVfix(docusaurus): align @docusaurus/* to 3.10.2 and fix v4 build regressions (#4441) The docusaurus build was failing on PR #4438 (and would fail on the piecemeal bumps #4322/#4323) because @docusaurus/core/preset/tsconfig/types were left at 3.9.x while module-type-aliases and the security-group lockfile bumps pulled newer webpack. Docusaurus requires all @docusaurus/* packages at the same version, so core@3.9.2 passed webpack ProgressPlugin options the newer schema rejected ("Progress Plugin ... does not match the API schema"). Changes: - Align every @docusaurus/* dependency to ^3.10.2 and regenerate the lockfile. This also moves @babel/plugin-transform-modules-systemjs (7.27.1 -> 7.29.8) and brace-expansion (1.1.12 -> 1.1.18) off their advisories, and lands qs/websocket-driver at the same versions as main (no new dependency-review findings). - future.faster: false — in 3.10 `v4: true` implies fasterByDefault, which needs the @docusaurus/faster (Rspack) package. Its SWC HTML minifier errors on existing i18n markup, so keep the webpack bundler for now. - markdown.mdx1Compat.headingIds: true — `v4: true` disables MDX v1 compat by default in 3.10, breaking `## Heading {#anchor}` syntax used across the ja/ i18n docs (66 files). Re-enable heading-id compat so they parse. Verified locally: npm ci, npm start, npm run build (both en + ja locales, exit 0, 0 errors), npm run serve; pages render 200 with correct titles via a headless browser. Supersedes #4438; relates to #4322, #4323. Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-awslabs-mcp · cb904956 · 2026-08-05
- 0.0ETVchore(deps): throttle Dependabot PR volume while cross-directory grouping is broken (upstream #14286) (#4371) * chore(deps): throttle Dependabot PR volume while cross-directory grouping is broken group-by: dependency-name (added in #4267) is not collapsing version-update PRs across directories — Dependabot still opens one PR per directory per dependency (e.g. gitpython -> #4357/#4358/#4359, setuptools -> 5 PRs, pillow -> 4 PRs). Root cause is an upstream regression, dependabot-core#14286 (the feature went default-on in dependabot-core#14292); it reproduces even with explicitly enumerated directories, so our config is correct-per-docs but inert. Add throttles that don't depend on the broken feature, to all four update entries: - open-pull-requests-limit: 10 (default is 5; caps the open queue) - cooldown: default-days: 7 (let fresh releases settle first) Keep schedule.interval weekly and leave group-by in place so grouping resumes automatically once the upstream fix ships. Tracked in #4370. Validated with npx @bugron/validate-dependabot-yaml (exit 0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(deps): group security updates, drop open-PR-limit bump Address review feedback (PR #4371): - Remove `open-pull-requests-limit: 10`. The default is 5, so this raised the ceiling rather than throttling; revert to the default and lower explicitly later if version-update volume needs a harder cap. - The limit and cooldown apply to VERSION updates only; security updates are exempt from both. The bulk of the open queue is security-driven (~1.9k open alerts), so neither throttle bounded it. - Add an `applies-to: security-updates` group per ecosystem to collapse each directory's security bumps into one PR — independent of the broken cross-directory grouping (dependabot-core#14286, #4370). - Keep `group-by: dependency-name` on the version-update groups so cross-directory grouping resumes when #14286 is fixed. - Keep `cooldown.default-days: 7` (a +4 increment over the built-in 3-day default). Validated with `@bugron/validate-dependabot-yaml` (exit 0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: scottschreckengaust <345885+scottschreckengaust@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-awslabs-mcp · 4f10089f · 2026-08-04