From a19258e8a15beffed1b283ea24ce477ca2e1bcc7 Mon Sep 17 00:00:00 2001 From: Mathew Goldsborough <1759329+mgoldsborough@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:31:35 -1000 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20green=20the=20template=20CI=20?= =?UTF-8?q?=E2=80=94=20await=20ctx.error=20+=20clean=20bundle=20scan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three pre-existing failures on main, all of which propagate to every server scaffolded from this template: - lint (ty): ctx.error() is async in FastMCP but was called without await in get_client/list_items/get_item, so the error notifications were never sent. Made get_client async and awaited all three call sites. - scan AI-05 (4x HIGH): tests-integration/ was packed into the bundle (unlisted in .mcpbignore) and flagged as unexpected executables. Excluded it. - scan CD-02 (2x HIGH): the code reads the EXAMPLE_API_KEY secret env var but the manifest declared no environment permission. Added the MTF permissions block (environment: read, network: outbound). Verified locally: ty + ruff clean, 25 tests pass, and a re-scan drops all 6 HIGH findings (risk HIGH -> MEDIUM), so the critical/high CI gate passes. --- .mcpbignore | 1 + manifest.json | 11 +++++++++++ src/mcp_example/server.py | 12 ++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.mcpbignore b/.mcpbignore index 6adc87d..f4020ba 100644 --- a/.mcpbignore +++ b/.mcpbignore @@ -30,6 +30,7 @@ __pycache__/ /build/ /dist/ /tests/ +/tests-integration/ /test/ /e2e/ /examples/ diff --git a/manifest.json b/manifest.json index afa027a..db250b9 100644 --- a/manifest.json +++ b/manifest.json @@ -25,5 +25,16 @@ "EXAMPLE_API_KEY": "${user_config.api_key}" } } + }, + "_meta": { + "org.mpaktrust": { + "permissions": { + "filesystem": "none", + "network": "outbound", + "environment": "read", + "subprocess": "none", + "native": "none" + } + } } } diff --git a/src/mcp_example/server.py b/src/mcp_example/server.py index f3db9f6..04efa6a 100644 --- a/src/mcp_example/server.py +++ b/src/mcp_example/server.py @@ -47,7 +47,7 @@ _client: ExampleClient | None = None -def get_client(ctx: Context | None = None) -> ExampleClient: +async def get_client(ctx: Context | None = None) -> ExampleClient: """Get or create the API client instance.""" global _client if _client is None: @@ -55,7 +55,7 @@ def get_client(ctx: Context | None = None) -> ExampleClient: if not api_key: msg = "EXAMPLE_API_KEY environment variable is required" if ctx: - ctx.error(msg) + await ctx.error(msg) raise ValueError(msg) _client = ExampleClient(api_key=api_key) return _client @@ -87,12 +87,12 @@ async def list_items( Returns: List of items """ - client = get_client(ctx) + client = await get_client(ctx) try: return await client.list_items(limit=limit) except ExampleAPIError as e: if ctx: - ctx.error(f"API error: {e.message}") + await ctx.error(f"API error: {e.message}") raise @@ -110,12 +110,12 @@ async def get_item( Returns: Item details """ - client = get_client(ctx) + client = await get_client(ctx) try: return await client.get_item(item_id) except ExampleAPIError as e: if ctx: - ctx.error(f"API error: {e.message}") + await ctx.error(f"API error: {e.message}") raise From 6033841a5cc36e6d48c4c3d682104e3ece30def8 Mon Sep 17 00:00:00 2001 From: Mathew Goldsborough <1759329+mgoldsborough@users.noreply.github.com> Date: Thu, 23 Jul 2026 12:36:27 -1000 Subject: [PATCH 2/3] fix(ci): write scan report via -o instead of stdout redirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanner prints 'Scanning ...' to stdout before the JSON, so '--json > scan-results.json' produced a file json.load couldn't parse (Expecting value: line 1 column 1). This was latent — the scanner step used to exit 1 on HIGH findings before the parse step ran. With the HIGH findings cleared, the scanner exits 0 and the parse bug surfaced. Use the scanner's -o flag (its intended machine-readable output) so the file is clean JSON. --- .github/workflows/scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 60808b1..28366b2 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -27,7 +27,7 @@ jobs: run: npx @anthropic-ai/mcpb pack - name: Run MTF scanner - run: uvx mpak-scanner scan *.mcpb --json > scan-results.json + run: uvx mpak-scanner scan *.mcpb --json -o scan-results.json - name: Check for critical/high findings run: | From 10be42f09c1298fe4cc56c3ba07f75760140b0a6 Mon Sep 17 00:00:00 2001 From: Mathew Goldsborough <1759329+mgoldsborough@users.noreply.github.com> Date: Thu, 23 Jul 2026 13:17:08 -1000 Subject: [PATCH 3/3] fix(manifest): declare filesystem: read to match SKILL.md resource read The server reads its bundled SKILL.md via importlib.resources at import time, so the MTF permissions block must declare filesystem access. With filesystem "none" the scanner raises CD-02 (undeclared filesystem permission, MEDIUM); declaring "read" makes the declaration accurate and drops the bundle's scan risk score to LOW. --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index db250b9..b669d29 100644 --- a/manifest.json +++ b/manifest.json @@ -29,7 +29,7 @@ "_meta": { "org.mpaktrust": { "permissions": { - "filesystem": "none", + "filesystem": "read", "network": "outbound", "environment": "read", "subprocess": "none",