From 0974f934f68f089f2ccd08ba54529bef1ed63d02 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Mon, 15 Jun 2026 14:35:46 +0100 Subject: [PATCH 01/12] Functional output ready Signed-off-by: Jitesh Nair --- mcpgateway/main.py | 3 + mcpgateway/routers/oauth_router.py | 1 + mcpgateway/routers/openapi_schema_router.py | 159 ++++++++ .../routers/test_openapi_schema_router.py | 353 ++++++++++++++++++ tests/utils/rbac_mocks.py | 6 +- 5 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 mcpgateway/routers/openapi_schema_router.py create mode 100644 tests/unit/mcpgateway/routers/test_openapi_schema_router.py diff --git a/mcpgateway/main.py b/mcpgateway/main.py index a0250bd6d4..481206f00a 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -125,6 +125,7 @@ stop_plugin_invalidation_listener, ) from mcpgateway.plugins.violation_codes import PLUGIN_VIOLATION_CODE_MAPPING, PluginViolationCode, VALID_HTTP_STATUS_CODES +from mcpgateway.routers.openapi_schema_router import router as openapi_schema_router from mcpgateway.routers.server_well_known import router as server_well_known_router from mcpgateway.routers.well_known import router as well_known_router from mcpgateway.schemas import ( @@ -12101,6 +12102,8 @@ async def cleanup_import_statuses(max_age_hours: int = 24, user=Depends(get_curr app.include_router(metrics_router) app.include_router(tag_router) app.include_router(export_import_router) +app.include_router(openapi_schema_router) +logger.info("OpenAPI schema generation router included") # Compliance report router (admin API) if settings.mcpgateway_admin_api_enabled: diff --git a/mcpgateway/routers/oauth_router.py b/mcpgateway/routers/oauth_router.py index ab36b5c65b..2b38ac30e5 100644 --- a/mcpgateway/routers/oauth_router.py +++ b/mcpgateway/routers/oauth_router.py @@ -379,6 +379,7 @@ async def initiate_oauth_flow(gateway_id: str, request: Request, current_user: E request: The FastAPI request object. current_user: The authenticated user initiating the OAuth flow. db: The database session dependency. + popup: Indicates if the OAuth flow is initiated in a popup window. Returns: A redirect response to the OAuth provider's authorization URL. diff --git a/mcpgateway/routers/openapi_schema_router.py b/mcpgateway/routers/openapi_schema_router.py new file mode 100644 index 0000000000..186410adc2 --- /dev/null +++ b/mcpgateway/routers/openapi_schema_router.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +"""Location: ./mcpgateway/routers/openapi_schema_router.py +Copyright 2026 +SPDX-License-Identifier: Apache-2.0 + +OpenAPI Schema Generation Router. + +This module provides a versioned REST API endpoint for generating MCP tool +input/output schemas from OpenAPI specifications. It mirrors the functionality +of the admin endpoint but without CSRF protection, making it suitable for +API consumers and integrations. +""" + +# Standard +import logging +import urllib.parse + +# Third-Party +from fastapi import APIRouter, Depends, Request +from fastapi.responses import JSONResponse +import httpx + +# First-Party +from mcpgateway.admin import _read_request_json +from mcpgateway.common.validators import SecurityValidator +from mcpgateway.middleware.rbac import get_current_user_with_permissions, require_permission +from mcpgateway.services.openapi_service import fetch_and_extract_schemas +from mcpgateway.utils.orjson_response import ORJSONResponse + +# Initialize router +router = APIRouter(prefix="/v1/tools", tags=["Tools"]) +logger = logging.getLogger(__name__) + + +@router.post("/generate-schema-from-openapi") +@require_permission("tools.create", allow_admin_bypass=False) +async def generate_schema_from_openapi( + request: Request, + _user=Depends(get_current_user_with_permissions), +) -> JSONResponse: + """ + Generate input_schema and output_schema from OpenAPI specification. + + This endpoint is part of the versioned REST API and does not require + admin UI access. It delegates to the same service logic as the admin + endpoint but without CSRF protection. + + Expects JSON body with: + - url: The tool URL (e.g., http://localhost:8100/calculate) + - request_type: HTTP method (GET, POST, etc.) - defaults to GET + - openapi_url: (optional) Direct OpenAPI spec URL + + Args: + request: FastAPI Request object containing JSON body + _user: Authenticated user from RBAC dependency + + Returns: + JSONResponse with generated schemas or error message. + """ + # Read and validate request body + try: + body = await _read_request_json(request) + except Exception: + return ORJSONResponse( + content={"message": "Invalid JSON in request body", "success": False}, + status_code=400, + ) + + if not isinstance(body, dict): + return ORJSONResponse( + content={"message": "Request body must be a JSON object", "success": False}, + status_code=400, + ) + + # Extract and validate fields + tool_url = body.get("url", "") + request_type = body.get("request_type", "GET") + openapi_url = body.get("openapi_url", "") + + if not isinstance(tool_url, str) or not isinstance(request_type, str) or not isinstance(openapi_url, str): + return ORJSONResponse( + content={"message": "'url', 'request_type', and 'openapi_url' must be strings", "success": False}, + status_code=400, + ) + + tool_url = tool_url.strip() + request_type = request_type.strip() + openapi_url = openapi_url.strip() + + if not tool_url: + return ORJSONResponse( + content={"message": "'url' is required to identify the API path and base URL", "success": False}, + status_code=400, + ) + + # Security validation + try: + SecurityValidator.validate_url(tool_url, "Tool URL") + except ValueError as e: + return ORJSONResponse( + content={"message": str(e), "success": False}, + status_code=400, + ) + + # Parse URL to extract base and path + parsed = urllib.parse.urlparse(tool_url) + base_url = f"{parsed.scheme}://{parsed.netloc}" + tool_path = parsed.path + + # Fetch and extract schemas + try: + input_schema, output_schema, spec_url = await fetch_and_extract_schemas( + base_url=base_url, + path=tool_path, + method=request_type, + openapi_url=openapi_url, + timeout=10.0, + ) + except ValueError as e: + return ORJSONResponse( + content={"message": f"Security validation failed: {str(e)}", "success": False}, + status_code=400, + ) + except KeyError as e: + return ORJSONResponse( + content={"message": str(e), "success": False}, + status_code=404, + ) + except httpx.HTTPStatusError as e: + logger.warning("OpenAPI spec server returned HTTP %s", e.response.status_code, exc_info=True) + return ORJSONResponse( + content={"message": f"OpenAPI spec server returned HTTP {e.response.status_code}", "success": False}, + status_code=502, + ) + except httpx.HTTPError: + logger.warning("Failed to fetch OpenAPI spec", exc_info=True) + return ORJSONResponse( + content={"message": "Failed to fetch OpenAPI spec from the provided URL", "success": False}, + status_code=502, + ) + except Exception: + logger.error("Error fetching OpenAPI spec", exc_info=True) + return ORJSONResponse( + content={"message": "An unexpected error occurred while processing the OpenAPI spec", "success": False}, + status_code=500, + ) + + return ORJSONResponse( + content={ + "message": "Schemas generated successfully from OpenAPI spec", + "success": True, + "input_schema": input_schema, + "output_schema": output_schema, + "spec_url": spec_url, + }, + status_code=200, + ) + +# Made with Bob diff --git a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py new file mode 100644 index 0000000000..ccec2d9714 --- /dev/null +++ b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py @@ -0,0 +1,353 @@ +# -*- coding: utf-8 -*- +"""Location: ./tests/unit/mcpgateway/routers/test_openapi_schema_router.py +Copyright 2026 +SPDX-License-Identifier: Apache-2.0 + +Unit tests for the OpenAPI schema generation router. + +Tests cover: + - POST /v1/tools/generate-schema-from-openapi: success, validation, error mapping + - RBAC enforcement + - Default value handling +""" + +# Standard +from unittest.mock import AsyncMock, MagicMock, patch + +# Third-Party +from fastapi import status +import httpx +import pytest + +# Local +from tests.utils.rbac_mocks import patch_rbac_decorators, restore_rbac_decorators + +_originals = patch_rbac_decorators() +# First-Party +from mcpgateway.routers import openapi_schema_router as router_mod # noqa: E402 # pylint: disable=wrong-import-position + +restore_rbac_decorators(_originals) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _mock_user(): + """Return mock user context dict.""" + return {"email": "test@example.com", "is_admin": False} + + +def _mock_request(body): + """Create mock FastAPI Request with JSON body.""" + request = MagicMock() + request.body = AsyncMock(return_value=body) + return request + + +# --------------------------------------------------------------------------- +# Happy Path Tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schema_success_all_fields(): + """Valid request with all fields returns schemas successfully.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate", "request_type": "POST", "openapi_url": "http://api.example.com/openapi.json"}') + + mock_schemas = ( + {"type": "object", "properties": {"a": {"type": "number"}}}, + {"type": "object", "properties": {"result": {"type": "number"}}}, + "http://api.example.com/openapi.json", + ) + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 200 + content = response.body + assert b'"success":true' in content + assert b'"message":"Schemas generated successfully from OpenAPI spec"' in content + assert b'"spec_url":"http://api.example.com/openapi.json"' in content + + +@pytest.mark.asyncio +async def test_generate_schema_success_minimal_fields(): + """Valid request with minimal fields applies defaults.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + mock_schemas = ( + {"type": "object"}, + {"type": "object"}, + "http://api.example.com/openapi.json", + ) + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 200 + # Verify default request_type="GET" was used + mock_fetch.assert_called_once() + call_kwargs = mock_fetch.call_args[1] + assert call_kwargs["method"] == "GET" + + +@pytest.mark.asyncio +async def test_generate_schema_auto_discovery(): + """Empty openapi_url triggers auto-discovery.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') + + mock_schemas = ( + {"type": "object"}, + {"type": "object"}, + "http://api.example.com/openapi.json", + ) + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 200 + # Verify empty openapi_url was passed (triggers auto-discovery in service) + call_kwargs = mock_fetch.call_args[1] + assert call_kwargs["openapi_url"] == "" + + +# --------------------------------------------------------------------------- +# Input Validation Tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schema_invalid_json(): + """Invalid JSON body returns 400.""" + request = _mock_request(b'{invalid json}') + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b'"message":"Invalid JSON in request body"' in content + + +@pytest.mark.asyncio +async def test_generate_schema_non_dict_body(): + """Non-dict JSON body returns 400.""" + request = _mock_request(b'["array", "not", "dict"]') + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b'"message":"Request body must be a JSON object"' in content + + +@pytest.mark.asyncio +async def test_generate_schema_missing_url(): + """Missing url field returns 400.""" + request = _mock_request(b'{"request_type": "POST"}') + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b"'url' is required" in content + + +@pytest.mark.asyncio +async def test_generate_schema_empty_url(): + """Empty url field returns 400.""" + request = _mock_request(b'{"url": " "}') + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b"'url' is required" in content + + +@pytest.mark.asyncio +async def test_generate_schema_non_string_fields(): + """Non-string field types return 400.""" + request = _mock_request(b'{"url": 123, "request_type": true, "openapi_url": null}') + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b"must be strings" in content + + +@pytest.mark.asyncio +async def test_generate_schema_invalid_url_format(): + """Invalid URL format returns 400 from security validation.""" + request = _mock_request(b'{"url": "not-a-valid-url"}') + + with patch("mcpgateway.routers.openapi_schema_router.SecurityValidator.validate_url") as mock_validate: + mock_validate.side_effect = ValueError("Invalid URL format") + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b"Invalid URL format" in content + + +# --------------------------------------------------------------------------- +# Service Error Mapping Tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schema_security_validation_error(): + """ValueError from security validation returns 400.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.side_effect = ValueError("Security validation failed: blocked domain") + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content + assert b"Security validation failed" in content + + +@pytest.mark.asyncio +async def test_generate_schema_path_not_found(): + """KeyError (path/method not found) returns 404.""" + request = _mock_request(b'{"url": "http://api.example.com/nonexistent"}') + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.side_effect = KeyError("Path /nonexistent not found in OpenAPI spec") + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 404 + content = response.body + assert b'"success":false' in content + + +@pytest.mark.asyncio +async def test_generate_schema_http_status_error(): + """httpx.HTTPStatusError returns 502.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + mock_response = MagicMock() + mock_response.status_code = 404 + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.side_effect = httpx.HTTPStatusError("Not Found", request=MagicMock(), response=mock_response) + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 502 + content = response.body + assert b'"success":false' in content + assert b"OpenAPI spec server returned HTTP 404" in content + + +@pytest.mark.asyncio +async def test_generate_schema_http_error(): + """httpx.HTTPError returns 502.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.side_effect = httpx.HTTPError("Connection failed") + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 502 + content = response.body + assert b'"success":false' in content + assert b"Failed to fetch OpenAPI spec" in content + + +@pytest.mark.asyncio +async def test_generate_schema_generic_exception(): + """Generic Exception returns 500.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.side_effect = Exception("Unexpected error") + + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + assert response.status_code == 500 + content = response.body + assert b'"success":false' in content + assert b"An unexpected error occurred" in content + + +# --------------------------------------------------------------------------- +# Default Value Tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schema_request_type_defaults_to_get(): + """request_type defaults to GET when omitted.""" + request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + + mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + call_kwargs = mock_fetch.call_args[1] + assert call_kwargs["method"] == "GET" + + +@pytest.mark.asyncio +async def test_generate_schema_openapi_url_can_be_empty(): + """openapi_url can be empty (triggers auto-discovery).""" + request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') + + mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + call_kwargs = mock_fetch.call_args[1] + assert call_kwargs["openapi_url"] == "" + + +# --------------------------------------------------------------------------- +# URL Parsing Tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schema_url_parsing(): + """URL is correctly parsed into base_url and path.""" + request = _mock_request(b'{"url": "https://api.example.com:8080/v1/calculate"}') + + mock_schemas = ({"type": "object"}, {"type": "object"}, "https://api.example.com:8080/openapi.json") + + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = mock_schemas + + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + + call_kwargs = mock_fetch.call_args[1] + assert call_kwargs["base_url"] == "https://api.example.com:8080" + assert call_kwargs["path"] == "/v1/calculate" + +# Made with Bob diff --git a/tests/utils/rbac_mocks.py b/tests/utils/rbac_mocks.py index d121d457f7..b596c550fd 100644 --- a/tests/utils/rbac_mocks.py +++ b/tests/utils/rbac_mocks.py @@ -265,7 +265,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.app.dependency_overrides.update(self.original_overrides) -def mock_require_permission_decorator(permission: str, resource_type: Optional[str] = None): +def mock_require_permission_decorator(permission: str, resource_type: Optional[str] = None, allow_admin_bypass: bool = True): """Mock version of the require_permission decorator that always allows access. This decorator bypasses all permission checks and simply executes the @@ -274,6 +274,7 @@ def mock_require_permission_decorator(permission: str, resource_type: Optional[s Args: permission: Required permission (ignored in mock) resource_type: Optional resource type (ignored in mock) + allow_admin_bypass: Whether to allow admin bypass (ignored in mock) Returns: Callable: A decorator that doesn't perform any permission checks @@ -301,12 +302,13 @@ def decorator(func): return decorator -def mock_require_any_permission(permissions, resource_type: Optional[str] = None): +def mock_require_any_permission(permissions, resource_type: Optional[str] = None, allow_admin_bypass: bool = True): """Mock version of require_any_permission that always allows access. Args: permissions: List of permissions (ignored in mock) resource_type: Optional resource type (ignored in mock) + allow_admin_bypass: Whether to allow admin bypass (ignored in mock) Returns: Callable: A decorator that doesn't perform any permission checks From a414a109aa75b401125a42b0d6694cbf07c9c259 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Mon, 15 Jun 2026 16:00:32 +0100 Subject: [PATCH 02/12] fixed pre-commit issues Signed-off-by: Jitesh Nair --- .secrets.baseline | 640 ++++++++++++------ mcpgateway/main.py | 2 +- mcpgateway/routers/openapi_schema_router.py | 26 +- .../routers/test_openapi_schema_router.py | 106 +-- 4 files changed, 516 insertions(+), 258 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 2298d53a50..89349c5c93 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-26T14:48:00Z", + "generated_at": "2026-06-15T14:20:15Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -100,7 +100,7 @@ "hashed_secret": "548a5cde15c93a9e50e12e4cba776b85f6befc83", "is_secret": false, "is_verified": false, - "line_number": 75, + "line_number": 72, "type": "Secret Keyword", "verified_result": null }, @@ -108,7 +108,7 @@ "hashed_secret": "8e9a8667ded25c805ce3dc920a5f2343bd2d198f", "is_secret": false, "is_verified": false, - "line_number": 76, + "line_number": 73, "type": "Secret Keyword", "verified_result": null }, @@ -116,7 +116,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 708, + "line_number": 690, "type": "Basic Auth Credentials", "verified_result": null }, @@ -124,7 +124,7 @@ "hashed_secret": "14f8aa3e560a47851908ab0f04ec856dbc512d93", "is_secret": false, "is_verified": false, - "line_number": 936, + "line_number": 918, "type": "Secret Keyword", "verified_result": null }, @@ -132,7 +132,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1222, + "line_number": 1205, "type": "Secret Keyword", "verified_result": null }, @@ -140,7 +140,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 1248, + "line_number": 1231, "type": "Secret Keyword", "verified_result": null }, @@ -148,7 +148,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 1256, + "line_number": 1239, "type": "Secret Keyword", "verified_result": null }, @@ -156,7 +156,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 1335, + "line_number": 1318, "type": "Secret Keyword", "verified_result": null }, @@ -164,7 +164,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 1340, + "line_number": 1323, "type": "Secret Keyword", "verified_result": null }, @@ -172,7 +172,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 1345, + "line_number": 1328, "type": "Secret Keyword", "verified_result": null }, @@ -180,7 +180,7 @@ "hashed_secret": "cb32747fcfb55eaa194c8cd8e4ba7d49ada08a94", "is_secret": false, "is_verified": false, - "line_number": 1351, + "line_number": 1334, "type": "Secret Keyword", "verified_result": null }, @@ -188,7 +188,7 @@ "hashed_secret": "6c178d51b13520496dbc767ed3d9d7aa5803ac72", "is_secret": false, "is_verified": false, - "line_number": 1363, + "line_number": 1346, "type": "Secret Keyword", "verified_result": null }, @@ -196,7 +196,7 @@ "hashed_secret": "ca45060a53fd8a255d1a83ee8d2f025283ccc66e", "is_secret": false, "is_verified": false, - "line_number": 1381, + "line_number": 1364, "type": "Secret Keyword", "verified_result": null }, @@ -204,7 +204,7 @@ "hashed_secret": "910fbf00f58e9bcb095ea26a75cc1d9a3355e671", "is_secret": false, "is_verified": false, - "line_number": 1442, + "line_number": 1425, "type": "Secret Keyword", "verified_result": null } @@ -232,7 +232,7 @@ "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", "is_secret": false, "is_verified": false, - "line_number": 121, + "line_number": 120, "type": "Secret Keyword", "verified_result": null }, @@ -240,7 +240,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 218, + "line_number": 217, "type": "Secret Keyword", "verified_result": null }, @@ -248,7 +248,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 220, + "line_number": 219, "type": "Secret Keyword", "verified_result": null }, @@ -256,7 +256,7 @@ "hashed_secret": "90bd1b48e958257948487b90bee080ba5ed00caa", "is_secret": false, "is_verified": false, - "line_number": 292, + "line_number": 291, "type": "Hex High Entropy String", "verified_result": null }, @@ -264,7 +264,7 @@ "hashed_secret": "48ffbad96aa9c2b33f9486f5a3c2108198acb518", "is_secret": false, "is_verified": false, - "line_number": 293, + "line_number": 292, "type": "Hex High Entropy String", "verified_result": null } @@ -274,7 +274,7 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, - "line_number": 1590, + "line_number": 1336, "type": "Secret Keyword", "verified_result": null }, @@ -282,7 +282,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1678, + "line_number": 1424, "type": "Secret Keyword", "verified_result": null }, @@ -290,7 +290,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 2028, + "line_number": 1774, "type": "Basic Auth Credentials", "verified_result": null }, @@ -298,7 +298,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3394, + "line_number": 3140, "type": "Basic Auth Credentials", "verified_result": null }, @@ -306,7 +306,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3485, + "line_number": 3231, "type": "Secret Keyword", "verified_result": null }, @@ -314,7 +314,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 3528, + "line_number": 3274, "type": "Secret Keyword", "verified_result": null }, @@ -322,7 +322,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 3533, + "line_number": 3279, "type": "Secret Keyword", "verified_result": null }, @@ -330,7 +330,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 3538, + "line_number": 3284, "type": "Secret Keyword", "verified_result": null } @@ -350,7 +350,7 @@ "hashed_secret": "844c398e469ef3fb919da3778944365ab2175fb7", "is_secret": false, "is_verified": false, - "line_number": 525, + "line_number": 530, "type": "Secret Keyword", "verified_result": null }, @@ -358,7 +358,7 @@ "hashed_secret": "319037749ce37e577db0b3628c7f90e333544391", "is_secret": false, "is_verified": false, - "line_number": 962, + "line_number": 967, "type": "Secret Keyword", "verified_result": null }, @@ -366,7 +366,7 @@ "hashed_secret": "6ae2832e494d1098e8901fe156083e39399a24f1", "is_secret": false, "is_verified": false, - "line_number": 964, + "line_number": 969, "type": "Secret Keyword", "verified_result": null }, @@ -374,7 +374,7 @@ "hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b", "is_secret": false, "is_verified": false, - "line_number": 1616, + "line_number": 1621, "type": "Secret Keyword", "verified_result": null }, @@ -382,7 +382,7 @@ "hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50", "is_secret": false, "is_verified": false, - "line_number": 1835, + "line_number": 1840, "type": "Secret Keyword", "verified_result": null }, @@ -390,7 +390,7 @@ "hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674", "is_secret": false, "is_verified": false, - "line_number": 6052, + "line_number": 6386, "type": "Secret Keyword", "verified_result": null }, @@ -398,7 +398,7 @@ "hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb", "is_secret": false, "is_verified": false, - "line_number": 6549, + "line_number": 6883, "type": "Secret Keyword", "verified_result": null }, @@ -406,7 +406,7 @@ "hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513", "is_secret": false, "is_verified": false, - "line_number": 6561, + "line_number": 6895, "type": "Secret Keyword", "verified_result": null }, @@ -414,7 +414,7 @@ "hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a", "is_secret": false, "is_verified": false, - "line_number": 6633, + "line_number": 6967, "type": "Secret Keyword", "verified_result": null }, @@ -422,7 +422,7 @@ "hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690", "is_secret": false, "is_verified": false, - "line_number": 7715, + "line_number": 8048, "type": "Secret Keyword", "verified_result": null } @@ -432,7 +432,7 @@ "hashed_secret": "b7991211e1e73c924ee8febee2d1e80f0173c762", "is_secret": false, "is_verified": false, - "line_number": 132, + "line_number": 131, "type": "Secret Keyword", "verified_result": null }, @@ -440,7 +440,7 @@ "hashed_secret": "108b310facc1a193833fc2971fd83081f775ea0c", "is_secret": false, "is_verified": false, - "line_number": 239, + "line_number": 238, "type": "Secret Keyword", "verified_result": null }, @@ -448,7 +448,7 @@ "hashed_secret": "5d4e7584ece9047cd53ae0e4b40726328968ce68", "is_secret": false, "is_verified": false, - "line_number": 327, + "line_number": 326, "type": "Hex High Entropy String", "verified_result": null }, @@ -456,7 +456,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 444, + "line_number": 443, "type": "Secret Keyword", "verified_result": null }, @@ -464,7 +464,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 444, + "line_number": 443, "type": "Base64 High Entropy String", "verified_result": null }, @@ -472,7 +472,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 532, + "line_number": 531, "type": "Secret Keyword", "verified_result": null }, @@ -480,7 +480,7 @@ "hashed_secret": "5546721ffdfc2e5b0e4c0da38f10774f9ad50b09", "is_secret": false, "is_verified": false, - "line_number": 628, + "line_number": 627, "type": "Secret Keyword", "verified_result": null }, @@ -488,7 +488,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 699, + "line_number": 698, "type": "Basic Auth Credentials", "verified_result": null }, @@ -496,7 +496,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 705, + "line_number": 704, "type": "Secret Keyword", "verified_result": null } @@ -703,6 +703,196 @@ "verified_result": null } ], + "client/src/hooks/useToolForm.ts": [ + { + "hashed_secret": "85d1e045b6ed7751f763f058a68da53ea26dbd4d", + "is_secret": false, + "is_verified": false, + "line_number": 271, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/en-US/auth.json": [ + { + "hashed_secret": "3a6ed43bb0d761d8137363ec5e194a5158115cf3", + "is_secret": false, + "is_verified": false, + "line_number": 18, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "b387e03f480bde661b1440f78acd7db569aea7f0", + "is_secret": false, + "is_verified": false, + "line_number": 19, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "a2c6b77a1f4f9da20c536a5c7177a008338948fd", + "is_secret": false, + "is_verified": false, + "line_number": 20, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "8800ddc46c3185561435ff69924b0d06bd30c73b", + "is_secret": false, + "is_verified": false, + "line_number": 23, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/en-US/users.json": [ + { + "hashed_secret": "a2c6b77a1f4f9da20c536a5c7177a008338948fd", + "is_secret": false, + "is_verified": false, + "line_number": 13, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/es-ES/auth.json": [ + { + "hashed_secret": "8c31b65bdecdc9f18b695d7318186fd1feed690d", + "is_secret": false, + "is_verified": false, + "line_number": 4, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "123ba0dd5f78127af4473d02cd1b18114ef52ab2", + "is_secret": false, + "is_verified": false, + "line_number": 7, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "b47f38ba10b4be808e0ed4eee32a54098905a48d", + "is_secret": false, + "is_verified": false, + "line_number": 18, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "c7cf571452d242282fa5fd6625ec338224a94891", + "is_secret": false, + "is_verified": false, + "line_number": 19, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", + "is_secret": false, + "is_verified": false, + "line_number": 20, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "0c819b12ff43d593e0bd136c9b0e477c08f7c313", + "is_secret": false, + "is_verified": false, + "line_number": 23, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/es-ES/users.json": [ + { + "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", + "is_secret": false, + "is_verified": false, + "line_number": 13, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "8c31b65bdecdc9f18b695d7318186fd1feed690d", + "is_secret": false, + "is_verified": false, + "line_number": 28, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/pt-BR/auth.json": [ + { + "hashed_secret": "7751a23fa55170a57e90374df13a3ab78efe0e99", + "is_secret": false, + "is_verified": false, + "line_number": 4, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "379b6f5adbaaf331096aa110bb196aef8c38d73a", + "is_secret": false, + "is_verified": false, + "line_number": 7, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "61769dd26d3b921cc7a1625b9785b101ba4ed135", + "is_secret": false, + "is_verified": false, + "line_number": 18, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "f2b368852b6112ad05424113abf098723a7b75cb", + "is_secret": false, + "is_verified": false, + "line_number": 19, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", + "is_secret": false, + "is_verified": false, + "line_number": 20, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "5a1e0088a6bda02a394bff63b95ae21229882c4f", + "is_secret": false, + "is_verified": false, + "line_number": 23, + "type": "Secret Keyword", + "verified_result": null + } + ], + "client/src/i18n/locales/pt-BR/users.json": [ + { + "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", + "is_secret": false, + "is_verified": false, + "line_number": 13, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "7751a23fa55170a57e90374df13a3ab78efe0e99", + "is_secret": false, + "is_verified": false, + "line_number": 28, + "type": "Secret Keyword", + "verified_result": null + } + ], "crates/mcp_runtime/tests/runtime.rs": [ { "hashed_secret": "5b204323030835cdda5d258742d1452e812988de", @@ -986,7 +1176,7 @@ "hashed_secret": "b6f3674b78a02881de33177e6f6fb8b851e0101c", "is_secret": false, "is_verified": false, - "line_number": 270, + "line_number": 269, "type": "Secret Keyword", "verified_result": null }, @@ -994,7 +1184,7 @@ "hashed_secret": "108b310facc1a193833fc2971fd83081f775ea0c", "is_secret": false, "is_verified": false, - "line_number": 365, + "line_number": 364, "type": "Secret Keyword", "verified_result": null }, @@ -1002,7 +1192,7 @@ "hashed_secret": "a6e38ae8688f390d45039a635c394dea67b9bc41", "is_secret": false, "is_verified": false, - "line_number": 415, + "line_number": 412, "type": "Secret Keyword", "verified_result": null }, @@ -1010,7 +1200,7 @@ "hashed_secret": "bba409d5cd2d77fe052d5ecc39b35f167641118c", "is_secret": false, "is_verified": false, - "line_number": 423, + "line_number": 420, "type": "Secret Keyword", "verified_result": null }, @@ -1018,7 +1208,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 471, + "line_number": 468, "type": "Secret Keyword", "verified_result": null }, @@ -1026,7 +1216,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 1007, + "line_number": 1004, "type": "Secret Keyword", "verified_result": null }, @@ -1034,7 +1224,7 @@ "hashed_secret": "bd0160c2cf35d950843c88f3be2b9412ed71f485", "is_secret": false, "is_verified": false, - "line_number": 1110, + "line_number": 1107, "type": "Secret Keyword", "verified_result": null }, @@ -1042,7 +1232,7 @@ "hashed_secret": "266bda40a4eed34ae3cead41cb813b8f94dc6f27", "is_secret": false, "is_verified": false, - "line_number": 1116, + "line_number": 1113, "type": "Secret Keyword", "verified_result": null }, @@ -1050,7 +1240,7 @@ "hashed_secret": "3879c93c04cab8707ab8ab6a4f97d51ea8fb6f47", "is_secret": false, "is_verified": false, - "line_number": 1225, + "line_number": 1222, "type": "Secret Keyword", "verified_result": null }, @@ -1058,7 +1248,7 @@ "hashed_secret": "756fa1fd4f0c6d5249cc2ad68d5a5a6bfe96aacd", "is_secret": false, "is_verified": false, - "line_number": 1260, + "line_number": 1257, "type": "Secret Keyword", "verified_result": null }, @@ -1066,7 +1256,7 @@ "hashed_secret": "2df14e4719f299249cd9a97cf68cc87232a27cbb", "is_secret": false, "is_verified": false, - "line_number": 1799, + "line_number": 1796, "type": "Hex High Entropy String", "verified_result": null }, @@ -1074,7 +1264,7 @@ "hashed_secret": "0772e9ff96270d7e9a41cef301e135da6f74a8fc", "is_secret": false, "is_verified": false, - "line_number": 1876, + "line_number": 1873, "type": "Hex High Entropy String", "verified_result": null }, @@ -1082,7 +1272,7 @@ "hashed_secret": "293324f6824bb3a6db5c4dc42a60ddd4a9851c99", "is_secret": false, "is_verified": false, - "line_number": 2192, + "line_number": 2190, "type": "Hex High Entropy String", "verified_result": null }, @@ -1090,7 +1280,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 2673, + "line_number": 2647, "type": "Secret Keyword", "verified_result": null }, @@ -1098,7 +1288,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 2673, + "line_number": 2647, "type": "Base64 High Entropy String", "verified_result": null }, @@ -1106,7 +1296,7 @@ "hashed_secret": "64f5490a2808803712ef99d9dfb8bc3ea5a15077", "is_secret": false, "is_verified": false, - "line_number": 2686, + "line_number": 2660, "type": "Secret Keyword", "verified_result": null }, @@ -1114,7 +1304,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 2834, + "line_number": 2808, "type": "Secret Keyword", "verified_result": null }, @@ -1122,7 +1312,7 @@ "hashed_secret": "12be9f7db42eb4a2d881a99fa9ba847e1f83677f", "is_secret": false, "is_verified": false, - "line_number": 2855, + "line_number": 2829, "type": "Secret Keyword", "verified_result": null }, @@ -1130,7 +1320,7 @@ "hashed_secret": "c3de40d5e3fc71ed62771c2127a8e42585026c97", "is_secret": false, "is_verified": false, - "line_number": 2863, + "line_number": 2837, "type": "Secret Keyword", "verified_result": null }, @@ -1138,7 +1328,7 @@ "hashed_secret": "ce53277317aa3cd27c1619d7d371306ded2ddd1e", "is_secret": false, "is_verified": false, - "line_number": 2868, + "line_number": 2842, "type": "Secret Keyword", "verified_result": null } @@ -1306,7 +1496,7 @@ "hashed_secret": "e204d28a2874f6123747650d3e4003d4357d75eb", "is_secret": false, "is_verified": false, - "line_number": 855, + "line_number": 852, "type": "Secret Keyword", "verified_result": null }, @@ -1314,17 +1504,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1150, - "type": "Secret Keyword", - "verified_result": null - } - ], - "docs/docs/architecture/oauth-design.md": [ - { - "hashed_secret": "5c216af8faacfff02f8ee00326ecfcfb3334922f", - "is_secret": false, - "is_verified": false, - "line_number": 153, + "line_number": 1147, "type": "Secret Keyword", "verified_result": null } @@ -1384,11 +1564,123 @@ } ], "docs/docs/architecture/roadmap.md": [ + { + "hashed_secret": "e62854369199d65b6a66cd90fe8203ac4d5be26c", + "is_secret": false, + "is_verified": false, + "line_number": 190, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "35722442567571107241e7e6ca2b554515e485da", + "is_secret": false, + "is_verified": false, + "line_number": 338, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "0608c4054662dd902e1314f7e450e3eaa81c1143", + "is_secret": false, + "is_verified": false, + "line_number": 340, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "176939638191d5a13935ccb90c0c8a70a5e30773", + "is_secret": false, + "is_verified": false, + "line_number": 347, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "564289a5f34546401bbc727c20a14069761f9143", + "is_secret": false, + "is_verified": false, + "line_number": 420, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "37745ed7a0f005fb14522c5cc7c1ba3d9e0df579", + "is_secret": false, + "is_verified": false, + "line_number": 424, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "0c8207b92779c0a42507a293a23106814cc381e1", + "is_secret": false, + "is_verified": false, + "line_number": 427, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "ce5070ef8a0cf3e595ae4cf3f73f6a0eca87e346", + "is_secret": false, + "is_verified": false, + "line_number": 429, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "46a808cfd5beafa5e60aefee867bf92025dc2849", + "is_secret": false, + "is_verified": false, + "line_number": 434, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "e4ae36d7ffd9dd805a909ac5a20de165a2629ba4", + "is_secret": false, + "is_verified": false, + "line_number": 435, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "9f08d40159a4b035fc7998b398bd16b38f91e806", + "is_secret": false, + "is_verified": false, + "line_number": 440, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "61a135089eac561a2ff7cedeefb03975bed000f8", + "is_secret": false, + "is_verified": false, + "line_number": 633, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "5bdcd3c0d4d24ae3e71b3b452a024c6324c7e4bb", + "is_secret": false, + "is_verified": false, + "line_number": 645, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "a4b3ee46e1384e5b0be8cfdb2169977d8a160136", + "is_secret": false, + "is_verified": false, + "line_number": 650, + "type": "Secret Keyword", + "verified_result": null + }, { "hashed_secret": "ff2ccd73154c1c71ef9a2982fe16e3c529e7a1ae", "is_secret": false, "is_verified": false, - "line_number": 105, + "line_number": 745, "type": "Secret Keyword", "verified_result": null }, @@ -1396,7 +1688,7 @@ "hashed_secret": "74f82b992f8a92b849c2be77447f98a510338333", "is_secret": false, "is_verified": false, - "line_number": 106, + "line_number": 746, "type": "Secret Keyword", "verified_result": null }, @@ -1404,7 +1696,7 @@ "hashed_secret": "64814a3b7fd8444a56ad3641fd3451c6deaf0757", "is_secret": false, "is_verified": false, - "line_number": 114, + "line_number": 754, "type": "Secret Keyword", "verified_result": null }, @@ -1412,7 +1704,7 @@ "hashed_secret": "af84d91fde168566c7dc18f3121ea2fbe651af1f", "is_secret": false, "is_verified": false, - "line_number": 143, + "line_number": 783, "type": "Secret Keyword", "verified_result": null }, @@ -1420,7 +1712,7 @@ "hashed_secret": "d1081e351fbebe0deb0c2867d5f731c8f9cc3fd8", "is_secret": false, "is_verified": false, - "line_number": 396, + "line_number": 1036, "type": "Secret Keyword", "verified_result": null }, @@ -1428,7 +1720,7 @@ "hashed_secret": "b7e9a6e2e04ed3edbf8b4e21def4beccbb6eb6b1", "is_secret": false, "is_verified": false, - "line_number": 397, + "line_number": 1037, "type": "Secret Keyword", "verified_result": null }, @@ -1436,7 +1728,7 @@ "hashed_secret": "6d244f58364223afcc2322f696137b01ece78d9d", "is_secret": false, "is_verified": false, - "line_number": 398, + "line_number": 1038, "type": "Secret Keyword", "verified_result": null }, @@ -1444,7 +1736,7 @@ "hashed_secret": "8bf177a72c93155020ebca9ee7f3c6e0fbdee946", "is_secret": false, "is_verified": false, - "line_number": 416, + "line_number": 1056, "type": "Secret Keyword", "verified_result": null }, @@ -1452,7 +1744,7 @@ "hashed_secret": "c41e6cd4245e404f07635fdbac351aad4d54a213", "is_secret": false, "is_verified": false, - "line_number": 515, + "line_number": 1155, "type": "Secret Keyword", "verified_result": null }, @@ -1460,7 +1752,7 @@ "hashed_secret": "6568a9761e26d16a111247f279b6fcabff1c8c86", "is_secret": false, "is_verified": false, - "line_number": 550, + "line_number": 1190, "type": "Secret Keyword", "verified_result": null }, @@ -1468,7 +1760,7 @@ "hashed_secret": "314fc7fd580f8c510be196143946bbe5ea753443", "is_secret": false, "is_verified": false, - "line_number": 551, + "line_number": 1191, "type": "Secret Keyword", "verified_result": null }, @@ -1476,7 +1768,7 @@ "hashed_secret": "afb9d1dcf212fa33d079a070ab90f0bef03c324b", "is_secret": false, "is_verified": false, - "line_number": 565, + "line_number": 1205, "type": "Secret Keyword", "verified_result": null }, @@ -1484,7 +1776,7 @@ "hashed_secret": "2736fab291f04e69b62d490c3c09361f5b82461a", "is_secret": false, "is_verified": false, - "line_number": 572, + "line_number": 1212, "type": "Secret Keyword", "verified_result": null }, @@ -1492,7 +1784,7 @@ "hashed_secret": "7542c8f677ffbbe75a5301a5c76f88401dc42897", "is_secret": false, "is_verified": false, - "line_number": 581, + "line_number": 1221, "type": "Secret Keyword", "verified_result": null }, @@ -1500,7 +1792,7 @@ "hashed_secret": "ca20728d35f00c3a4c21b1fc8b0086b59f89df2d", "is_secret": false, "is_verified": false, - "line_number": 586, + "line_number": 1226, "type": "Secret Keyword", "verified_result": null }, @@ -1508,7 +1800,7 @@ "hashed_secret": "5b7dcd14a4faa2cdd54cf6eb8d4bc35da31914a1", "is_secret": false, "is_verified": false, - "line_number": 598, + "line_number": 1238, "type": "Secret Keyword", "verified_result": null }, @@ -1516,7 +1808,7 @@ "hashed_secret": "245dbfa0498aaa6898fe57a191a5b3130466a943", "is_secret": false, "is_verified": false, - "line_number": 619, + "line_number": 1259, "type": "Secret Keyword", "verified_result": null }, @@ -1524,7 +1816,7 @@ "hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997", "is_secret": false, "is_verified": false, - "line_number": 621, + "line_number": 1261, "type": "Secret Keyword", "verified_result": null }, @@ -1532,7 +1824,7 @@ "hashed_secret": "ee977806d7286510da8b9a7492ba58e2484c0ecc", "is_secret": false, "is_verified": false, - "line_number": 624, + "line_number": 1264, "type": "Secret Keyword", "verified_result": null }, @@ -1540,7 +1832,7 @@ "hashed_secret": "a603075604516de626cda903868e457fad826533", "is_secret": false, "is_verified": false, - "line_number": 631, + "line_number": 1271, "type": "Secret Keyword", "verified_result": null }, @@ -1548,7 +1840,7 @@ "hashed_secret": "95ef18f58f32e3821ec7e627af6ee4757f68760e", "is_secret": false, "is_verified": false, - "line_number": 632, + "line_number": 1272, "type": "Secret Keyword", "verified_result": null }, @@ -1556,7 +1848,7 @@ "hashed_secret": "b3aca92c793ee0e9b1a9b0a5f5fc044e05140df3", "is_secret": false, "is_verified": false, - "line_number": 670, + "line_number": 1310, "type": "Secret Keyword", "verified_result": null }, @@ -1564,7 +1856,7 @@ "hashed_secret": "ae21c64a87f6bb0b8e16e55c48be4cc638d7bd3f", "is_secret": false, "is_verified": false, - "line_number": 681, + "line_number": 1321, "type": "Secret Keyword", "verified_result": null }, @@ -1572,7 +1864,7 @@ "hashed_secret": "58d1bbce297de3c304a9fefc3b483181872a5c6b", "is_secret": false, "is_verified": false, - "line_number": 683, + "line_number": 1323, "type": "Secret Keyword", "verified_result": null }, @@ -1580,7 +1872,7 @@ "hashed_secret": "de93ba2d15f3dbf65f76e6fd21e1e4b002459dd8", "is_secret": false, "is_verified": false, - "line_number": 715, + "line_number": 1355, "type": "Secret Keyword", "verified_result": null }, @@ -1588,7 +1880,7 @@ "hashed_secret": "533739bb9d1dad53e71d8c93200cc2510d442226", "is_secret": false, "is_verified": false, - "line_number": 718, + "line_number": 1358, "type": "Secret Keyword", "verified_result": null } @@ -2182,7 +2474,7 @@ "hashed_secret": "ad7920eddff0914889d74a47bb45fc80697da5a8", "is_secret": false, "is_verified": false, - "line_number": 265, + "line_number": 268, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2190,7 +2482,7 @@ "hashed_secret": "998ab9e14841d778dcafdf72ceaac0cacbb5ea2a", "is_secret": false, "is_verified": false, - "line_number": 467, + "line_number": 399, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2198,7 +2490,7 @@ "hashed_secret": "d20047e4dff166045c7f61066c62955ec33c1657", "is_secret": false, "is_verified": false, - "line_number": 670, + "line_number": 602, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2206,7 +2498,7 @@ "hashed_secret": "fc19bda36b0af2d6a957034199a79c6583c707a8", "is_secret": false, "is_verified": false, - "line_number": 831, + "line_number": 763, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2214,7 +2506,7 @@ "hashed_secret": "848f72afea73993a89ad3dcd66fc152f04f2be41", "is_secret": false, "is_verified": false, - "line_number": 999, + "line_number": 931, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2222,7 +2514,7 @@ "hashed_secret": "5193dda17da630c041fe949c06e7b6dd5ac8628f", "is_secret": false, "is_verified": false, - "line_number": 1276, + "line_number": 1208, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2230,7 +2522,15 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1397, + "line_number": 1329, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", + "is_secret": false, + "is_verified": false, + "line_number": 1578, "type": "Secret Keyword", "verified_result": null } @@ -2286,7 +2586,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 151, + "line_number": 146, "type": "Secret Keyword", "verified_result": null }, @@ -2294,7 +2594,7 @@ "hashed_secret": "bc2f74c22f98f7b6ffbc2f67453dbfa99bce9a32", "is_secret": false, "is_verified": false, - "line_number": 432, + "line_number": 427, "type": "Secret Keyword", "verified_result": null }, @@ -2302,7 +2602,7 @@ "hashed_secret": "a2166e0b243f4e192e14000a6aa8f46cde6bfc20", "is_secret": false, "is_verified": false, - "line_number": 798, + "line_number": 793, "type": "Secret Keyword", "verified_result": null }, @@ -2310,7 +2610,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1280, + "line_number": 1275, "type": "Basic Auth Credentials", "verified_result": null }, @@ -2318,7 +2618,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1284, + "line_number": 1279, "type": "Secret Keyword", "verified_result": null } @@ -2543,22 +2843,6 @@ "line_number": 570, "type": "Secret Keyword", "verified_result": null - }, - { - "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", - "is_secret": false, - "is_verified": false, - "line_number": 643, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", - "is_secret": false, - "is_verified": false, - "line_number": 645, - "type": "Secret Keyword", - "verified_result": null } ], "docs/docs/manage/oauth.md": [ @@ -2706,7 +2990,7 @@ "hashed_secret": "1513802ada0ec04c2446206e16baa7256ebff74b", "is_secret": false, "is_verified": false, - "line_number": 601, + "line_number": 598, "type": "Secret Keyword", "verified_result": null } @@ -2750,7 +3034,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 320, + "line_number": 428, "type": "Secret Keyword", "verified_result": null } @@ -2922,7 +3206,7 @@ "hashed_secret": "c58994eefb19ae2d0bfc26a106de438359e53fb6", "is_secret": false, "is_verified": false, - "line_number": 325, + "line_number": 314, "type": "Secret Keyword", "verified_result": null }, @@ -2930,7 +3214,7 @@ "hashed_secret": "8c9fdbd88e905d33102d0be537adeab8595fe0da", "is_secret": false, "is_verified": false, - "line_number": 355, + "line_number": 344, "type": "Secret Keyword", "verified_result": null }, @@ -2938,7 +3222,7 @@ "hashed_secret": "6ea1c94703f93074853165df24e0ded6917472af", "is_secret": false, "is_verified": false, - "line_number": 381, + "line_number": 370, "type": "Secret Keyword", "verified_result": null }, @@ -2946,7 +3230,7 @@ "hashed_secret": "b05b46675e3b6cecdb5ed7d0c24a8a183abd4de8", "is_secret": false, "is_verified": false, - "line_number": 751, + "line_number": 740, "type": "Secret Keyword", "verified_result": null } @@ -3076,27 +3360,11 @@ "type": "Secret Keyword", "verified_result": null }, - { - "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", - "is_secret": false, - "is_verified": false, - "line_number": 777, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "5c216af8faacfff02f8ee00326ecfcfb3334922f", - "is_secret": false, - "is_verified": false, - "line_number": 799, - "type": "Secret Keyword", - "verified_result": null - }, { "hashed_secret": "157b314984dacdea10427c1ae3a856d286318e0d", "is_secret": false, "is_verified": false, - "line_number": 902, + "line_number": 844, "type": "Secret Keyword", "verified_result": null } @@ -3363,20 +3631,20 @@ "verified_result": null } ], - "docs/docs/using/agents/beeai.md": [ + "docs/docs/using/agents/bee.md": [ { "hashed_secret": "5546721ffdfc2e5b0e4c0da38f10774f9ad50b09", "is_secret": false, "is_verified": false, - "line_number": 35, + "line_number": 39, "type": "Secret Keyword", "verified_result": null }, { - "hashed_secret": "ec545cfb40ff2f1b4eb959dbd0e3177236c540fa", + "hashed_secret": "be0e0678e7132e8f991d463818e1befbaf3aad9d", "is_secret": false, "is_verified": false, - "line_number": 203, + "line_number": 61, "type": "Secret Keyword", "verified_result": null } @@ -3938,7 +4206,7 @@ "hashed_secret": "1e5c2f367f02e47a8c160cda1cd9d91decbac441", "is_secret": false, "is_verified": false, - "line_number": 200, + "line_number": 172, "type": "Secret Keyword", "verified_result": null } @@ -4215,30 +4483,12 @@ "verified_result": null } ], - "mcpgateway/alembic/versions/e28cd485ad3c_remove_global_unique_constraints_for_.py": [ - { - "hashed_secret": "64589d1d5776faad3fc219e30c877e7ab8abb551", - "is_secret": false, - "is_verified": false, - "line_number": 33, - "type": "Hex High Entropy String", - "verified_result": null - }, - { - "hashed_secret": "5dbedebc3423bc4336c49a36bd75fe7d370c5176", - "is_secret": false, - "is_verified": false, - "line_number": 34, - "type": "Hex High Entropy String", - "verified_result": null - } - ], "mcpgateway/common/validators.py": [ { "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 751, + "line_number": 753, "type": "Hex High Entropy String", "verified_result": null } @@ -4274,7 +4524,7 @@ "hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe", "is_secret": false, "is_verified": false, - "line_number": 738, + "line_number": 828, "type": "Secret Keyword", "verified_result": null } @@ -4284,7 +4534,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4419, + "line_number": 4409, "type": "Hex High Entropy String", "verified_result": null } @@ -4304,7 +4554,7 @@ "hashed_secret": "3ee08a29a2ca26171fe152b8741d0c90b598263a", "is_secret": false, "is_verified": false, - "line_number": 15016, + "line_number": 15019, "type": "Secret Keyword", "verified_result": null } @@ -4954,9 +5204,17 @@ "hashed_secret": "1b255e8e77fb3fafbf1fe2013347f762fbca9cd6", "is_secret": false, "is_verified": false, - "line_number": 18, + "line_number": 17, "type": "Secret Keyword", "verified_result": null + }, + { + "hashed_secret": "1b255e8e77fb3fafbf1fe2013347f762fbca9cd6", + "is_secret": false, + "is_verified": false, + "line_number": 34, + "type": "Basic Auth Credentials", + "verified_result": null } ], "tests/performance/MANUAL_TESTING.md": [ @@ -5066,7 +5324,7 @@ "hashed_secret": "e360adbe2fa9bb945b9afc993ea1306cb976e947", "is_secret": false, "is_verified": false, - "line_number": 213, + "line_number": 212, "type": "Secret Keyword", "verified_result": null }, @@ -5074,7 +5332,7 @@ "hashed_secret": "50e8361b5726f72eed9dffaec824570b635147b6", "is_secret": false, "is_verified": false, - "line_number": 246, + "line_number": 245, "type": "Secret Keyword", "verified_result": null }, @@ -5082,7 +5340,7 @@ "hashed_secret": "fe1bae27cb7c1fb823f496f286e78f1d2ae87734", "is_secret": false, "is_verified": false, - "line_number": 319, + "line_number": 318, "type": "Secret Keyword", "verified_result": null } @@ -5112,7 +5370,7 @@ "hashed_secret": "995e87a207ec41ef95f938dcf53a4184312b0d93", "is_secret": false, "is_verified": false, - "line_number": 11423, + "line_number": 11379, "type": "Hex High Entropy String", "verified_result": null }, @@ -5120,7 +5378,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 17840, + "line_number": 17773, "type": "Secret Keyword", "verified_result": null } @@ -5130,7 +5388,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 235, + "line_number": 228, "type": "Basic Auth Credentials", "verified_result": null } @@ -5140,7 +5398,7 @@ "hashed_secret": "4f13f134744a2fadbbe2d624687246347d12fa63", "is_secret": false, "is_verified": false, - "line_number": 2770, + "line_number": 2733, "type": "Hex High Entropy String", "verified_result": null } diff --git a/mcpgateway/main.py b/mcpgateway/main.py index 481206f00a..96c1d67646 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -12344,7 +12344,7 @@ async def cleanup_import_statuses(max_age_hours: int = 24, user=Depends(get_curr # Lazy import: mcpgateway.admin is a large module (~19k lines, ~120ms cold). # Only load it when the admin API is actually mounted. # First-Party - from mcpgateway.admin import admin_router, enforce_admin_csrf, set_logging_service, validate_section_permissions # pylint: disable=import-outside-toplevel + from mcpgateway.admin import enforce_admin_csrf, validate_section_permissions # pylint: disable=import-outside-toplevel set_logging_service(logging_service) app.include_router(admin_router) # Admin routes imported from admin.py diff --git a/mcpgateway/routers/openapi_schema_router.py b/mcpgateway/routers/openapi_schema_router.py index 186410adc2..351ed2808b 100644 --- a/mcpgateway/routers/openapi_schema_router.py +++ b/mcpgateway/routers/openapi_schema_router.py @@ -40,20 +40,20 @@ async def generate_schema_from_openapi( ) -> JSONResponse: """ Generate input_schema and output_schema from OpenAPI specification. - + This endpoint is part of the versioned REST API and does not require admin UI access. It delegates to the same service logic as the admin endpoint but without CSRF protection. - + Expects JSON body with: - url: The tool URL (e.g., http://localhost:8100/calculate) - request_type: HTTP method (GET, POST, etc.) - defaults to GET - openapi_url: (optional) Direct OpenAPI spec URL - + Args: request: FastAPI Request object containing JSON body _user: Authenticated user from RBAC dependency - + Returns: JSONResponse with generated schemas or error message. """ @@ -65,34 +65,34 @@ async def generate_schema_from_openapi( content={"message": "Invalid JSON in request body", "success": False}, status_code=400, ) - + if not isinstance(body, dict): return ORJSONResponse( content={"message": "Request body must be a JSON object", "success": False}, status_code=400, ) - + # Extract and validate fields tool_url = body.get("url", "") request_type = body.get("request_type", "GET") openapi_url = body.get("openapi_url", "") - + if not isinstance(tool_url, str) or not isinstance(request_type, str) or not isinstance(openapi_url, str): return ORJSONResponse( content={"message": "'url', 'request_type', and 'openapi_url' must be strings", "success": False}, status_code=400, ) - + tool_url = tool_url.strip() request_type = request_type.strip() openapi_url = openapi_url.strip() - + if not tool_url: return ORJSONResponse( content={"message": "'url' is required to identify the API path and base URL", "success": False}, status_code=400, ) - + # Security validation try: SecurityValidator.validate_url(tool_url, "Tool URL") @@ -101,12 +101,12 @@ async def generate_schema_from_openapi( content={"message": str(e), "success": False}, status_code=400, ) - + # Parse URL to extract base and path parsed = urllib.parse.urlparse(tool_url) base_url = f"{parsed.scheme}://{parsed.netloc}" tool_path = parsed.path - + # Fetch and extract schemas try: input_schema, output_schema, spec_url = await fetch_and_extract_schemas( @@ -144,7 +144,7 @@ async def generate_schema_from_openapi( content={"message": "An unexpected error occurred while processing the OpenAPI spec", "success": False}, status_code=500, ) - + return ORJSONResponse( content={ "message": "Schemas generated successfully from OpenAPI spec", diff --git a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py index ccec2d9714..fcadf40b1d 100644 --- a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py +++ b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py @@ -55,18 +55,18 @@ def _mock_request(body): async def test_generate_schema_success_all_fields(): """Valid request with all fields returns schemas successfully.""" request = _mock_request(b'{"url": "http://api.example.com/calculate", "request_type": "POST", "openapi_url": "http://api.example.com/openapi.json"}') - + mock_schemas = ( {"type": "object", "properties": {"a": {"type": "number"}}}, {"type": "object", "properties": {"result": {"type": "number"}}}, "http://api.example.com/openapi.json", ) - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 200 content = response.body assert b'"success":true' in content @@ -78,18 +78,18 @@ async def test_generate_schema_success_all_fields(): async def test_generate_schema_success_minimal_fields(): """Valid request with minimal fields applies defaults.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + mock_schemas = ( {"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json", ) - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 200 # Verify default request_type="GET" was used mock_fetch.assert_called_once() @@ -101,18 +101,18 @@ async def test_generate_schema_success_minimal_fields(): async def test_generate_schema_auto_discovery(): """Empty openapi_url triggers auto-discovery.""" request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') - + mock_schemas = ( {"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json", ) - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 200 # Verify empty openapi_url was passed (triggers auto-discovery in service) call_kwargs = mock_fetch.call_args[1] @@ -128,9 +128,9 @@ async def test_generate_schema_auto_discovery(): async def test_generate_schema_invalid_json(): """Invalid JSON body returns 400.""" request = _mock_request(b'{invalid json}') - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -141,9 +141,9 @@ async def test_generate_schema_invalid_json(): async def test_generate_schema_non_dict_body(): """Non-dict JSON body returns 400.""" request = _mock_request(b'["array", "not", "dict"]') - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -154,9 +154,9 @@ async def test_generate_schema_non_dict_body(): async def test_generate_schema_missing_url(): """Missing url field returns 400.""" request = _mock_request(b'{"request_type": "POST"}') - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -167,9 +167,9 @@ async def test_generate_schema_missing_url(): async def test_generate_schema_empty_url(): """Empty url field returns 400.""" request = _mock_request(b'{"url": " "}') - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -180,9 +180,9 @@ async def test_generate_schema_empty_url(): async def test_generate_schema_non_string_fields(): """Non-string field types return 400.""" request = _mock_request(b'{"url": 123, "request_type": true, "openapi_url": null}') - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -193,12 +193,12 @@ async def test_generate_schema_non_string_fields(): async def test_generate_schema_invalid_url_format(): """Invalid URL format returns 400 from security validation.""" request = _mock_request(b'{"url": "not-a-valid-url"}') - + with patch("mcpgateway.routers.openapi_schema_router.SecurityValidator.validate_url") as mock_validate: mock_validate.side_effect = ValueError("Invalid URL format") - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -214,12 +214,12 @@ async def test_generate_schema_invalid_url_format(): async def test_generate_schema_security_validation_error(): """ValueError from security validation returns 400.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = ValueError("Security validation failed: blocked domain") - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 400 content = response.body assert b'"success":false' in content @@ -230,12 +230,12 @@ async def test_generate_schema_security_validation_error(): async def test_generate_schema_path_not_found(): """KeyError (path/method not found) returns 404.""" request = _mock_request(b'{"url": "http://api.example.com/nonexistent"}') - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = KeyError("Path /nonexistent not found in OpenAPI spec") - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 404 content = response.body assert b'"success":false' in content @@ -245,15 +245,15 @@ async def test_generate_schema_path_not_found(): async def test_generate_schema_http_status_error(): """httpx.HTTPStatusError returns 502.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + mock_response = MagicMock() mock_response.status_code = 404 - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = httpx.HTTPStatusError("Not Found", request=MagicMock(), response=mock_response) - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 502 content = response.body assert b'"success":false' in content @@ -264,12 +264,12 @@ async def test_generate_schema_http_status_error(): async def test_generate_schema_http_error(): """httpx.HTTPError returns 502.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = httpx.HTTPError("Connection failed") - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 502 content = response.body assert b'"success":false' in content @@ -280,12 +280,12 @@ async def test_generate_schema_http_error(): async def test_generate_schema_generic_exception(): """Generic Exception returns 500.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = Exception("Unexpected error") - + response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + assert response.status_code == 500 content = response.body assert b'"success":false' in content @@ -301,14 +301,14 @@ async def test_generate_schema_generic_exception(): async def test_generate_schema_request_type_defaults_to_get(): """request_type defaults to GET when omitted.""" request = _mock_request(b'{"url": "http://api.example.com/calculate"}') - + mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + call_kwargs = mock_fetch.call_args[1] assert call_kwargs["method"] == "GET" @@ -317,14 +317,14 @@ async def test_generate_schema_request_type_defaults_to_get(): async def test_generate_schema_openapi_url_can_be_empty(): """openapi_url can be empty (triggers auto-discovery).""" request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') - + mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + call_kwargs = mock_fetch.call_args[1] assert call_kwargs["openapi_url"] == "" @@ -338,14 +338,14 @@ async def test_generate_schema_openapi_url_can_be_empty(): async def test_generate_schema_url_parsing(): """URL is correctly parsed into base_url and path.""" request = _mock_request(b'{"url": "https://api.example.com:8080/v1/calculate"}') - + mock_schemas = ({"type": "object"}, {"type": "object"}, "https://api.example.com:8080/openapi.json") - + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - + await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - + call_kwargs = mock_fetch.call_args[1] assert call_kwargs["base_url"] == "https://api.example.com:8080" assert call_kwargs["path"] == "/v1/calculate" From 4334344557bdde55a3d4f3a002d1e3da9e5d0c48 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Tue, 16 Jun 2026 19:04:55 +0100 Subject: [PATCH 03/12] fixed precommit and ruff Signed-off-by: Jitesh Nair --- .secrets.baseline | 474 ++++++++------------------------------------- mcpgateway/main.py | 3 +- 2 files changed, 83 insertions(+), 394 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 89349c5c93..d9f70b76d4 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-15T14:20:15Z", + "generated_at": "2026-06-16T17:54:16Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -100,7 +100,7 @@ "hashed_secret": "548a5cde15c93a9e50e12e4cba776b85f6befc83", "is_secret": false, "is_verified": false, - "line_number": 72, + "line_number": 75, "type": "Secret Keyword", "verified_result": null }, @@ -108,7 +108,7 @@ "hashed_secret": "8e9a8667ded25c805ce3dc920a5f2343bd2d198f", "is_secret": false, "is_verified": false, - "line_number": 73, + "line_number": 76, "type": "Secret Keyword", "verified_result": null }, @@ -116,7 +116,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 690, + "line_number": 702, "type": "Basic Auth Credentials", "verified_result": null }, @@ -124,7 +124,7 @@ "hashed_secret": "14f8aa3e560a47851908ab0f04ec856dbc512d93", "is_secret": false, "is_verified": false, - "line_number": 918, + "line_number": 930, "type": "Secret Keyword", "verified_result": null }, @@ -132,7 +132,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1205, + "line_number": 1217, "type": "Secret Keyword", "verified_result": null }, @@ -140,7 +140,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 1231, + "line_number": 1243, "type": "Secret Keyword", "verified_result": null }, @@ -148,7 +148,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 1239, + "line_number": 1251, "type": "Secret Keyword", "verified_result": null }, @@ -156,7 +156,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 1318, + "line_number": 1330, "type": "Secret Keyword", "verified_result": null }, @@ -164,7 +164,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 1323, + "line_number": 1335, "type": "Secret Keyword", "verified_result": null }, @@ -172,7 +172,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 1328, + "line_number": 1340, "type": "Secret Keyword", "verified_result": null }, @@ -180,7 +180,7 @@ "hashed_secret": "cb32747fcfb55eaa194c8cd8e4ba7d49ada08a94", "is_secret": false, "is_verified": false, - "line_number": 1334, + "line_number": 1346, "type": "Secret Keyword", "verified_result": null }, @@ -188,7 +188,7 @@ "hashed_secret": "6c178d51b13520496dbc767ed3d9d7aa5803ac72", "is_secret": false, "is_verified": false, - "line_number": 1346, + "line_number": 1358, "type": "Secret Keyword", "verified_result": null }, @@ -196,7 +196,7 @@ "hashed_secret": "ca45060a53fd8a255d1a83ee8d2f025283ccc66e", "is_secret": false, "is_verified": false, - "line_number": 1364, + "line_number": 1376, "type": "Secret Keyword", "verified_result": null }, @@ -204,7 +204,7 @@ "hashed_secret": "910fbf00f58e9bcb095ea26a75cc1d9a3355e671", "is_secret": false, "is_verified": false, - "line_number": 1425, + "line_number": 1437, "type": "Secret Keyword", "verified_result": null } @@ -274,7 +274,7 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, - "line_number": 1336, + "line_number": 1479, "type": "Secret Keyword", "verified_result": null }, @@ -282,7 +282,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1424, + "line_number": 1567, "type": "Secret Keyword", "verified_result": null }, @@ -290,7 +290,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 1774, + "line_number": 1917, "type": "Basic Auth Credentials", "verified_result": null }, @@ -298,7 +298,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3140, + "line_number": 3283, "type": "Basic Auth Credentials", "verified_result": null }, @@ -306,7 +306,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3231, + "line_number": 3374, "type": "Secret Keyword", "verified_result": null }, @@ -314,7 +314,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 3274, + "line_number": 3417, "type": "Secret Keyword", "verified_result": null }, @@ -322,7 +322,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 3279, + "line_number": 3422, "type": "Secret Keyword", "verified_result": null }, @@ -330,7 +330,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 3284, + "line_number": 3427, "type": "Secret Keyword", "verified_result": null } @@ -350,7 +350,7 @@ "hashed_secret": "844c398e469ef3fb919da3778944365ab2175fb7", "is_secret": false, "is_verified": false, - "line_number": 530, + "line_number": 525, "type": "Secret Keyword", "verified_result": null }, @@ -358,7 +358,7 @@ "hashed_secret": "319037749ce37e577db0b3628c7f90e333544391", "is_secret": false, "is_verified": false, - "line_number": 967, + "line_number": 962, "type": "Secret Keyword", "verified_result": null }, @@ -366,7 +366,7 @@ "hashed_secret": "6ae2832e494d1098e8901fe156083e39399a24f1", "is_secret": false, "is_verified": false, - "line_number": 969, + "line_number": 964, "type": "Secret Keyword", "verified_result": null }, @@ -374,7 +374,7 @@ "hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b", "is_secret": false, "is_verified": false, - "line_number": 1621, + "line_number": 1616, "type": "Secret Keyword", "verified_result": null }, @@ -382,7 +382,7 @@ "hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50", "is_secret": false, "is_verified": false, - "line_number": 1840, + "line_number": 1835, "type": "Secret Keyword", "verified_result": null }, @@ -390,7 +390,7 @@ "hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674", "is_secret": false, "is_verified": false, - "line_number": 6386, + "line_number": 6382, "type": "Secret Keyword", "verified_result": null }, @@ -398,7 +398,7 @@ "hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb", "is_secret": false, "is_verified": false, - "line_number": 6883, + "line_number": 6879, "type": "Secret Keyword", "verified_result": null }, @@ -406,7 +406,7 @@ "hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513", "is_secret": false, "is_verified": false, - "line_number": 6895, + "line_number": 6891, "type": "Secret Keyword", "verified_result": null }, @@ -414,7 +414,7 @@ "hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a", "is_secret": false, "is_verified": false, - "line_number": 6967, + "line_number": 6963, "type": "Secret Keyword", "verified_result": null }, @@ -422,7 +422,7 @@ "hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690", "is_secret": false, "is_verified": false, - "line_number": 8048, + "line_number": 8045, "type": "Secret Keyword", "verified_result": null } @@ -703,196 +703,6 @@ "verified_result": null } ], - "client/src/hooks/useToolForm.ts": [ - { - "hashed_secret": "85d1e045b6ed7751f763f058a68da53ea26dbd4d", - "is_secret": false, - "is_verified": false, - "line_number": 271, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/en-US/auth.json": [ - { - "hashed_secret": "3a6ed43bb0d761d8137363ec5e194a5158115cf3", - "is_secret": false, - "is_verified": false, - "line_number": 18, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "b387e03f480bde661b1440f78acd7db569aea7f0", - "is_secret": false, - "is_verified": false, - "line_number": 19, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "a2c6b77a1f4f9da20c536a5c7177a008338948fd", - "is_secret": false, - "is_verified": false, - "line_number": 20, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "8800ddc46c3185561435ff69924b0d06bd30c73b", - "is_secret": false, - "is_verified": false, - "line_number": 23, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/en-US/users.json": [ - { - "hashed_secret": "a2c6b77a1f4f9da20c536a5c7177a008338948fd", - "is_secret": false, - "is_verified": false, - "line_number": 13, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/es-ES/auth.json": [ - { - "hashed_secret": "8c31b65bdecdc9f18b695d7318186fd1feed690d", - "is_secret": false, - "is_verified": false, - "line_number": 4, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "123ba0dd5f78127af4473d02cd1b18114ef52ab2", - "is_secret": false, - "is_verified": false, - "line_number": 7, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "b47f38ba10b4be808e0ed4eee32a54098905a48d", - "is_secret": false, - "is_verified": false, - "line_number": 18, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "c7cf571452d242282fa5fd6625ec338224a94891", - "is_secret": false, - "is_verified": false, - "line_number": 19, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", - "is_secret": false, - "is_verified": false, - "line_number": 20, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "0c819b12ff43d593e0bd136c9b0e477c08f7c313", - "is_secret": false, - "is_verified": false, - "line_number": 23, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/es-ES/users.json": [ - { - "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", - "is_secret": false, - "is_verified": false, - "line_number": 13, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "8c31b65bdecdc9f18b695d7318186fd1feed690d", - "is_secret": false, - "is_verified": false, - "line_number": 28, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/pt-BR/auth.json": [ - { - "hashed_secret": "7751a23fa55170a57e90374df13a3ab78efe0e99", - "is_secret": false, - "is_verified": false, - "line_number": 4, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "379b6f5adbaaf331096aa110bb196aef8c38d73a", - "is_secret": false, - "is_verified": false, - "line_number": 7, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "61769dd26d3b921cc7a1625b9785b101ba4ed135", - "is_secret": false, - "is_verified": false, - "line_number": 18, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "f2b368852b6112ad05424113abf098723a7b75cb", - "is_secret": false, - "is_verified": false, - "line_number": 19, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", - "is_secret": false, - "is_verified": false, - "line_number": 20, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "5a1e0088a6bda02a394bff63b95ae21229882c4f", - "is_secret": false, - "is_verified": false, - "line_number": 23, - "type": "Secret Keyword", - "verified_result": null - } - ], - "client/src/i18n/locales/pt-BR/users.json": [ - { - "hashed_secret": "b02e1938dabf1a93a2f5890dfe6aa0ff2e23d7de", - "is_secret": false, - "is_verified": false, - "line_number": 13, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "7751a23fa55170a57e90374df13a3ab78efe0e99", - "is_secret": false, - "is_verified": false, - "line_number": 28, - "type": "Secret Keyword", - "verified_result": null - } - ], "crates/mcp_runtime/tests/runtime.rs": [ { "hashed_secret": "5b204323030835cdda5d258742d1452e812988de", @@ -1272,7 +1082,7 @@ "hashed_secret": "293324f6824bb3a6db5c4dc42a60ddd4a9851c99", "is_secret": false, "is_verified": false, - "line_number": 2190, + "line_number": 2189, "type": "Hex High Entropy String", "verified_result": null }, @@ -1280,7 +1090,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 2647, + "line_number": 2649, "type": "Secret Keyword", "verified_result": null }, @@ -1288,7 +1098,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 2647, + "line_number": 2649, "type": "Base64 High Entropy String", "verified_result": null }, @@ -1296,7 +1106,7 @@ "hashed_secret": "64f5490a2808803712ef99d9dfb8bc3ea5a15077", "is_secret": false, "is_verified": false, - "line_number": 2660, + "line_number": 2662, "type": "Secret Keyword", "verified_result": null }, @@ -1304,7 +1114,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 2808, + "line_number": 2810, "type": "Secret Keyword", "verified_result": null }, @@ -1312,7 +1122,7 @@ "hashed_secret": "12be9f7db42eb4a2d881a99fa9ba847e1f83677f", "is_secret": false, "is_verified": false, - "line_number": 2829, + "line_number": 2831, "type": "Secret Keyword", "verified_result": null }, @@ -1320,7 +1130,7 @@ "hashed_secret": "c3de40d5e3fc71ed62771c2127a8e42585026c97", "is_secret": false, "is_verified": false, - "line_number": 2837, + "line_number": 2839, "type": "Secret Keyword", "verified_result": null }, @@ -1328,7 +1138,7 @@ "hashed_secret": "ce53277317aa3cd27c1619d7d371306ded2ddd1e", "is_secret": false, "is_verified": false, - "line_number": 2842, + "line_number": 2844, "type": "Secret Keyword", "verified_result": null } @@ -1564,123 +1374,11 @@ } ], "docs/docs/architecture/roadmap.md": [ - { - "hashed_secret": "e62854369199d65b6a66cd90fe8203ac4d5be26c", - "is_secret": false, - "is_verified": false, - "line_number": 190, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "35722442567571107241e7e6ca2b554515e485da", - "is_secret": false, - "is_verified": false, - "line_number": 338, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "0608c4054662dd902e1314f7e450e3eaa81c1143", - "is_secret": false, - "is_verified": false, - "line_number": 340, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "176939638191d5a13935ccb90c0c8a70a5e30773", - "is_secret": false, - "is_verified": false, - "line_number": 347, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "564289a5f34546401bbc727c20a14069761f9143", - "is_secret": false, - "is_verified": false, - "line_number": 420, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "37745ed7a0f005fb14522c5cc7c1ba3d9e0df579", - "is_secret": false, - "is_verified": false, - "line_number": 424, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "0c8207b92779c0a42507a293a23106814cc381e1", - "is_secret": false, - "is_verified": false, - "line_number": 427, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "ce5070ef8a0cf3e595ae4cf3f73f6a0eca87e346", - "is_secret": false, - "is_verified": false, - "line_number": 429, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "46a808cfd5beafa5e60aefee867bf92025dc2849", - "is_secret": false, - "is_verified": false, - "line_number": 434, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "e4ae36d7ffd9dd805a909ac5a20de165a2629ba4", - "is_secret": false, - "is_verified": false, - "line_number": 435, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "9f08d40159a4b035fc7998b398bd16b38f91e806", - "is_secret": false, - "is_verified": false, - "line_number": 440, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "61a135089eac561a2ff7cedeefb03975bed000f8", - "is_secret": false, - "is_verified": false, - "line_number": 633, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "5bdcd3c0d4d24ae3e71b3b452a024c6324c7e4bb", - "is_secret": false, - "is_verified": false, - "line_number": 645, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "a4b3ee46e1384e5b0be8cfdb2169977d8a160136", - "is_secret": false, - "is_verified": false, - "line_number": 650, - "type": "Secret Keyword", - "verified_result": null - }, { "hashed_secret": "ff2ccd73154c1c71ef9a2982fe16e3c529e7a1ae", "is_secret": false, "is_verified": false, - "line_number": 745, + "line_number": 104, "type": "Secret Keyword", "verified_result": null }, @@ -1688,7 +1386,7 @@ "hashed_secret": "74f82b992f8a92b849c2be77447f98a510338333", "is_secret": false, "is_verified": false, - "line_number": 746, + "line_number": 105, "type": "Secret Keyword", "verified_result": null }, @@ -1696,7 +1394,7 @@ "hashed_secret": "64814a3b7fd8444a56ad3641fd3451c6deaf0757", "is_secret": false, "is_verified": false, - "line_number": 754, + "line_number": 113, "type": "Secret Keyword", "verified_result": null }, @@ -1704,7 +1402,7 @@ "hashed_secret": "af84d91fde168566c7dc18f3121ea2fbe651af1f", "is_secret": false, "is_verified": false, - "line_number": 783, + "line_number": 142, "type": "Secret Keyword", "verified_result": null }, @@ -1712,7 +1410,7 @@ "hashed_secret": "d1081e351fbebe0deb0c2867d5f731c8f9cc3fd8", "is_secret": false, "is_verified": false, - "line_number": 1036, + "line_number": 395, "type": "Secret Keyword", "verified_result": null }, @@ -1720,7 +1418,7 @@ "hashed_secret": "b7e9a6e2e04ed3edbf8b4e21def4beccbb6eb6b1", "is_secret": false, "is_verified": false, - "line_number": 1037, + "line_number": 396, "type": "Secret Keyword", "verified_result": null }, @@ -1728,7 +1426,7 @@ "hashed_secret": "6d244f58364223afcc2322f696137b01ece78d9d", "is_secret": false, "is_verified": false, - "line_number": 1038, + "line_number": 397, "type": "Secret Keyword", "verified_result": null }, @@ -1736,7 +1434,7 @@ "hashed_secret": "8bf177a72c93155020ebca9ee7f3c6e0fbdee946", "is_secret": false, "is_verified": false, - "line_number": 1056, + "line_number": 415, "type": "Secret Keyword", "verified_result": null }, @@ -1744,7 +1442,7 @@ "hashed_secret": "c41e6cd4245e404f07635fdbac351aad4d54a213", "is_secret": false, "is_verified": false, - "line_number": 1155, + "line_number": 514, "type": "Secret Keyword", "verified_result": null }, @@ -1752,7 +1450,7 @@ "hashed_secret": "6568a9761e26d16a111247f279b6fcabff1c8c86", "is_secret": false, "is_verified": false, - "line_number": 1190, + "line_number": 549, "type": "Secret Keyword", "verified_result": null }, @@ -1760,7 +1458,7 @@ "hashed_secret": "314fc7fd580f8c510be196143946bbe5ea753443", "is_secret": false, "is_verified": false, - "line_number": 1191, + "line_number": 550, "type": "Secret Keyword", "verified_result": null }, @@ -1768,7 +1466,7 @@ "hashed_secret": "afb9d1dcf212fa33d079a070ab90f0bef03c324b", "is_secret": false, "is_verified": false, - "line_number": 1205, + "line_number": 564, "type": "Secret Keyword", "verified_result": null }, @@ -1776,7 +1474,7 @@ "hashed_secret": "2736fab291f04e69b62d490c3c09361f5b82461a", "is_secret": false, "is_verified": false, - "line_number": 1212, + "line_number": 571, "type": "Secret Keyword", "verified_result": null }, @@ -1784,7 +1482,7 @@ "hashed_secret": "7542c8f677ffbbe75a5301a5c76f88401dc42897", "is_secret": false, "is_verified": false, - "line_number": 1221, + "line_number": 580, "type": "Secret Keyword", "verified_result": null }, @@ -1792,7 +1490,7 @@ "hashed_secret": "ca20728d35f00c3a4c21b1fc8b0086b59f89df2d", "is_secret": false, "is_verified": false, - "line_number": 1226, + "line_number": 585, "type": "Secret Keyword", "verified_result": null }, @@ -1800,7 +1498,7 @@ "hashed_secret": "5b7dcd14a4faa2cdd54cf6eb8d4bc35da31914a1", "is_secret": false, "is_verified": false, - "line_number": 1238, + "line_number": 597, "type": "Secret Keyword", "verified_result": null }, @@ -1808,7 +1506,7 @@ "hashed_secret": "245dbfa0498aaa6898fe57a191a5b3130466a943", "is_secret": false, "is_verified": false, - "line_number": 1259, + "line_number": 618, "type": "Secret Keyword", "verified_result": null }, @@ -1816,7 +1514,7 @@ "hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997", "is_secret": false, "is_verified": false, - "line_number": 1261, + "line_number": 620, "type": "Secret Keyword", "verified_result": null }, @@ -1824,7 +1522,7 @@ "hashed_secret": "ee977806d7286510da8b9a7492ba58e2484c0ecc", "is_secret": false, "is_verified": false, - "line_number": 1264, + "line_number": 623, "type": "Secret Keyword", "verified_result": null }, @@ -1832,7 +1530,7 @@ "hashed_secret": "a603075604516de626cda903868e457fad826533", "is_secret": false, "is_verified": false, - "line_number": 1271, + "line_number": 630, "type": "Secret Keyword", "verified_result": null }, @@ -1840,7 +1538,7 @@ "hashed_secret": "95ef18f58f32e3821ec7e627af6ee4757f68760e", "is_secret": false, "is_verified": false, - "line_number": 1272, + "line_number": 631, "type": "Secret Keyword", "verified_result": null }, @@ -1848,7 +1546,7 @@ "hashed_secret": "b3aca92c793ee0e9b1a9b0a5f5fc044e05140df3", "is_secret": false, "is_verified": false, - "line_number": 1310, + "line_number": 669, "type": "Secret Keyword", "verified_result": null }, @@ -1856,7 +1554,7 @@ "hashed_secret": "ae21c64a87f6bb0b8e16e55c48be4cc638d7bd3f", "is_secret": false, "is_verified": false, - "line_number": 1321, + "line_number": 680, "type": "Secret Keyword", "verified_result": null }, @@ -1864,7 +1562,7 @@ "hashed_secret": "58d1bbce297de3c304a9fefc3b483181872a5c6b", "is_secret": false, "is_verified": false, - "line_number": 1323, + "line_number": 682, "type": "Secret Keyword", "verified_result": null }, @@ -1872,7 +1570,7 @@ "hashed_secret": "de93ba2d15f3dbf65f76e6fd21e1e4b002459dd8", "is_secret": false, "is_verified": false, - "line_number": 1355, + "line_number": 714, "type": "Secret Keyword", "verified_result": null }, @@ -1880,7 +1578,7 @@ "hashed_secret": "533739bb9d1dad53e71d8c93200cc2510d442226", "is_secret": false, "is_verified": false, - "line_number": 1358, + "line_number": 717, "type": "Secret Keyword", "verified_result": null } @@ -2474,7 +2172,7 @@ "hashed_secret": "ad7920eddff0914889d74a47bb45fc80697da5a8", "is_secret": false, "is_verified": false, - "line_number": 268, + "line_number": 265, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2482,7 +2180,7 @@ "hashed_secret": "998ab9e14841d778dcafdf72ceaac0cacbb5ea2a", "is_secret": false, "is_verified": false, - "line_number": 399, + "line_number": 396, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2490,7 +2188,7 @@ "hashed_secret": "d20047e4dff166045c7f61066c62955ec33c1657", "is_secret": false, "is_verified": false, - "line_number": 602, + "line_number": 599, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2498,7 +2196,7 @@ "hashed_secret": "fc19bda36b0af2d6a957034199a79c6583c707a8", "is_secret": false, "is_verified": false, - "line_number": 763, + "line_number": 760, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2506,7 +2204,7 @@ "hashed_secret": "848f72afea73993a89ad3dcd66fc152f04f2be41", "is_secret": false, "is_verified": false, - "line_number": 931, + "line_number": 928, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2514,7 +2212,7 @@ "hashed_secret": "5193dda17da630c041fe949c06e7b6dd5ac8628f", "is_secret": false, "is_verified": false, - "line_number": 1208, + "line_number": 1205, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2522,15 +2220,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1329, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6", - "is_secret": false, - "is_verified": false, - "line_number": 1578, + "line_number": 1326, "type": "Secret Keyword", "verified_result": null } @@ -3034,7 +2724,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 428, + "line_number": 320, "type": "Secret Keyword", "verified_result": null } @@ -4524,7 +4214,7 @@ "hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe", "is_secret": false, "is_verified": false, - "line_number": 828, + "line_number": 741, "type": "Secret Keyword", "verified_result": null } @@ -4534,7 +4224,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4409, + "line_number": 4403, "type": "Hex High Entropy String", "verified_result": null } @@ -4554,7 +4244,7 @@ "hashed_secret": "3ee08a29a2ca26171fe152b8741d0c90b598263a", "is_secret": false, "is_verified": false, - "line_number": 15019, + "line_number": 15017, "type": "Secret Keyword", "verified_result": null } @@ -5324,7 +5014,7 @@ "hashed_secret": "e360adbe2fa9bb945b9afc993ea1306cb976e947", "is_secret": false, "is_verified": false, - "line_number": 212, + "line_number": 213, "type": "Secret Keyword", "verified_result": null }, @@ -5332,7 +5022,7 @@ "hashed_secret": "50e8361b5726f72eed9dffaec824570b635147b6", "is_secret": false, "is_verified": false, - "line_number": 245, + "line_number": 246, "type": "Secret Keyword", "verified_result": null }, @@ -5340,7 +5030,7 @@ "hashed_secret": "fe1bae27cb7c1fb823f496f286e78f1d2ae87734", "is_secret": false, "is_verified": false, - "line_number": 318, + "line_number": 319, "type": "Secret Keyword", "verified_result": null } @@ -5388,7 +5078,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 228, + "line_number": 235, "type": "Basic Auth Credentials", "verified_result": null } diff --git a/mcpgateway/main.py b/mcpgateway/main.py index 96c1d67646..c655f18dae 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -12344,9 +12344,8 @@ async def cleanup_import_statuses(max_age_hours: int = 24, user=Depends(get_curr # Lazy import: mcpgateway.admin is a large module (~19k lines, ~120ms cold). # Only load it when the admin API is actually mounted. # First-Party - from mcpgateway.admin import enforce_admin_csrf, validate_section_permissions # pylint: disable=import-outside-toplevel + from mcpgateway.admin import admin_router, enforce_admin_csrf, validate_section_permissions # pylint: disable=import-outside-toplevel - set_logging_service(logging_service) app.include_router(admin_router) # Admin routes imported from admin.py # Validate section-to-permission mapping consistency at startup From 00e967ed9b8e17f32b51f8ceac3f9874fa528d72 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Wed, 17 Jun 2026 15:06:02 +0100 Subject: [PATCH 04/12] made changes based on the comments made by reviewrs Signed-off-by: Jitesh Nair --- .secrets.baseline | 2 +- mcpgateway/main.py | 4 +- mcpgateway/routers/openapi_schema_router.py | 71 ++----- .../routers/test_openapi_schema_router.py | 186 ++++++++++-------- 4 files changed, 126 insertions(+), 137 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index d9f70b76d4..8828a1c22e 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-16T17:54:16Z", + "generated_at": "2026-06-17T14:01:31Z", "plugins_used": [ { "name": "AWSKeyDetector" diff --git a/mcpgateway/main.py b/mcpgateway/main.py index c655f18dae..11b19a4e61 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -12103,7 +12103,6 @@ async def cleanup_import_statuses(max_age_hours: int = 24, user=Depends(get_curr app.include_router(tag_router) app.include_router(export_import_router) app.include_router(openapi_schema_router) -logger.info("OpenAPI schema generation router included") # Compliance report router (admin API) if settings.mcpgateway_admin_api_enabled: @@ -12344,8 +12343,9 @@ async def cleanup_import_statuses(max_age_hours: int = 24, user=Depends(get_curr # Lazy import: mcpgateway.admin is a large module (~19k lines, ~120ms cold). # Only load it when the admin API is actually mounted. # First-Party - from mcpgateway.admin import admin_router, enforce_admin_csrf, validate_section_permissions # pylint: disable=import-outside-toplevel + from mcpgateway.admin import admin_router, enforce_admin_csrf, set_logging_service, validate_section_permissions # pylint: disable=import-outside-toplevel + set_logging_service(logging_service) app.include_router(admin_router) # Admin routes imported from admin.py # Validate section-to-permission mapping consistency at startup diff --git a/mcpgateway/routers/openapi_schema_router.py b/mcpgateway/routers/openapi_schema_router.py index 351ed2808b..6702f34df0 100644 --- a/mcpgateway/routers/openapi_schema_router.py +++ b/mcpgateway/routers/openapi_schema_router.py @@ -16,12 +16,12 @@ import urllib.parse # Third-Party -from fastapi import APIRouter, Depends, Request +from fastapi import APIRouter, Body, Depends from fastapi.responses import JSONResponse import httpx +from pydantic import BaseModel, Field # First-Party -from mcpgateway.admin import _read_request_json from mcpgateway.common.validators import SecurityValidator from mcpgateway.middleware.rbac import get_current_user_with_permissions, require_permission from mcpgateway.services.openapi_service import fetch_and_extract_schemas @@ -32,10 +32,18 @@ logger = logging.getLogger(__name__) -@router.post("/generate-schema-from-openapi") +class GenerateSchemaRequest(BaseModel): + """Request body for generating schemas from OpenAPI specification.""" + + url: str = Field(..., description="The tool URL (e.g., http://localhost:8100/calculate)") + request_type: str = Field("GET", description="HTTP method (GET, POST, etc.)") + openapi_url: str = Field("", description="Direct OpenAPI spec URL (optional, auto-discovered if empty)") + + +@router.post("/generate-schemas-from-openapi") @require_permission("tools.create", allow_admin_bypass=False) -async def generate_schema_from_openapi( - request: Request, +async def generate_schemas_from_openapi( + body: GenerateSchemaRequest = Body(...), _user=Depends(get_current_user_with_permissions), ) -> JSONResponse: """ @@ -45,57 +53,16 @@ async def generate_schema_from_openapi( admin UI access. It delegates to the same service logic as the admin endpoint but without CSRF protection. - Expects JSON body with: - - url: The tool URL (e.g., http://localhost:8100/calculate) - - request_type: HTTP method (GET, POST, etc.) - defaults to GET - - openapi_url: (optional) Direct OpenAPI spec URL - Args: - request: FastAPI Request object containing JSON body + body: Request body with url, request_type, and openapi_url _user: Authenticated user from RBAC dependency Returns: JSONResponse with generated schemas or error message. """ - # Read and validate request body - try: - body = await _read_request_json(request) - except Exception: - return ORJSONResponse( - content={"message": "Invalid JSON in request body", "success": False}, - status_code=400, - ) - - if not isinstance(body, dict): - return ORJSONResponse( - content={"message": "Request body must be a JSON object", "success": False}, - status_code=400, - ) - - # Extract and validate fields - tool_url = body.get("url", "") - request_type = body.get("request_type", "GET") - openapi_url = body.get("openapi_url", "") - - if not isinstance(tool_url, str) or not isinstance(request_type, str) or not isinstance(openapi_url, str): - return ORJSONResponse( - content={"message": "'url', 'request_type', and 'openapi_url' must be strings", "success": False}, - status_code=400, - ) - - tool_url = tool_url.strip() - request_type = request_type.strip() - openapi_url = openapi_url.strip() - - if not tool_url: - return ORJSONResponse( - content={"message": "'url' is required to identify the API path and base URL", "success": False}, - status_code=400, - ) - # Security validation try: - SecurityValidator.validate_url(tool_url, "Tool URL") + SecurityValidator.validate_url(body.url, "Tool URL") except ValueError as e: return ORJSONResponse( content={"message": str(e), "success": False}, @@ -103,7 +70,7 @@ async def generate_schema_from_openapi( ) # Parse URL to extract base and path - parsed = urllib.parse.urlparse(tool_url) + parsed = urllib.parse.urlparse(body.url) base_url = f"{parsed.scheme}://{parsed.netloc}" tool_path = parsed.path @@ -112,8 +79,8 @@ async def generate_schema_from_openapi( input_schema, output_schema, spec_url = await fetch_and_extract_schemas( base_url=base_url, path=tool_path, - method=request_type, - openapi_url=openapi_url, + method=body.request_type, + openapi_url=body.openapi_url or "", timeout=10.0, ) except ValueError as e: @@ -155,5 +122,3 @@ async def generate_schema_from_openapi( }, status_code=200, ) - -# Made with Bob diff --git a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py index fcadf40b1d..f55dbe07fa 100644 --- a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py +++ b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py @@ -6,8 +6,8 @@ Unit tests for the OpenAPI schema generation router. Tests cover: - - POST /v1/tools/generate-schema-from-openapi: success, validation, error mapping - - RBAC enforcement + - POST /v1/tools/generate-schemas-from-openapi: success, validation, error mapping + - RBAC enforcement (with real permission check) - Default value handling """ @@ -15,7 +15,7 @@ from unittest.mock import AsyncMock, MagicMock, patch # Third-Party -from fastapi import status +from fastapi import HTTPException import httpx import pytest @@ -39,11 +39,11 @@ def _mock_user(): return {"email": "test@example.com", "is_admin": False} -def _mock_request(body): - """Create mock FastAPI Request with JSON body.""" - request = MagicMock() - request.body = AsyncMock(return_value=body) - return request +def _create_body(url, request_type="GET", openapi_url=""): + """Create GenerateSchemaRequest body object.""" + from mcpgateway.routers.openapi_schema_router import GenerateSchemaRequest + + return GenerateSchemaRequest(url=url, request_type=request_type, openapi_url=openapi_url) # --------------------------------------------------------------------------- @@ -54,7 +54,11 @@ def _mock_request(body): @pytest.mark.asyncio async def test_generate_schema_success_all_fields(): """Valid request with all fields returns schemas successfully.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate", "request_type": "POST", "openapi_url": "http://api.example.com/openapi.json"}') + body = _create_body( + url="http://api.example.com/calculate", + request_type="POST", + openapi_url="http://api.example.com/openapi.json", + ) mock_schemas = ( {"type": "object", "properties": {"a": {"type": "number"}}}, @@ -65,7 +69,7 @@ async def test_generate_schema_success_all_fields(): with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 200 content = response.body @@ -77,7 +81,7 @@ async def test_generate_schema_success_all_fields(): @pytest.mark.asyncio async def test_generate_schema_success_minimal_fields(): """Valid request with minimal fields applies defaults.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") mock_schemas = ( {"type": "object"}, @@ -88,7 +92,7 @@ async def test_generate_schema_success_minimal_fields(): with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 200 # Verify default request_type="GET" was used @@ -100,7 +104,7 @@ async def test_generate_schema_success_minimal_fields(): @pytest.mark.asyncio async def test_generate_schema_auto_discovery(): """Empty openapi_url triggers auto-discovery.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') + body = _create_body(url="http://api.example.com/calculate", openapi_url="") mock_schemas = ( {"type": "object"}, @@ -111,7 +115,7 @@ async def test_generate_schema_auto_discovery(): with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 200 # Verify empty openapi_url was passed (triggers auto-discovery in service) @@ -126,78 +130,52 @@ async def test_generate_schema_auto_discovery(): @pytest.mark.asyncio async def test_generate_schema_invalid_json(): - """Invalid JSON body returns 400.""" - request = _mock_request(b'{invalid json}') - - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - - assert response.status_code == 400 - content = response.body - assert b'"success":false' in content - assert b'"message":"Invalid JSON in request body"' in content - - -@pytest.mark.asyncio -async def test_generate_schema_non_dict_body(): - """Non-dict JSON body returns 400.""" - request = _mock_request(b'["array", "not", "dict"]') - - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + """Invalid JSON body returns 422 (Pydantic validation error).""" + # Pydantic validation happens before the function is called + # This test verifies that invalid input is rejected by FastAPI + # In practice, FastAPI returns 422 for validation errors + # We test this by attempting to create an invalid body object + with pytest.raises(Exception): # Pydantic will raise validation error + from mcpgateway.routers.openapi_schema_router import GenerateSchemaRequest - assert response.status_code == 400 - content = response.body - assert b'"success":false' in content - assert b'"message":"Request body must be a JSON object"' in content + GenerateSchemaRequest.model_validate_json('{invalid json}') @pytest.mark.asyncio async def test_generate_schema_missing_url(): - """Missing url field returns 400.""" - request = _mock_request(b'{"request_type": "POST"}') - - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + """Missing url field returns validation error.""" + # Pydantic validation happens before the function is called + with pytest.raises(Exception): # Pydantic will raise validation error + from mcpgateway.routers.openapi_schema_router import GenerateSchemaRequest - assert response.status_code == 400 - content = response.body - assert b'"success":false' in content - assert b"'url' is required" in content + GenerateSchemaRequest(request_type="POST") # Missing required 'url' @pytest.mark.asyncio async def test_generate_schema_empty_url(): - """Empty url field returns 400.""" - request = _mock_request(b'{"url": " "}') - - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) - - assert response.status_code == 400 - content = response.body - assert b'"success":false' in content - assert b"'url' is required" in content - + """Empty url field is accepted by Pydantic but rejected by security validation.""" + body = _create_body(url=" ") -@pytest.mark.asyncio -async def test_generate_schema_non_string_fields(): - """Non-string field types return 400.""" - request = _mock_request(b'{"url": 123, "request_type": true, "openapi_url": null}') + # Empty URL passes Pydantic but fails security validation + with patch("mcpgateway.routers.openapi_schema_router.SecurityValidator.validate_url") as mock_validate: + mock_validate.side_effect = ValueError("URL cannot be empty") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) - assert response.status_code == 400 - content = response.body - assert b'"success":false' in content - assert b"must be strings" in content + assert response.status_code == 400 + content = response.body + assert b'"success":false' in content @pytest.mark.asyncio async def test_generate_schema_invalid_url_format(): """Invalid URL format returns 400 from security validation.""" - request = _mock_request(b'{"url": "not-a-valid-url"}') + body = _create_body(url="not-a-valid-url") with patch("mcpgateway.routers.openapi_schema_router.SecurityValidator.validate_url") as mock_validate: mock_validate.side_effect = ValueError("Invalid URL format") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 400 content = response.body @@ -213,12 +191,12 @@ async def test_generate_schema_invalid_url_format(): @pytest.mark.asyncio async def test_generate_schema_security_validation_error(): """ValueError from security validation returns 400.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = ValueError("Security validation failed: blocked domain") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 400 content = response.body @@ -229,12 +207,12 @@ async def test_generate_schema_security_validation_error(): @pytest.mark.asyncio async def test_generate_schema_path_not_found(): """KeyError (path/method not found) returns 404.""" - request = _mock_request(b'{"url": "http://api.example.com/nonexistent"}') + body = _create_body(url="http://api.example.com/nonexistent") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = KeyError("Path /nonexistent not found in OpenAPI spec") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 404 content = response.body @@ -244,7 +222,7 @@ async def test_generate_schema_path_not_found(): @pytest.mark.asyncio async def test_generate_schema_http_status_error(): """httpx.HTTPStatusError returns 502.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") mock_response = MagicMock() mock_response.status_code = 404 @@ -252,7 +230,7 @@ async def test_generate_schema_http_status_error(): with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = httpx.HTTPStatusError("Not Found", request=MagicMock(), response=mock_response) - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 502 content = response.body @@ -263,12 +241,12 @@ async def test_generate_schema_http_status_error(): @pytest.mark.asyncio async def test_generate_schema_http_error(): """httpx.HTTPError returns 502.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = httpx.HTTPError("Connection failed") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 502 content = response.body @@ -279,12 +257,12 @@ async def test_generate_schema_http_error(): @pytest.mark.asyncio async def test_generate_schema_generic_exception(): """Generic Exception returns 500.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.side_effect = Exception("Unexpected error") - response = await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + response = await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) assert response.status_code == 500 content = response.body @@ -300,14 +278,14 @@ async def test_generate_schema_generic_exception(): @pytest.mark.asyncio async def test_generate_schema_request_type_defaults_to_get(): """request_type defaults to GET when omitted.""" - request = _mock_request(b'{"url": "http://api.example.com/calculate"}') + body = _create_body(url="http://api.example.com/calculate") mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) call_kwargs = mock_fetch.call_args[1] assert call_kwargs["method"] == "GET" @@ -316,14 +294,14 @@ async def test_generate_schema_request_type_defaults_to_get(): @pytest.mark.asyncio async def test_generate_schema_openapi_url_can_be_empty(): """openapi_url can be empty (triggers auto-discovery).""" - request = _mock_request(b'{"url": "http://api.example.com/calculate", "openapi_url": ""}') + body = _create_body(url="http://api.example.com/calculate", openapi_url="") mock_schemas = ({"type": "object"}, {"type": "object"}, "http://api.example.com/openapi.json") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) call_kwargs = mock_fetch.call_args[1] assert call_kwargs["openapi_url"] == "" @@ -337,17 +315,63 @@ async def test_generate_schema_openapi_url_can_be_empty(): @pytest.mark.asyncio async def test_generate_schema_url_parsing(): """URL is correctly parsed into base_url and path.""" - request = _mock_request(b'{"url": "https://api.example.com:8080/v1/calculate"}') + body = _create_body(url="https://api.example.com:8080/v1/calculate") mock_schemas = ({"type": "object"}, {"type": "object"}, "https://api.example.com:8080/openapi.json") with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: mock_fetch.return_value = mock_schemas - await router_mod.generate_schema_from_openapi(request, _user=_mock_user()) + await router_mod.generate_schemas_from_openapi(body, _user=_mock_user()) call_kwargs = mock_fetch.call_args[1] assert call_kwargs["base_url"] == "https://api.example.com:8080" assert call_kwargs["path"] == "/v1/calculate" -# Made with Bob + +# --------------------------------------------------------------------------- +# RBAC Tests (Real Permission Check) +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_generate_schemas_403_when_permission_denied(monkeypatch: pytest.MonkeyPatch): + """Endpoint returns 403 when user lacks tools.create permission. + + This test temporarily restores the real RBAC decorators to verify + the endpoint correctly enforces the tools.create permission. + """ + # Restore real decorators for this test + from tests.utils.rbac_mocks import restore_rbac_decorators + restore_rbac_decorators(_originals) + + # Patch PermissionService to deny all permissions + class DenyAll: + def __init__(self, _db): + pass + + async def check_permission(self, **_kwargs): + return False + + monkeypatch.setattr("mcpgateway.middleware.rbac.PermissionService", DenyAll) + + # Re-import the router module to pick up the restored decorators + import importlib + importlib.reload(router_mod) + + # Create test data + body = router_mod.GenerateSchemaRequest(url="http://api.example.com/calculate") + non_admin_user = {"email": "user@example.com", "is_admin": False, "ip_address": "127.0.0.1", "user_agent": "tests"} + + # The @require_permission decorator should raise HTTPException with 403 + # when PermissionService.check_permission returns False + with pytest.raises(HTTPException) as exc: + await router_mod.generate_schemas_from_openapi(body, _user=non_admin_user) + + assert exc.value.status_code == 403 + assert "access denied" in str(exc.value.detail).lower() + + # Re-patch decorators for remaining tests + from tests.utils.rbac_mocks import patch_rbac_decorators + patch_rbac_decorators() + importlib.reload(router_mod) From 0f7582bcbb0b6808dcdc145a6ea0de2c14530665 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Wed, 17 Jun 2026 15:32:48 +0100 Subject: [PATCH 05/12] fixed precommit Signed-off-by: Jitesh Nair --- .secrets.baseline | 108 +++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 8828a1c22e..a9489d41b8 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-17T14:01:31Z", + "generated_at": "2026-06-17T14:21:03Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -116,7 +116,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 702, + "line_number": 708, "type": "Basic Auth Credentials", "verified_result": null }, @@ -124,7 +124,7 @@ "hashed_secret": "14f8aa3e560a47851908ab0f04ec856dbc512d93", "is_secret": false, "is_verified": false, - "line_number": 930, + "line_number": 936, "type": "Secret Keyword", "verified_result": null }, @@ -132,7 +132,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1217, + "line_number": 1222, "type": "Secret Keyword", "verified_result": null }, @@ -140,7 +140,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 1243, + "line_number": 1248, "type": "Secret Keyword", "verified_result": null }, @@ -148,7 +148,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 1251, + "line_number": 1256, "type": "Secret Keyword", "verified_result": null }, @@ -156,7 +156,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 1330, + "line_number": 1335, "type": "Secret Keyword", "verified_result": null }, @@ -164,7 +164,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 1335, + "line_number": 1340, "type": "Secret Keyword", "verified_result": null }, @@ -172,7 +172,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 1340, + "line_number": 1345, "type": "Secret Keyword", "verified_result": null }, @@ -180,7 +180,7 @@ "hashed_secret": "cb32747fcfb55eaa194c8cd8e4ba7d49ada08a94", "is_secret": false, "is_verified": false, - "line_number": 1346, + "line_number": 1351, "type": "Secret Keyword", "verified_result": null }, @@ -188,7 +188,7 @@ "hashed_secret": "6c178d51b13520496dbc767ed3d9d7aa5803ac72", "is_secret": false, "is_verified": false, - "line_number": 1358, + "line_number": 1363, "type": "Secret Keyword", "verified_result": null }, @@ -196,7 +196,7 @@ "hashed_secret": "ca45060a53fd8a255d1a83ee8d2f025283ccc66e", "is_secret": false, "is_verified": false, - "line_number": 1376, + "line_number": 1381, "type": "Secret Keyword", "verified_result": null }, @@ -204,7 +204,7 @@ "hashed_secret": "910fbf00f58e9bcb095ea26a75cc1d9a3355e671", "is_secret": false, "is_verified": false, - "line_number": 1437, + "line_number": 1442, "type": "Secret Keyword", "verified_result": null } @@ -274,7 +274,7 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, - "line_number": 1479, + "line_number": 1485, "type": "Secret Keyword", "verified_result": null }, @@ -282,7 +282,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1567, + "line_number": 1573, "type": "Secret Keyword", "verified_result": null }, @@ -290,7 +290,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 1917, + "line_number": 1923, "type": "Basic Auth Credentials", "verified_result": null }, @@ -298,7 +298,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3283, + "line_number": 3289, "type": "Basic Auth Credentials", "verified_result": null }, @@ -306,7 +306,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3374, + "line_number": 3380, "type": "Secret Keyword", "verified_result": null }, @@ -314,7 +314,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 3417, + "line_number": 3423, "type": "Secret Keyword", "verified_result": null }, @@ -322,7 +322,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 3422, + "line_number": 3428, "type": "Secret Keyword", "verified_result": null }, @@ -330,7 +330,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 3427, + "line_number": 3433, "type": "Secret Keyword", "verified_result": null } @@ -432,7 +432,7 @@ "hashed_secret": "b7991211e1e73c924ee8febee2d1e80f0173c762", "is_secret": false, "is_verified": false, - "line_number": 131, + "line_number": 132, "type": "Secret Keyword", "verified_result": null }, @@ -440,7 +440,7 @@ "hashed_secret": "108b310facc1a193833fc2971fd83081f775ea0c", "is_secret": false, "is_verified": false, - "line_number": 238, + "line_number": 239, "type": "Secret Keyword", "verified_result": null }, @@ -448,7 +448,7 @@ "hashed_secret": "5d4e7584ece9047cd53ae0e4b40726328968ce68", "is_secret": false, "is_verified": false, - "line_number": 326, + "line_number": 327, "type": "Hex High Entropy String", "verified_result": null }, @@ -456,7 +456,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 443, + "line_number": 444, "type": "Secret Keyword", "verified_result": null }, @@ -464,7 +464,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 443, + "line_number": 444, "type": "Base64 High Entropy String", "verified_result": null }, @@ -472,7 +472,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 531, + "line_number": 532, "type": "Secret Keyword", "verified_result": null }, @@ -480,7 +480,7 @@ "hashed_secret": "5546721ffdfc2e5b0e4c0da38f10774f9ad50b09", "is_secret": false, "is_verified": false, - "line_number": 627, + "line_number": 628, "type": "Secret Keyword", "verified_result": null }, @@ -488,7 +488,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 698, + "line_number": 699, "type": "Basic Auth Credentials", "verified_result": null }, @@ -496,7 +496,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 704, + "line_number": 705, "type": "Secret Keyword", "verified_result": null } @@ -986,7 +986,7 @@ "hashed_secret": "b6f3674b78a02881de33177e6f6fb8b851e0101c", "is_secret": false, "is_verified": false, - "line_number": 269, + "line_number": 270, "type": "Secret Keyword", "verified_result": null }, @@ -994,7 +994,7 @@ "hashed_secret": "108b310facc1a193833fc2971fd83081f775ea0c", "is_secret": false, "is_verified": false, - "line_number": 364, + "line_number": 365, "type": "Secret Keyword", "verified_result": null }, @@ -1002,7 +1002,7 @@ "hashed_secret": "a6e38ae8688f390d45039a635c394dea67b9bc41", "is_secret": false, "is_verified": false, - "line_number": 412, + "line_number": 413, "type": "Secret Keyword", "verified_result": null }, @@ -1010,7 +1010,7 @@ "hashed_secret": "bba409d5cd2d77fe052d5ecc39b35f167641118c", "is_secret": false, "is_verified": false, - "line_number": 420, + "line_number": 421, "type": "Secret Keyword", "verified_result": null }, @@ -1018,7 +1018,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 468, + "line_number": 469, "type": "Secret Keyword", "verified_result": null }, @@ -1026,7 +1026,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 1004, + "line_number": 1005, "type": "Secret Keyword", "verified_result": null }, @@ -1034,7 +1034,7 @@ "hashed_secret": "bd0160c2cf35d950843c88f3be2b9412ed71f485", "is_secret": false, "is_verified": false, - "line_number": 1107, + "line_number": 1108, "type": "Secret Keyword", "verified_result": null }, @@ -1042,7 +1042,7 @@ "hashed_secret": "266bda40a4eed34ae3cead41cb813b8f94dc6f27", "is_secret": false, "is_verified": false, - "line_number": 1113, + "line_number": 1114, "type": "Secret Keyword", "verified_result": null }, @@ -1050,7 +1050,7 @@ "hashed_secret": "3879c93c04cab8707ab8ab6a4f97d51ea8fb6f47", "is_secret": false, "is_verified": false, - "line_number": 1222, + "line_number": 1223, "type": "Secret Keyword", "verified_result": null }, @@ -1058,7 +1058,7 @@ "hashed_secret": "756fa1fd4f0c6d5249cc2ad68d5a5a6bfe96aacd", "is_secret": false, "is_verified": false, - "line_number": 1257, + "line_number": 1258, "type": "Secret Keyword", "verified_result": null }, @@ -1066,7 +1066,7 @@ "hashed_secret": "2df14e4719f299249cd9a97cf68cc87232a27cbb", "is_secret": false, "is_verified": false, - "line_number": 1796, + "line_number": 1797, "type": "Hex High Entropy String", "verified_result": null }, @@ -1074,7 +1074,7 @@ "hashed_secret": "0772e9ff96270d7e9a41cef301e135da6f74a8fc", "is_secret": false, "is_verified": false, - "line_number": 1873, + "line_number": 1874, "type": "Hex High Entropy String", "verified_result": null }, @@ -1082,7 +1082,7 @@ "hashed_secret": "293324f6824bb3a6db5c4dc42a60ddd4a9851c99", "is_secret": false, "is_verified": false, - "line_number": 2189, + "line_number": 2190, "type": "Hex High Entropy String", "verified_result": null }, @@ -1090,7 +1090,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 2649, + "line_number": 2650, "type": "Secret Keyword", "verified_result": null }, @@ -1098,7 +1098,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 2649, + "line_number": 2650, "type": "Base64 High Entropy String", "verified_result": null }, @@ -1106,7 +1106,7 @@ "hashed_secret": "64f5490a2808803712ef99d9dfb8bc3ea5a15077", "is_secret": false, "is_verified": false, - "line_number": 2662, + "line_number": 2663, "type": "Secret Keyword", "verified_result": null }, @@ -1114,7 +1114,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 2810, + "line_number": 2811, "type": "Secret Keyword", "verified_result": null }, @@ -1122,7 +1122,7 @@ "hashed_secret": "12be9f7db42eb4a2d881a99fa9ba847e1f83677f", "is_secret": false, "is_verified": false, - "line_number": 2831, + "line_number": 2832, "type": "Secret Keyword", "verified_result": null }, @@ -1130,7 +1130,7 @@ "hashed_secret": "c3de40d5e3fc71ed62771c2127a8e42585026c97", "is_secret": false, "is_verified": false, - "line_number": 2839, + "line_number": 2840, "type": "Secret Keyword", "verified_result": null }, @@ -1138,7 +1138,7 @@ "hashed_secret": "ce53277317aa3cd27c1619d7d371306ded2ddd1e", "is_secret": false, "is_verified": false, - "line_number": 2844, + "line_number": 2845, "type": "Secret Keyword", "verified_result": null } @@ -4224,7 +4224,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4403, + "line_number": 4411, "type": "Hex High Entropy String", "verified_result": null } @@ -4244,7 +4244,7 @@ "hashed_secret": "3ee08a29a2ca26171fe152b8741d0c90b598263a", "is_secret": false, "is_verified": false, - "line_number": 15017, + "line_number": 15016, "type": "Secret Keyword", "verified_result": null } @@ -5060,7 +5060,7 @@ "hashed_secret": "995e87a207ec41ef95f938dcf53a4184312b0d93", "is_secret": false, "is_verified": false, - "line_number": 11379, + "line_number": 11389, "type": "Hex High Entropy String", "verified_result": null }, @@ -5068,7 +5068,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 17773, + "line_number": 17785, "type": "Secret Keyword", "verified_result": null } From 1e6f7b13740970e46e799260365d97f83b952750 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Mon, 22 Jun 2026 13:30:59 +0100 Subject: [PATCH 06/12] fixed precommit Signed-off-by: Jitesh Nair --- .secrets.baseline | 48 +++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index a9489d41b8..8546a54f9e 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-17T14:21:03Z", + "generated_at": "2026-06-22T12:12:01Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -2180,7 +2180,7 @@ "hashed_secret": "998ab9e14841d778dcafdf72ceaac0cacbb5ea2a", "is_secret": false, "is_verified": false, - "line_number": 396, + "line_number": 467, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2188,7 +2188,7 @@ "hashed_secret": "d20047e4dff166045c7f61066c62955ec33c1657", "is_secret": false, "is_verified": false, - "line_number": 599, + "line_number": 670, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2196,7 +2196,7 @@ "hashed_secret": "fc19bda36b0af2d6a957034199a79c6583c707a8", "is_secret": false, "is_verified": false, - "line_number": 760, + "line_number": 831, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2204,7 +2204,7 @@ "hashed_secret": "848f72afea73993a89ad3dcd66fc152f04f2be41", "is_secret": false, "is_verified": false, - "line_number": 928, + "line_number": 999, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2212,7 +2212,7 @@ "hashed_secret": "5193dda17da630c041fe949c06e7b6dd5ac8628f", "is_secret": false, "is_verified": false, - "line_number": 1205, + "line_number": 1276, "type": "Base64 High Entropy String", "verified_result": null }, @@ -2220,7 +2220,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1326, + "line_number": 1397, "type": "Secret Keyword", "verified_result": null } @@ -2276,7 +2276,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 146, + "line_number": 151, "type": "Secret Keyword", "verified_result": null }, @@ -2284,7 +2284,7 @@ "hashed_secret": "bc2f74c22f98f7b6ffbc2f67453dbfa99bce9a32", "is_secret": false, "is_verified": false, - "line_number": 427, + "line_number": 432, "type": "Secret Keyword", "verified_result": null }, @@ -2292,7 +2292,7 @@ "hashed_secret": "a2166e0b243f4e192e14000a6aa8f46cde6bfc20", "is_secret": false, "is_verified": false, - "line_number": 793, + "line_number": 798, "type": "Secret Keyword", "verified_result": null }, @@ -2300,7 +2300,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1275, + "line_number": 1280, "type": "Basic Auth Credentials", "verified_result": null }, @@ -2308,7 +2308,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1279, + "line_number": 1284, "type": "Secret Keyword", "verified_result": null } @@ -4224,7 +4224,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 4411, + "line_number": 4419, "type": "Hex High Entropy String", "verified_result": null } @@ -4894,15 +4894,17 @@ "hashed_secret": "1b255e8e77fb3fafbf1fe2013347f762fbca9cd6", "is_secret": false, "is_verified": false, - "line_number": 17, + "line_number": 18, "type": "Secret Keyword", "verified_result": null - }, + } + ], + "tests/migration/utils/container_manager.py": [ { "hashed_secret": "1b255e8e77fb3fafbf1fe2013347f762fbca9cd6", "is_secret": false, "is_verified": false, - "line_number": 34, + "line_number": 409, "type": "Basic Auth Credentials", "verified_result": null } @@ -4999,6 +5001,16 @@ "verified_result": null } ], + "tests/unit/mcpgateway/services/test_gateway_service.py": [ + { + "hashed_secret": "3654bd5ef523a79741766b7c3f22228fd43c3836", + "is_secret": false, + "is_verified": false, + "line_number": 8137, + "type": "Secret Keyword", + "verified_result": null + } + ], "tests/unit/mcpgateway/services/test_mcp_client_chat_service_extended.py": [ { "hashed_secret": "d576acab7ea77edc8aa4f9ebea6bd4c1cc0d1764", @@ -5060,7 +5072,7 @@ "hashed_secret": "995e87a207ec41ef95f938dcf53a4184312b0d93", "is_secret": false, "is_verified": false, - "line_number": 11389, + "line_number": 11421, "type": "Hex High Entropy String", "verified_result": null }, @@ -5068,7 +5080,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 17785, + "line_number": 17838, "type": "Secret Keyword", "verified_result": null } From 444294c9e453603de37cba735c8f782e5ca89b96 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Thu, 25 Jun 2026 12:06:10 +0100 Subject: [PATCH 07/12] fixed tests and changelog updates Signed-off-by: Jitesh Nair --- CHANGELOG.md | 4 ++ .../routers/test_openapi_schema_router.py | 60 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b65d9263fc..842ba2803a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## [Unreleased] +### Added + +- New REST API endpoint `POST /v1/tools/generate-schemas-from-openapi` for generating MCP tool schemas from OpenAPI specifications without admin UI dependencies (#5142) + ### Deprecation Notice - Rust MCP runtime sidecar, Rust A2A runtime sidecar, and ValidationMiddleware are deprecated as of 2026-06-11 and will sunset on 2026-07-07. Use the Python MCP transport path, the Python A2A invocation path, and endpoint-level Pydantic or protocol-specific validation instead. See [Deprecations](docs/docs/deprecations.md). diff --git a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py index f55dbe07fa..f9df490a43 100644 --- a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py +++ b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py @@ -375,3 +375,63 @@ async def check_permission(self, **_kwargs): from tests.utils.rbac_mocks import patch_rbac_decorators patch_rbac_decorators() importlib.reload(router_mod) + + +# --------------------------------------------------------------------------- +# TestClient Integration Tests +# --------------------------------------------------------------------------- + + +def test_endpoint_via_testclient_validates_route_registration(): + """Verify route is registered and accessible via ASGI app without CSRF token. + + This test confirms: + 1. Route registration works when the router is mounted on a FastAPI app + 2. Pydantic validation returns 422 for invalid input (not 400) + 3. No CSRF token is required (unlike admin endpoint) + 4. Valid request with mocked service returns 200 + """ + from fastapi import FastAPI + from fastapi.testclient import TestClient + from mcpgateway.middleware.rbac import get_current_user_with_permissions + from mcpgateway.routers.openapi_schema_router import router as schema_router + + test_app = FastAPI() + test_app.include_router(schema_router) + + async def mock_user(): + return {"email": "test@example.com", "is_admin": False} + + test_app.dependency_overrides[get_current_user_with_permissions] = mock_user + + client = TestClient(test_app, raise_server_exceptions=False) + + # Test 1: Invalid request body (missing required 'url') should return 422 (Pydantic validation) + response = client.post( + "/v1/tools/generate-schemas-from-openapi", + json={"invalid": "data"}, + ) + assert response.status_code == 422, f"Expected 422, got {response.status_code}" + assert "detail" in response.json() + + # Test 2: Valid request returns 200 without CSRF token + with patch("mcpgateway.routers.openapi_schema_router.fetch_and_extract_schemas", new_callable=AsyncMock) as mock_fetch: + mock_fetch.return_value = ( + {"type": "object", "properties": {"a": {"type": "number"}}}, + {"type": "object", "properties": {"result": {"type": "number"}}}, + "http://api.example.com/openapi.json", + ) + with patch("mcpgateway.routers.openapi_schema_router.SecurityValidator.validate_url"): + response = client.post( + "/v1/tools/generate-schemas-from-openapi", + json={ + "url": "http://api.example.com/calculate", + "request_type": "GET", + "openapi_url": "http://api.example.com/openapi.json", + }, + ) + assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" + data = response.json() + assert data["success"] is True + assert data["message"] == "Schemas generated successfully from OpenAPI spec" + # Confirm no CSRF token was needed (request succeeded without X-CSRF-Token header) From f6b9ac5155f4376ec03951c71b4b536c9905e70d Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Thu, 25 Jun 2026 15:30:14 +0100 Subject: [PATCH 08/12] fixed pre-commit Signed-off-by: Jitesh Nair --- .secrets.baseline | 156 ++++++++---------- ...c_remove_global_unique_constraints_for_.py | 4 +- 2 files changed, 70 insertions(+), 90 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 8546a54f9e..164a06fc2f 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-22T12:12:01Z", + "generated_at": "2026-06-25T14:26:15Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -274,7 +274,7 @@ "hashed_secret": "b4673e578b9b30fe8bba1b555b7b59883444c697", "is_secret": false, "is_verified": false, - "line_number": 1485, + "line_number": 1594, "type": "Secret Keyword", "verified_result": null }, @@ -282,7 +282,7 @@ "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", "is_secret": false, "is_verified": false, - "line_number": 1573, + "line_number": 1682, "type": "Secret Keyword", "verified_result": null }, @@ -290,7 +290,7 @@ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_secret": false, "is_verified": false, - "line_number": 1923, + "line_number": 2032, "type": "Basic Auth Credentials", "verified_result": null }, @@ -298,7 +298,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3289, + "line_number": 3398, "type": "Basic Auth Credentials", "verified_result": null }, @@ -306,7 +306,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 3380, + "line_number": 3489, "type": "Secret Keyword", "verified_result": null }, @@ -314,7 +314,7 @@ "hashed_secret": "ac371b6dcce28a86c90d12bc57d946a800eebf17", "is_secret": false, "is_verified": false, - "line_number": 3423, + "line_number": 3532, "type": "Secret Keyword", "verified_result": null }, @@ -322,7 +322,7 @@ "hashed_secret": "0b6ec68df700dec4dcd64babd0eda1edccddace1", "is_secret": false, "is_verified": false, - "line_number": 3428, + "line_number": 3537, "type": "Secret Keyword", "verified_result": null }, @@ -330,7 +330,7 @@ "hashed_secret": "4ad6f0082ee224001beb3ca5c3e81c8ceea5ed86", "is_secret": false, "is_verified": false, - "line_number": 3433, + "line_number": 3542, "type": "Secret Keyword", "verified_result": null } @@ -390,7 +390,7 @@ "hashed_secret": "d3ac7a4ef1a838b4134f2f6e7f3c0d249d74b674", "is_secret": false, "is_verified": false, - "line_number": 6382, + "line_number": 6052, "type": "Secret Keyword", "verified_result": null }, @@ -398,7 +398,7 @@ "hashed_secret": "5932862bcd24dd27d0dc0407ec94fe9d6ea24aeb", "is_secret": false, "is_verified": false, - "line_number": 6879, + "line_number": 6549, "type": "Secret Keyword", "verified_result": null }, @@ -406,7 +406,7 @@ "hashed_secret": "c77c805e32f173e4321ee9187de9c29cb3804513", "is_secret": false, "is_verified": false, - "line_number": 6891, + "line_number": 6561, "type": "Secret Keyword", "verified_result": null }, @@ -414,7 +414,7 @@ "hashed_secret": "8fe3df8a68ddd0d4ab2214186cbb8e38ccd0e06a", "is_secret": false, "is_verified": false, - "line_number": 6963, + "line_number": 6633, "type": "Secret Keyword", "verified_result": null }, @@ -422,7 +422,7 @@ "hashed_secret": "93ac8946882128457cd9e283b30ca851945e6690", "is_secret": false, "is_verified": false, - "line_number": 8045, + "line_number": 7715, "type": "Secret Keyword", "verified_result": null } @@ -1002,7 +1002,7 @@ "hashed_secret": "a6e38ae8688f390d45039a635c394dea67b9bc41", "is_secret": false, "is_verified": false, - "line_number": 413, + "line_number": 415, "type": "Secret Keyword", "verified_result": null }, @@ -1010,7 +1010,7 @@ "hashed_secret": "bba409d5cd2d77fe052d5ecc39b35f167641118c", "is_secret": false, "is_verified": false, - "line_number": 421, + "line_number": 423, "type": "Secret Keyword", "verified_result": null }, @@ -1018,7 +1018,7 @@ "hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841", "is_secret": false, "is_verified": false, - "line_number": 469, + "line_number": 471, "type": "Secret Keyword", "verified_result": null }, @@ -1026,7 +1026,7 @@ "hashed_secret": "08cd923367890009657eab812753379bdb321eeb", "is_secret": false, "is_verified": false, - "line_number": 1005, + "line_number": 1007, "type": "Secret Keyword", "verified_result": null }, @@ -1034,7 +1034,7 @@ "hashed_secret": "bd0160c2cf35d950843c88f3be2b9412ed71f485", "is_secret": false, "is_verified": false, - "line_number": 1108, + "line_number": 1110, "type": "Secret Keyword", "verified_result": null }, @@ -1042,7 +1042,7 @@ "hashed_secret": "266bda40a4eed34ae3cead41cb813b8f94dc6f27", "is_secret": false, "is_verified": false, - "line_number": 1114, + "line_number": 1116, "type": "Secret Keyword", "verified_result": null }, @@ -1050,7 +1050,7 @@ "hashed_secret": "3879c93c04cab8707ab8ab6a4f97d51ea8fb6f47", "is_secret": false, "is_verified": false, - "line_number": 1223, + "line_number": 1225, "type": "Secret Keyword", "verified_result": null }, @@ -1058,7 +1058,7 @@ "hashed_secret": "756fa1fd4f0c6d5249cc2ad68d5a5a6bfe96aacd", "is_secret": false, "is_verified": false, - "line_number": 1258, + "line_number": 1260, "type": "Secret Keyword", "verified_result": null }, @@ -1066,7 +1066,7 @@ "hashed_secret": "2df14e4719f299249cd9a97cf68cc87232a27cbb", "is_secret": false, "is_verified": false, - "line_number": 1797, + "line_number": 1799, "type": "Hex High Entropy String", "verified_result": null }, @@ -1074,7 +1074,7 @@ "hashed_secret": "0772e9ff96270d7e9a41cef301e135da6f74a8fc", "is_secret": false, "is_verified": false, - "line_number": 1874, + "line_number": 1876, "type": "Hex High Entropy String", "verified_result": null }, @@ -1082,7 +1082,7 @@ "hashed_secret": "293324f6824bb3a6db5c4dc42a60ddd4a9851c99", "is_secret": false, "is_verified": false, - "line_number": 2190, + "line_number": 2192, "type": "Hex High Entropy String", "verified_result": null }, @@ -1090,7 +1090,7 @@ "hashed_secret": "543d4766b92de8d18b1899c24e825638fd2a2bb7", "is_secret": false, "is_verified": false, - "line_number": 2650, + "line_number": 2673, "type": "Secret Keyword", "verified_result": null }, @@ -1098,7 +1098,7 @@ "hashed_secret": "76a5af8c9ee406ff91da84664bc6f58cacb2ad76", "is_secret": false, "is_verified": false, - "line_number": 2650, + "line_number": 2673, "type": "Base64 High Entropy String", "verified_result": null }, @@ -1106,7 +1106,7 @@ "hashed_secret": "64f5490a2808803712ef99d9dfb8bc3ea5a15077", "is_secret": false, "is_verified": false, - "line_number": 2663, + "line_number": 2686, "type": "Secret Keyword", "verified_result": null }, @@ -1114,7 +1114,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 2811, + "line_number": 2834, "type": "Secret Keyword", "verified_result": null }, @@ -1122,7 +1122,7 @@ "hashed_secret": "12be9f7db42eb4a2d881a99fa9ba847e1f83677f", "is_secret": false, "is_verified": false, - "line_number": 2832, + "line_number": 2855, "type": "Secret Keyword", "verified_result": null }, @@ -1130,7 +1130,7 @@ "hashed_secret": "c3de40d5e3fc71ed62771c2127a8e42585026c97", "is_secret": false, "is_verified": false, - "line_number": 2840, + "line_number": 2863, "type": "Secret Keyword", "verified_result": null }, @@ -1138,7 +1138,7 @@ "hashed_secret": "ce53277317aa3cd27c1619d7d371306ded2ddd1e", "is_secret": false, "is_verified": false, - "line_number": 2845, + "line_number": 2868, "type": "Secret Keyword", "verified_result": null } @@ -1378,7 +1378,7 @@ "hashed_secret": "ff2ccd73154c1c71ef9a2982fe16e3c529e7a1ae", "is_secret": false, "is_verified": false, - "line_number": 104, + "line_number": 105, "type": "Secret Keyword", "verified_result": null }, @@ -1386,7 +1386,7 @@ "hashed_secret": "74f82b992f8a92b849c2be77447f98a510338333", "is_secret": false, "is_verified": false, - "line_number": 105, + "line_number": 106, "type": "Secret Keyword", "verified_result": null }, @@ -1394,7 +1394,7 @@ "hashed_secret": "64814a3b7fd8444a56ad3641fd3451c6deaf0757", "is_secret": false, "is_verified": false, - "line_number": 113, + "line_number": 114, "type": "Secret Keyword", "verified_result": null }, @@ -1402,7 +1402,7 @@ "hashed_secret": "af84d91fde168566c7dc18f3121ea2fbe651af1f", "is_secret": false, "is_verified": false, - "line_number": 142, + "line_number": 143, "type": "Secret Keyword", "verified_result": null }, @@ -1410,7 +1410,7 @@ "hashed_secret": "d1081e351fbebe0deb0c2867d5f731c8f9cc3fd8", "is_secret": false, "is_verified": false, - "line_number": 395, + "line_number": 396, "type": "Secret Keyword", "verified_result": null }, @@ -1418,7 +1418,7 @@ "hashed_secret": "b7e9a6e2e04ed3edbf8b4e21def4beccbb6eb6b1", "is_secret": false, "is_verified": false, - "line_number": 396, + "line_number": 397, "type": "Secret Keyword", "verified_result": null }, @@ -1426,7 +1426,7 @@ "hashed_secret": "6d244f58364223afcc2322f696137b01ece78d9d", "is_secret": false, "is_verified": false, - "line_number": 397, + "line_number": 398, "type": "Secret Keyword", "verified_result": null }, @@ -1434,7 +1434,7 @@ "hashed_secret": "8bf177a72c93155020ebca9ee7f3c6e0fbdee946", "is_secret": false, "is_verified": false, - "line_number": 415, + "line_number": 416, "type": "Secret Keyword", "verified_result": null }, @@ -1442,7 +1442,7 @@ "hashed_secret": "c41e6cd4245e404f07635fdbac351aad4d54a213", "is_secret": false, "is_verified": false, - "line_number": 514, + "line_number": 515, "type": "Secret Keyword", "verified_result": null }, @@ -1450,7 +1450,7 @@ "hashed_secret": "6568a9761e26d16a111247f279b6fcabff1c8c86", "is_secret": false, "is_verified": false, - "line_number": 549, + "line_number": 550, "type": "Secret Keyword", "verified_result": null }, @@ -1458,7 +1458,7 @@ "hashed_secret": "314fc7fd580f8c510be196143946bbe5ea753443", "is_secret": false, "is_verified": false, - "line_number": 550, + "line_number": 551, "type": "Secret Keyword", "verified_result": null }, @@ -1466,7 +1466,7 @@ "hashed_secret": "afb9d1dcf212fa33d079a070ab90f0bef03c324b", "is_secret": false, "is_verified": false, - "line_number": 564, + "line_number": 565, "type": "Secret Keyword", "verified_result": null }, @@ -1474,7 +1474,7 @@ "hashed_secret": "2736fab291f04e69b62d490c3c09361f5b82461a", "is_secret": false, "is_verified": false, - "line_number": 571, + "line_number": 572, "type": "Secret Keyword", "verified_result": null }, @@ -1482,7 +1482,7 @@ "hashed_secret": "7542c8f677ffbbe75a5301a5c76f88401dc42897", "is_secret": false, "is_verified": false, - "line_number": 580, + "line_number": 581, "type": "Secret Keyword", "verified_result": null }, @@ -1490,7 +1490,7 @@ "hashed_secret": "ca20728d35f00c3a4c21b1fc8b0086b59f89df2d", "is_secret": false, "is_verified": false, - "line_number": 585, + "line_number": 586, "type": "Secret Keyword", "verified_result": null }, @@ -1498,7 +1498,7 @@ "hashed_secret": "5b7dcd14a4faa2cdd54cf6eb8d4bc35da31914a1", "is_secret": false, "is_verified": false, - "line_number": 597, + "line_number": 598, "type": "Secret Keyword", "verified_result": null }, @@ -1506,7 +1506,7 @@ "hashed_secret": "245dbfa0498aaa6898fe57a191a5b3130466a943", "is_secret": false, "is_verified": false, - "line_number": 618, + "line_number": 619, "type": "Secret Keyword", "verified_result": null }, @@ -1514,7 +1514,7 @@ "hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997", "is_secret": false, "is_verified": false, - "line_number": 620, + "line_number": 621, "type": "Secret Keyword", "verified_result": null }, @@ -1522,7 +1522,7 @@ "hashed_secret": "ee977806d7286510da8b9a7492ba58e2484c0ecc", "is_secret": false, "is_verified": false, - "line_number": 623, + "line_number": 624, "type": "Secret Keyword", "verified_result": null }, @@ -1530,7 +1530,7 @@ "hashed_secret": "a603075604516de626cda903868e457fad826533", "is_secret": false, "is_verified": false, - "line_number": 630, + "line_number": 631, "type": "Secret Keyword", "verified_result": null }, @@ -1538,7 +1538,7 @@ "hashed_secret": "95ef18f58f32e3821ec7e627af6ee4757f68760e", "is_secret": false, "is_verified": false, - "line_number": 631, + "line_number": 632, "type": "Secret Keyword", "verified_result": null }, @@ -1546,7 +1546,7 @@ "hashed_secret": "b3aca92c793ee0e9b1a9b0a5f5fc044e05140df3", "is_secret": false, "is_verified": false, - "line_number": 669, + "line_number": 670, "type": "Secret Keyword", "verified_result": null }, @@ -1554,7 +1554,7 @@ "hashed_secret": "ae21c64a87f6bb0b8e16e55c48be4cc638d7bd3f", "is_secret": false, "is_verified": false, - "line_number": 680, + "line_number": 681, "type": "Secret Keyword", "verified_result": null }, @@ -1562,7 +1562,7 @@ "hashed_secret": "58d1bbce297de3c304a9fefc3b483181872a5c6b", "is_secret": false, "is_verified": false, - "line_number": 682, + "line_number": 683, "type": "Secret Keyword", "verified_result": null }, @@ -1570,7 +1570,7 @@ "hashed_secret": "de93ba2d15f3dbf65f76e6fd21e1e4b002459dd8", "is_secret": false, "is_verified": false, - "line_number": 714, + "line_number": 715, "type": "Secret Keyword", "verified_result": null }, @@ -1578,7 +1578,7 @@ "hashed_secret": "533739bb9d1dad53e71d8c93200cc2510d442226", "is_secret": false, "is_verified": false, - "line_number": 717, + "line_number": 718, "type": "Secret Keyword", "verified_result": null } @@ -2896,7 +2896,7 @@ "hashed_secret": "c58994eefb19ae2d0bfc26a106de438359e53fb6", "is_secret": false, "is_verified": false, - "line_number": 314, + "line_number": 325, "type": "Secret Keyword", "verified_result": null }, @@ -2904,7 +2904,7 @@ "hashed_secret": "8c9fdbd88e905d33102d0be537adeab8595fe0da", "is_secret": false, "is_verified": false, - "line_number": 344, + "line_number": 355, "type": "Secret Keyword", "verified_result": null }, @@ -2912,7 +2912,7 @@ "hashed_secret": "6ea1c94703f93074853165df24e0ded6917472af", "is_secret": false, "is_verified": false, - "line_number": 370, + "line_number": 381, "type": "Secret Keyword", "verified_result": null }, @@ -2920,7 +2920,7 @@ "hashed_secret": "b05b46675e3b6cecdb5ed7d0c24a8a183abd4de8", "is_secret": false, "is_verified": false, - "line_number": 740, + "line_number": 751, "type": "Secret Keyword", "verified_result": null } @@ -3896,7 +3896,7 @@ "hashed_secret": "1e5c2f367f02e47a8c160cda1cd9d91decbac441", "is_secret": false, "is_verified": false, - "line_number": 172, + "line_number": 200, "type": "Secret Keyword", "verified_result": null } @@ -4178,7 +4178,7 @@ "hashed_secret": "c377074d6473f35a91001981355da793dc808ffd", "is_secret": false, "is_verified": false, - "line_number": 753, + "line_number": 751, "type": "Hex High Entropy String", "verified_result": null } @@ -4214,7 +4214,7 @@ "hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe", "is_secret": false, "is_verified": false, - "line_number": 741, + "line_number": 739, "type": "Secret Keyword", "verified_result": null } @@ -4899,16 +4899,6 @@ "verified_result": null } ], - "tests/migration/utils/container_manager.py": [ - { - "hashed_secret": "1b255e8e77fb3fafbf1fe2013347f762fbca9cd6", - "is_secret": false, - "is_verified": false, - "line_number": 409, - "type": "Basic Auth Credentials", - "verified_result": null - } - ], "tests/performance/MANUAL_TESTING.md": [ { "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", @@ -5001,16 +4991,6 @@ "verified_result": null } ], - "tests/unit/mcpgateway/services/test_gateway_service.py": [ - { - "hashed_secret": "3654bd5ef523a79741766b7c3f22228fd43c3836", - "is_secret": false, - "is_verified": false, - "line_number": 8137, - "type": "Secret Keyword", - "verified_result": null - } - ], "tests/unit/mcpgateway/services/test_mcp_client_chat_service_extended.py": [ { "hashed_secret": "d576acab7ea77edc8aa4f9ebea6bd4c1cc0d1764", @@ -5072,7 +5052,7 @@ "hashed_secret": "995e87a207ec41ef95f938dcf53a4184312b0d93", "is_secret": false, "is_verified": false, - "line_number": 11421, + "line_number": 11423, "type": "Hex High Entropy String", "verified_result": null }, @@ -5080,7 +5060,7 @@ "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", "is_secret": false, "is_verified": false, - "line_number": 17838, + "line_number": 17840, "type": "Secret Keyword", "verified_result": null } @@ -5100,7 +5080,7 @@ "hashed_secret": "4f13f134744a2fadbbe2d624687246347d12fa63", "is_secret": false, "is_verified": false, - "line_number": 2733, + "line_number": 2770, "type": "Hex High Entropy String", "verified_result": null } diff --git a/mcpgateway/alembic/versions/e28cd485ad3c_remove_global_unique_constraints_for_.py b/mcpgateway/alembic/versions/e28cd485ad3c_remove_global_unique_constraints_for_.py index 0cd84073db..1243e00755 100644 --- a/mcpgateway/alembic/versions/e28cd485ad3c_remove_global_unique_constraints_for_.py +++ b/mcpgateway/alembic/versions/e28cd485ad3c_remove_global_unique_constraints_for_.py @@ -30,8 +30,8 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision: str = "e28cd485ad3c" -down_revision: Union[str, Sequence[str], None] = "0a089912b5f0" +revision: str = "e28cd485ad3c" # pragma: allowlist secret +down_revision: Union[str, Sequence[str], None] = "0a089912b5f0" # pragma: allowlist secret branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None From b85cbb45ac4b4918b760a653afe18a081b801e49 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Fri, 26 Jun 2026 14:29:37 +0100 Subject: [PATCH 09/12] fixed tests Signed-off-by: Jitesh Nair --- .../routers/test_openapi_schema_router.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py index f9df490a43..696b7ef291 100644 --- a/tests/unit/mcpgateway/routers/test_openapi_schema_router.py +++ b/tests/unit/mcpgateway/routers/test_openapi_schema_router.py @@ -15,7 +15,6 @@ from unittest.mock import AsyncMock, MagicMock, patch # Third-Party -from fastapi import HTTPException import httpx import pytest @@ -334,18 +333,27 @@ async def test_generate_schema_url_parsing(): # --------------------------------------------------------------------------- -@pytest.mark.asyncio -async def test_generate_schemas_403_when_permission_denied(monkeypatch: pytest.MonkeyPatch): - """Endpoint returns 403 when user lacks tools.create permission. +def test_generate_schemas_403_when_permission_denied(monkeypatch: pytest.MonkeyPatch): + """Endpoint returns 403 via ASGI when user lacks tools.create permission. - This test temporarily restores the real RBAC decorators to verify - the endpoint correctly enforces the tools.create permission. + Drives the request through TestClient (ASGI routing) rather than calling + the function directly, so it verifies that @require_permission is wired + correctly on the registered route — not just on the bare function. """ - # Restore real decorators for this test - from tests.utils.rbac_mocks import restore_rbac_decorators + import importlib + + from fastapi import FastAPI + from fastapi.testclient import TestClient + from mcpgateway.middleware.rbac import get_current_user_with_permissions + from tests.utils.rbac_mocks import patch_rbac_decorators, restore_rbac_decorators + + # Restore real decorators so the route is decorated with the real @require_permission restore_rbac_decorators(_originals) - # Patch PermissionService to deny all permissions + # Reload router to pick up the real decorator + importlib.reload(router_mod) + + # Monkeypatch PermissionService to deny all permissions class DenyAll: def __init__(self, _db): pass @@ -355,24 +363,27 @@ async def check_permission(self, **_kwargs): monkeypatch.setattr("mcpgateway.middleware.rbac.PermissionService", DenyAll) - # Re-import the router module to pick up the restored decorators - import importlib - importlib.reload(router_mod) + # Mount the freshly-reloaded router on a throwaway app + test_app = FastAPI() + test_app.include_router(router_mod.router) - # Create test data - body = router_mod.GenerateSchemaRequest(url="http://api.example.com/calculate") - non_admin_user = {"email": "user@example.com", "is_admin": False, "ip_address": "127.0.0.1", "user_agent": "tests"} + # Override auth dependency to return an unprivileged user (no DB lookup) + async def unprivileged_user(): + return {"email": "user@example.com", "is_admin": False, "ip_address": "127.0.0.1", "user_agent": "tests"} - # The @require_permission decorator should raise HTTPException with 403 - # when PermissionService.check_permission returns False - with pytest.raises(HTTPException) as exc: - await router_mod.generate_schemas_from_openapi(body, _user=non_admin_user) + test_app.dependency_overrides[get_current_user_with_permissions] = unprivileged_user + + client = TestClient(test_app, raise_server_exceptions=False) + + response = client.post( + "/v1/tools/generate-schemas-from-openapi", + json={"url": "http://api.example.com/calculate"}, + ) - assert exc.value.status_code == 403 - assert "access denied" in str(exc.value.detail).lower() + assert response.status_code == 403, f"Expected 403, got {response.status_code}: {response.text}" + assert "access denied" in response.json().get("detail", "").lower() - # Re-patch decorators for remaining tests - from tests.utils.rbac_mocks import patch_rbac_decorators + # Re-patch decorators and reload for remaining tests patch_rbac_decorators() importlib.reload(router_mod) From 0d660627786e03f029f42f344ccb407de634bab4 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Fri, 26 Jun 2026 14:44:51 +0100 Subject: [PATCH 10/12] fixed pre-commit Signed-off-by: Jitesh Nair --- .secrets.baseline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.secrets.baseline b/.secrets.baseline index 164a06fc2f..df707dcf29 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-25T14:26:15Z", + "generated_at": "2026-06-26T13:32:28Z", "plugins_used": [ { "name": "AWSKeyDetector" From cda2a3af04c3d33b3e53e494acd396344bbabcf2 Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Fri, 26 Jun 2026 15:14:42 +0100 Subject: [PATCH 11/12] fixed pre-commit Signed-off-by: Jitesh Nair --- .secrets.baseline | 62 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index df707dcf29..1c7cd2769a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-26T13:32:28Z", + "generated_at": "2026-06-26T14:01:29Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -232,7 +232,7 @@ "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", "is_secret": false, "is_verified": false, - "line_number": 120, + "line_number": 121, "type": "Secret Keyword", "verified_result": null }, @@ -240,7 +240,7 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 217, + "line_number": 218, "type": "Secret Keyword", "verified_result": null }, @@ -248,7 +248,7 @@ "hashed_secret": "7b4455a56fbf1d198e45e04c437488514645a82c", "is_secret": false, "is_verified": false, - "line_number": 219, + "line_number": 220, "type": "Secret Keyword", "verified_result": null }, @@ -256,7 +256,7 @@ "hashed_secret": "90bd1b48e958257948487b90bee080ba5ed00caa", "is_secret": false, "is_verified": false, - "line_number": 291, + "line_number": 292, "type": "Hex High Entropy String", "verified_result": null }, @@ -264,7 +264,7 @@ "hashed_secret": "48ffbad96aa9c2b33f9486f5a3c2108198acb518", "is_secret": false, "is_verified": false, - "line_number": 292, + "line_number": 293, "type": "Hex High Entropy String", "verified_result": null } @@ -1306,7 +1306,7 @@ "hashed_secret": "e204d28a2874f6123747650d3e4003d4357d75eb", "is_secret": false, "is_verified": false, - "line_number": 852, + "line_number": 855, "type": "Secret Keyword", "verified_result": null }, @@ -1314,7 +1314,17 @@ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3", "is_secret": false, "is_verified": false, - "line_number": 1147, + "line_number": 1150, + "type": "Secret Keyword", + "verified_result": null + } + ], + "docs/docs/architecture/oauth-design.md": [ + { + "hashed_secret": "5c216af8faacfff02f8ee00326ecfcfb3334922f", + "is_secret": false, + "is_verified": false, + "line_number": 153, "type": "Secret Keyword", "verified_result": null } @@ -2533,6 +2543,22 @@ "line_number": 570, "type": "Secret Keyword", "verified_result": null + }, + { + "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", + "is_secret": false, + "is_verified": false, + "line_number": 643, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "f4793151b0607198d4de9b1ca458d3e25adf1cb7", + "is_secret": false, + "is_verified": false, + "line_number": 645, + "type": "Secret Keyword", + "verified_result": null } ], "docs/docs/manage/oauth.md": [ @@ -2680,7 +2706,7 @@ "hashed_secret": "1513802ada0ec04c2446206e16baa7256ebff74b", "is_secret": false, "is_verified": false, - "line_number": 598, + "line_number": 601, "type": "Secret Keyword", "verified_result": null } @@ -3050,11 +3076,27 @@ "type": "Secret Keyword", "verified_result": null }, + { + "hashed_secret": "4a0a2df96d4c9a13a282268cab33ac4b8cbb2c72", + "is_secret": false, + "is_verified": false, + "line_number": 777, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "5c216af8faacfff02f8ee00326ecfcfb3334922f", + "is_secret": false, + "is_verified": false, + "line_number": 799, + "type": "Secret Keyword", + "verified_result": null + }, { "hashed_secret": "157b314984dacdea10427c1ae3a856d286318e0d", "is_secret": false, "is_verified": false, - "line_number": 844, + "line_number": 902, "type": "Secret Keyword", "verified_result": null } From de3c72579f89a47ac0691fe5f2869986f988607f Mon Sep 17 00:00:00 2001 From: Jitesh Nair Date: Fri, 26 Jun 2026 16:37:17 +0100 Subject: [PATCH 12/12] fixed pre-commit Signed-off-by: Jitesh Nair --- .secrets.baseline | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 1c7cd2769a..9bf68977e5 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$", "lines": null }, - "generated_at": "2026-06-26T14:01:29Z", + "generated_at": "2026-06-26T15:26:45Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -3363,20 +3363,20 @@ "verified_result": null } ], - "docs/docs/using/agents/bee.md": [ + "docs/docs/using/agents/beeai.md": [ { "hashed_secret": "5546721ffdfc2e5b0e4c0da38f10774f9ad50b09", "is_secret": false, "is_verified": false, - "line_number": 39, + "line_number": 35, "type": "Secret Keyword", "verified_result": null }, { - "hashed_secret": "be0e0678e7132e8f991d463818e1befbaf3aad9d", + "hashed_secret": "ec545cfb40ff2f1b4eb959dbd0e3177236c540fa", "is_secret": false, "is_verified": false, - "line_number": 61, + "line_number": 203, "type": "Secret Keyword", "verified_result": null }