Skip to content

Commit ed64a6a

Browse files
authored
test(compliance): MCP 2025-11-25 protocol compliance harness (#4301)
* test(compliance): add MCP 2025-11-25 protocol compliance harness Build a FastMCP-based test harness that probes ContextForge against the MCP 2025-11-25 specification across three targets: reference stdio, gateway proxy (federated), and gateway virtual server. Includes a new reference server (mcp-servers/python/compliance_reference_server) with tools/resources/prompts covering lifecycle, transport, pagination, notifications, subscriptions, roots, sampling, elicitation, and logging. Key additions: - tests/protocol_compliance/: parametrized test suite with XPASS-capture, drift detection across targets, and gap-tracking workflow - scripts/compliance_matrix.py: orchestrator that runs the suite across engine modes (python/shadow/edge/full) and aggregates results - Runtime-mutable MCP mode tests covering shadow<->edge flip, boot-mode rails, publish/audit response contract, and data-plane runtime header - OAuth 2.1 / Authorization tests against Keycloak (tier-gated skip) - Security Best Practices probes (origin validation, bind address, confused-deputy) - Rewrite tests/e2e/test_mcp_cli_protocol.py as test_mcp_protocol_e2e.py using FastMCP Client directly (no external CLI deps) - Makefile: test-protocol-compliance* targets; testing-up folds in sso profile so Keycloak comes up with the gateway stack - pre-commit: scope name-tests-test hook to tests/unit/ (integration, e2e, and compliance suites legitimately host non-test Python modules) Closes #4273 (runtime-mutable MCP mode observability coverage). Signed-off-by: Jonathan Springer <jps@s390x.com> * refactor(tests): group test helpers under tests/*/helpers/ subdirs Move loose non-test Python modules into conventional helpers/ subdirs so the test-naming hook can exclude a single pattern instead of enumerating each file. Moves: - tests/e2e/mcp_test_helpers.py -> tests/e2e/helpers/mcp_test_helpers.py - tests/protocol_compliance/_helpers.py -> tests/protocol_compliance/helpers/compliance.py - tests/protocol_compliance/_drift.py -> tests/protocol_compliance/helpers/drift.py Updates all import sites across tests/e2e, tests/e2e_rust, and tests/protocol_compliance. Config cleanup: - name-tests-test now excludes tests/*/(helpers|fixtures|targets|pages)/ as a single verbose-regex alternative instead of file-by-file. - Top-level pre-commit exclude and detect-secrets excludes converted to verbose-regex with inline comments. - Dropped non-existent excludes (scripts/sign_image.sh, scripts/zap, sonar-project.properties) and the redundant .secrets.baseline entry (detect-secrets skips its own baseline automatically). - detect-secrets hook in pre-commit now carries its own exclude list, kept in sync with the Makefile invocation. Signed-off-by: Jonathan Springer <jps@s390x.com> * fix(keycloak): enable service-accounts on the mcp-gateway dev realm client The local Keycloak dev realm disables the client_credentials grant, so compliance tests that exercise OAuth 2.1 token issuance against the preconfigured `mcp-gateway` client fail with `unauthorized_client: Client not enabled to retrieve service account`. Flip `serviceAccountsEnabled` to `true` so fresh `docker compose` volume imports come up with the grant available. Existing stacks can apply the same change via the Keycloak Admin API against the running realm; the stored volume overrides the import file once the realm exists, which is why the change has to land here to stick across rebuilds. Signed-off-by: Jonathan Springer <jps@s390x.com> * test(compliance): PR-review follow-ups — silent-failure fixes, behavioral coverage, type design, stream attribution Bundles the fixes surfaced by running the pr-review-toolkit agents (code-reviewer, pr-test-analyzer, comment-analyzer, silent-failure-hunter, type-design-analyzer) plus two iterations of Codex stop-review feedback. Silent-failure fixes: - _parse_junit / _run_slice / stale-junit unlink / _wait_data_plane_converges return-value check close the "0/0/0/0 row" silent path in the matrix orchestrator. - Reference server stdout+stderr captured to a session tmp log; tail dumped on readiness timeout instead of swallowed. - flip_runtime_mode / _delete_if_exists / _wait_for_federation_sync / _probe_state / Keycloak token fetch surface last-status diagnostics instead of collapsing to None. - conftest fixture-resolution narrows catch-all so ImportError / NameError / SyntaxError from a broken fixture definition propagate; only runtime unreachability skips. - pytest_runtest_logreport wraps sidecar write in try/except so an unwriteable log doesn't poison the run. Type design: - ComplianceTarget: __init_subclass__ enforces name and supported_transports on every concrete subclass; base client() validates transport support and dispatches to abstract _open_client(); per-subclass boilerplate deleted. - SliceResult: stored `ran` field removed (derive from skip_reason); __post_init__ rejects negative counts and the inconsistent "skipped slice with non-zero counts" state. - ReferenceUpstream: frozen, no raw Popen field — process handle stays in fixture closure so tests can't race teardown. - KeycloakConfig: token_endpoint is now a property; __repr__ redacts client_secret. Stream attribution: - MCP 2025-11-25 § Listening for Messages is explicit: server→client *requests* (roots/list, sampling/createMessage, elicitation/create) MUST NOT ride the standalone GET /mcp/ stream; they travel on the POST-correlated stream. GAP-002..005 and seven xfail reasons wrongly blamed #4205 (standalone-stream closure). Rewrote the Why sections and reasons to point at POST-correlated-stream relay instead. - GAP-011 (resource-subscription updates) is the only gap that legitimately points at #4205. - Added a canonical "Stream-attribution note" to COMPLIANCE_GAPS.md. - Filed GAP-011 (resource subscriptions) and GAP-012 (gateway returns -32000 where spec requires -32601). Behavioral coverage (6 new tests + 4 reference-server witnesses): - notifications/cancelled end-to-end delivery. - logging/setLevel filter-threshold round-trip. - JSON-RPC reserved codes -32700 / -32601 / -32602. - tools/call argument-type validation (narrowed to McpError). - initialize-twice lifecycle rejection. - MCP-Protocol-Version mismatch handling. - Reference-server side-effect witnesses for mutate_resource_list / mutate_prompt_list / bump_subscribable notifications and the subscribe/unsubscribe handler round-trip. Each raw-httpx test fails on 5xx and accepts 4xx as HTTP-level rejection; no false-green paths where a server crash would read as compliance. Bug fixes: - Reference server: mutator tools now use a dedicated _mutation_counter to prevent ephemeral_0 name-collisions. - test_drift._collect narrows to pytest.skip.Exception; other probe errors are recorded so drift diffs show the root cause. - Makefile PYTEST_IGNORE excludes tests/protocol_compliance. Matrix rust_full slice: 58 passed / 0 failed / 40 xfailed / 0 xpassed / 11 skipped (was 53/0/34/0/11). Zero unexpected failures. Signed-off-by: Jonathan Springer <jps@s390x.com> * fix(e2e): align test_oauth_jwks_e2e JWT secret with gateway minimum Test defaulted its JWT secret to "my-test-key" (11 chars) when JWT_SECRET_KEY was unset; the gateway's validator requires ≥32 chars so every admin-API call the fixtures make would fail with 401 "Invalid authentication credentials" on default deployments. Import JWT_SECRET from helpers.mcp_test_helpers — the 40-char default the rest of the e2e suite already shares — so this test self-configures without requiring the operator to export JWT_SECRET_KEY. Overriding via the env var still works. Signed-off-by: Jonathan Springer <jps@s390x.com> * ci(pre-commit): exclude tests/**/targets/ from name-tests-test in the lite config The main pre-commit config already excludes these POM-style support directories, but CI runs the lite config (Makefile:4175 — `pre-commit run --config .pre-commit-lite.yaml`) which was a near-duplicate that hadn't been kept in sync. Add `targets` to its excluded-dir alternation so the four compliance-harness target modules stop tripping the hook in CI. Signed-off-by: Jonathan Springer <jps@s390x.com> * chore(deps): pin authlib>=1.7.0 in the dev group via exclude-newer-package override authlib is a transitive dep of fastmcp, which the compliance harness uses as a test client — both live in the dev dependency group. mcpgateway itself imports nothing from authlib, so the constraint goes in `[dependency-groups] dev` rather than main project deps. Release 1.7.0 (2026-04-18) ships security fixes but was blocked by the repo-wide `exclude-newer = "10 days"` newness filter, leaving the lockfile on 1.6.9. Adds an `exclude-newer-package` entry for authlib (2026-04-19) so the filter allows the new release. Signed-off-by: Jonathan Springer <jps@s390x.com> --------- Signed-off-by: Jonathan Springer <jps@s390x.com>
1 parent a0ad03e commit ed64a6a

78 files changed

Lines changed: 5947 additions & 804 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,23 @@
1818
# report issues (linters). Modified files will need to be staged again.
1919
# -----------------------------------------------------------------------------
2020

21-
exclude: '(^|/)(\.pre-commit-.*\.yaml|normalize_special_characters\.py|test_input_validation\.py|ai_artifacts_normalizer\.py)$|(^|/)mcp-servers/templates/|(^|/)plugin_templates/|(^|/)tests/load/|.*\.(jinja|j2)$' # ignore these files, all templates, load tests, and jinja files
21+
exclude: |
22+
(?x)(
23+
# Specific files (auto-generated or intentionally exempt)
24+
(^|/)(
25+
\.pre-commit-.*\.yaml # transient pre-commit state/baselines
26+
|normalize_special_characters\.py
27+
|test_input_validation\.py
28+
|ai_artifacts_normalizer\.py
29+
)$
30+
# Template trees (not real source)
31+
|(^|/)mcp-servers/templates/
32+
|(^|/)plugin_templates/
33+
# Load tests (separate lifecycle)
34+
|(^|/)tests/load/
35+
# Jinja templates
36+
|.*\.(jinja|j2)$
37+
)
2238
fail_fast: true
2339

2440
repos:
@@ -347,10 +363,16 @@ repos:
347363

348364
- id: name-tests-test
349365
name: 🐍 Python Tests Naming
350-
description: Verifies test files in tests/ directories start with `test_`.
366+
description: Verifies test files start with `test_` in unit, e2e, protocol_compliance, security, and playwright suites.
351367
language: python
352-
files: (^|/)tests/.+\.py$
353-
exclude: ^tests/(.*/)?(pages|helpers|fuzzers|scripts|fixtures|migration|utils|manual|async|load.*|populate)/.*\.py$|^tests/e2e/mcp_test_helpers\.py$
368+
files: ^tests/(unit|e2e|protocol_compliance|security|playwright)/.+\.py$
369+
exclude: |
370+
(?x)^(
371+
# POM / helper / fixture / target support modules — by convention,
372+
# any *.py under a helpers/, fixtures/, targets/, or pages/ dir is
373+
# not a test file.
374+
tests/(unit|e2e|protocol_compliance|security|playwright)/(.*/)?(helpers|fixtures|targets|pages)/.*\.py
375+
)$
354376
args: [--pytest-test-first] # `test_.*\.py`
355377

356378

@@ -614,3 +636,13 @@ repos:
614636
description: Detects secrets within a repository using IBM's detect-secrets.
615637
args: ['--baseline', '.secrets.baseline', --use-all-plugins, --fail-on-unaudited]
616638
types: [text]
639+
# Keep in sync with DETECT_SECRETS_FILES_EXCLUDE in Makefile.
640+
# (.secrets.baseline is auto-excluded by detect-secrets itself.)
641+
exclude: |
642+
(?x)(
643+
package-lock\.json$
644+
|Cargo\.lock$
645+
|uv\.lock$
646+
|go\.sum$
647+
|mcpgateway/sri_hashes\.json$
648+
)

.pre-commit-lite.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ repos:
354354
description: Verifies test files in tests/ directories start with `test_`.
355355
language: python
356356
files: (^|/)tests/.+\.py$
357-
exclude: ^tests/(.*/)?(pages|helpers|fuzzers|scripts|fixtures|migration|utils|manual|async|load|loadtest|jmeter|client|populate)/.*\.py$|^tests/e2e/mcp_test_helpers\.py$
357+
exclude: ^tests/(.*/)?(pages|helpers|fuzzers|scripts|fixtures|migration|utils|manual|async|load|loadtest|jmeter|client|populate|targets)/.*\.py$
358358
args: [--pytest-test-first] # `test_.*\.py`
359359

360360
# - repo: https://github.com/pycqa/bandit

.secrets.baseline

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"exclude": {
3-
"files": "^.secrets.baseline|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock|go.sum|mcpgateway/sri_hashes.json|^.secrets.baseline$",
3+
"files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2026-04-18T21:54:58Z",
6+
"generated_at": "2026-04-19T08:51:02Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -326,71 +326,71 @@
326326
"hashed_secret": "319037749ce37e577db0b3628c7f90e333544391",
327327
"is_secret": false,
328328
"is_verified": false,
329-
"line_number": 893,
329+
"line_number": 929,
330330
"type": "Secret Keyword",
331331
"verified_result": null
332332
},
333333
{
334334
"hashed_secret": "6ae2832e494d1098e8901fe156083e39399a24f1",
335335
"is_secret": false,
336336
"is_verified": false,
337-
"line_number": 895,
337+
"line_number": 931,
338338
"type": "Secret Keyword",
339339
"verified_result": null
340340
},
341341
{
342342
"hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b",
343343
"is_secret": false,
344344
"is_verified": false,
345-
"line_number": 1586,
345+
"line_number": 1622,
346346
"type": "Secret Keyword",
347347
"verified_result": null
348348
},
349349
{
350350
"hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50",
351351
"is_secret": false,
352352
"is_verified": false,
353-
"line_number": 1797,
353+
"line_number": 1834,
354354
"type": "Secret Keyword",
355355
"verified_result": null
356356
},
357357
{
358358
"hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674",
359359
"is_secret": false,
360360
"is_verified": false,
361-
"line_number": 6219,
361+
"line_number": 6256,
362362
"type": "Secret Keyword",
363363
"verified_result": null
364364
},
365365
{
366366
"hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb",
367367
"is_secret": false,
368368
"is_verified": false,
369-
"line_number": 6716,
369+
"line_number": 6753,
370370
"type": "Secret Keyword",
371371
"verified_result": null
372372
},
373373
{
374374
"hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513",
375375
"is_secret": false,
376376
"is_verified": false,
377-
"line_number": 6728,
377+
"line_number": 6765,
378378
"type": "Secret Keyword",
379379
"verified_result": null
380380
},
381381
{
382382
"hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a",
383383
"is_secret": false,
384384
"is_verified": false,
385-
"line_number": 6800,
385+
"line_number": 6837,
386386
"type": "Secret Keyword",
387387
"verified_result": null
388388
},
389389
{
390390
"hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690",
391391
"is_secret": false,
392392
"is_verified": false,
393-
"line_number": 7870,
393+
"line_number": 7916,
394394
"type": "Secret Keyword",
395395
"verified_result": null
396396
}
@@ -5977,12 +5977,12 @@
59775977
"verified_result": null
59785978
}
59795979
],
5980-
"tests/e2e/mcp_test_helpers.py": [
5980+
"tests/e2e/helpers/mcp_test_helpers.py": [
59815981
{
59825982
"hashed_secret": "07b7b454e840ca79e1582edcf973b051fc56e079",
59835983
"is_secret": false,
59845984
"is_verified": false,
5985-
"line_number": 39,
5985+
"line_number": 45,
59865986
"type": "Secret Keyword",
59875987
"verified_result": null
59885988
}

Makefile

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -724,10 +724,12 @@ clean:
724724
# =============================================================================
725725
# help: 🧪 TESTING
726726
# help: smoketest - Run smoketest.py --verbose (build container, add MCP server, test endpoints)
727-
# help: test-mcp-cli - Run MCP protocol tests via mcp-cli against live gateway (localhost:8080)
728-
# help: Requires: mcp-cli installed, ContextForge running (docker-compose up)
729-
# help: Override gateway URL: MCP_CLI_BASE_URL=http://localhost:4444 make test-mcp-cli
730-
# help: No LLM or API key required - tests MCP protocol only
727+
# help: test-protocol-compliance - MCP protocol compliance harness: full (target, transport) matrix across reference + gateway (K=<filter> to pick one)
728+
# help: test-protocol-compliance-reference - Protocol compliance harness, reference server only (fast, always-on)
729+
# help: test-protocol-compliance-gateway - Protocol compliance harness, gateway-proxy + gateway-virtual targets (requires working gateway boot)
730+
# help: test-protocol-compliance-matrix - Protocol compliance matrix across every runnable engine; summary table (pass MATRIX_ARGS='--format markdown --out X' to override)
731+
# help: test-mcp-protocol-e2e - MCP protocol E2E via FastMCP client against live gateway (K=<filter> to pick one; MCP_E2E_CLIENT_TIMEOUT env to extend the 5s client timeout)
732+
# help: test-mcp-cli - [DEPRECATED] Alias for test-mcp-protocol-e2e (accepts same K=<filter>)
731733
# help: test - Run unit tests with pytest
732734
# help: test-verbose - Run tests sequentially with real-time test name output
733735
# help: test-profile - Run tests and show slowest 20 tests (durations >= 1s)
@@ -759,9 +761,10 @@ clean:
759761
# Dirs/files always excluded from standard pytest runs
760762
PYTEST_IGNORE := tests/fuzz tests/manual test.py \
761763
tests/e2e/test_entra_id_integration.py \
762-
tests/e2e/test_mcp_cli_protocol.py \
764+
tests/e2e/test_mcp_protocol_e2e.py \
763765
tests/e2e/test_mcp_rbac_transport.py \
764-
tests/e2e_rust
766+
tests/e2e_rust \
767+
tests/protocol_compliance
765768

766769
# Expand to --ignore=<path> flags for pytest CLI
767770
PYTEST_IGNORE_FLAGS := $(foreach p,$(PYTEST_IGNORE),--ignore=$(p))
@@ -773,13 +776,46 @@ smoketest:
773776
@$(VENV_DIR)/bin/python ./smoketest.py --verbose || { echo "❌ Smoketest failed!"; exit 1; }
774777
@echo "✅ Smoketest passed!"
775778

776-
test-mcp-cli: ## MCP protocol tests via mcp-cli + wrapper stdio (no LLM needed)
777-
@echo "🔌 Running MCP protocol tests via mcp-cli against $${MCP_CLI_BASE_URL:-http://localhost:8080}..."
779+
test-mcp-protocol-e2e: ## MCP protocol E2E via FastMCP client (K=<filter> to pick one)
780+
@echo "🔌 Running MCP protocol E2E tests against $${MCP_CLI_BASE_URL:-http://localhost:8080}..."
778781
@echo " Env: MCP_CLI_BASE_URL (gateway URL) JWT_SECRET_KEY PLATFORM_ADMIN_EMAIL"
782+
@echo " Timeout: $${MCP_E2E_CLIENT_TIMEOUT:-5.0}s per client operation (override MCP_E2E_CLIENT_TIMEOUT)"
783+
@if [ -n "$(K)" ]; then echo " Filter: -k \"$(K)\""; fi
779784
@/bin/bash -c 'source $(VENV_DIR)/bin/activate && \
780-
$(VENV_DIR)/bin/pytest tests/e2e/test_mcp_cli_protocol.py -v -s --tb=short \
781-
|| { echo "❌ mcp-cli protocol tests failed!"; exit 1; }; \
782-
echo "✅ mcp-cli protocol tests passed!"'
785+
$(VENV_DIR)/bin/pytest tests/e2e/test_mcp_protocol_e2e.py $(if $(K),-k "$(K)") -v -s --tb=short \
786+
|| { echo "❌ MCP protocol E2E tests failed!"; exit 1; }; \
787+
echo "✅ MCP protocol E2E tests passed!"'
788+
789+
test-mcp-cli: ## [DEPRECATED] Alias for test-mcp-protocol-e2e (subprocess + mcp-cli path removed)
790+
@echo "⚠️ 'make test-mcp-cli' is deprecated — use 'make test-mcp-protocol-e2e'."
791+
@echo " The mcp-cli + mcpgateway.wrapper subprocess path was replaced by the FastMCP client."
792+
@$(MAKE) test-mcp-protocol-e2e
793+
794+
test-protocol-compliance: ## MCP protocol compliance harness — full (target, transport) matrix (K=<filter> to pick one)
795+
@echo "📜 Running MCP protocol compliance harness (tests/protocol_compliance)..."
796+
@if [ -n "$(K)" ]; then echo " Filter: -k \"$(K)\""; fi
797+
@/bin/bash -c 'source $(VENV_DIR)/bin/activate && \
798+
$(VENV_DIR)/bin/pytest tests/protocol_compliance $(if $(K),-k "$(K)") -v --tb=short \
799+
|| { echo "❌ protocol compliance harness failed!"; exit 1; }; \
800+
echo "✅ protocol compliance harness passed!"'
801+
802+
test-protocol-compliance-reference: ## Protocol compliance harness — reference server only (fast, always-on)
803+
@echo "📜 Running MCP protocol compliance harness (reference target only)..."
804+
@/bin/bash -c 'source $(VENV_DIR)/bin/activate && \
805+
$(VENV_DIR)/bin/pytest tests/protocol_compliance -k "reference-stdio" -v --tb=short \
806+
|| { echo "❌ reference-target compliance harness failed!"; exit 1; }; \
807+
echo "✅ reference-target compliance harness passed!"'
808+
809+
test-protocol-compliance-gateway: ## Protocol compliance harness — gateway-proxy + gateway-virtual (needs in-process gateway boot to succeed)
810+
@echo "📜 Running MCP protocol compliance harness (gateway targets)..."
811+
@/bin/bash -c 'source $(VENV_DIR)/bin/activate && \
812+
$(VENV_DIR)/bin/pytest tests/protocol_compliance -k "gateway_proxy or gateway_virtual" -v --tb=short \
813+
|| { echo "❌ gateway-target compliance harness failed!"; exit 1; }; \
814+
echo "✅ gateway-target compliance harness passed!"'
815+
816+
test-protocol-compliance-matrix: ## MCP compliance matrix across every runnable engine (reference, python, rust_edge, rust_full) with aggregated summary
817+
@/bin/bash -c 'source $(VENV_DIR)/bin/activate && \
818+
$(VENV_DIR)/bin/python scripts/compliance_matrix.py $(MATRIX_ARGS)'
783819

784820
test-mcp-rbac: ## RBAC + multi-transport MCP protocol tests (needs live gateway + SSE)
785821
@echo "🔐 Running RBAC + multi-transport MCP protocol tests against $${MCP_CLI_BASE_URL:-http://localhost:8080}..."
@@ -1619,7 +1655,7 @@ testing-up: ## Start testing stack (Locust + A2A
16191655
@mkdir -p reports
16201656
HOST_UID=$(HOST_UID) HOST_GID=$(HOST_GID) \
16211657
LOCUST_EXPECT_WORKERS=$(TESTING_LOCUST_WORKERS) \
1622-
$(COMPOSE_CMD_MONITOR) --profile testing --profile inspector up -d --scale locust_worker=$(TESTING_LOCUST_WORKERS)
1658+
$(COMPOSE_CMD_MONITOR) --profile testing --profile inspector --profile sso up -d --scale locust_worker=$(TESTING_LOCUST_WORKERS)
16231659
@echo ""
16241660
@echo "✅ Testing stack started!"
16251661
@echo ""
@@ -1630,6 +1666,7 @@ testing-up: ## Start testing stack (Locust + A2A
16301666
@echo "Fast Test Server http://localhost:8880 MCP benchmark target"
16311667
@echo "A2A Echo Agent http://localhost:9100 A2A protocol target"
16321668
@echo "MCP Inspector http://localhost:6274 Interactive MCP client"
1669+
@echo "Keycloak http://localhost:8180 SSO / OAuth 2.1 provider (realm: mcp-gateway)"
16331670
@echo ""
16341671
@echo " 🔒 For DAST security scanning, also start ZAP: make testing-zap-up"
16351672
@echo ""
@@ -1676,7 +1713,7 @@ testing-rebuild-rust-full: ## Rebuild Rust image with no cache,
16761713
.PHONY: testing-down
16771714
testing-down: ## Stop testing stack
16781715
@echo "🧪 Stopping testing stack..."
1679-
$(COMPOSE_CMD_MONITOR) --profile testing --profile inspector --profile dast down --remove-orphans
1716+
$(COMPOSE_CMD_MONITOR) --profile testing --profile inspector --profile dast --profile sso down --remove-orphans
16801717
@echo "✅ Testing stack stopped."
16811718

16821719
.PHONY: testing-status
@@ -7703,8 +7740,17 @@ async-clean:
77037740
@pkill -f "aiomonitor" || true
77047741
@pkill -f "snakeviz" || true
77057742

7706-
# Exclude pattern for detect-secrets to skip common directories and auto generated files
7707-
DETECT_SECRETS_FILES_EXCLUDE := '^.secrets.baseline|package-lock.json|Cargo.lock|scripts/sign_image.sh|scripts/zap|sonar-project.properties|uv.lock|go.sum|mcpgateway/sri_hashes.json'
7743+
# Exclude pattern for detect-secrets to skip common directories and auto generated files.
7744+
# Uses Python verbose-regex mode (?x) so each alternative can be commented.
7745+
# Backslash line continuations collapse the value to one line with interleaved
7746+
# spaces — harmless under (?x).
7747+
DETECT_SECRETS_FILES_EXCLUDE := '(?x)( \
7748+
package-lock\.json$$ \
7749+
|Cargo\.lock$$ \
7750+
|uv\.lock$$ \
7751+
|go\.sum$$ \
7752+
|mcpgateway/sri_hashes\.json$$ \
7753+
)'
77087754

77097755
.PHONY: detect-secrets-scan
77107756
detect-secrets-scan: uv ## 🔍 detect-secrets scan for secrets in repository

0 commit comments

Comments
 (0)