feat(auth): propagate end-user identity to upstream MCP servers#2990
Closed
crivetimihai wants to merge 2208 commits intomainfrom
Closed
feat(auth): propagate end-user identity to upstream MCP servers#2990crivetimihai wants to merge 2208 commits intomainfrom
crivetimihai wants to merge 2208 commits intomainfrom
Conversation
Signed-off-by: Chris PC <chrispc@li-4dc2bf4c-325d-11b2-a85c-b68e8b1fc307.ibm.com> Co-authored-by: Chris PC <chrispc@li-4dc2bf4c-325d-11b2-a85c-b68e8b1fc307.ibm.com>
* fix tags in mcp servers Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com> * fix(tags): handle dict-format tags in filtering and read paths Address issues with dict-format tag storage introduced by migration: - Add json_contains_tag_expr() to handle both List[str] and List[Dict] tag formats in database queries (SQLite, PostgreSQL, MySQL) - Update all services (gateway, tool, server, prompt, resource, a2a) to use json_contains_tag_expr for tag filtering - Fix catalog_service.py to pass through dict-format tags without re-validation - Restore legacy tag handling in _prepare_gateway_for_read for backward compatibility - Update tests to mock json_contains_tag_expr instead of json_contains_expr Closes #2203 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…#2253) This commit fixes a bug where deactivating a virtual server associated with an email notification team would fail with a database error. The root cause was the use of `joinedload` to fetch the `email_team` relationship when retrieving the server to be updated. This resulted in a `LEFT OUTER JOIN` in the underlying SQL query. When the query also included a `FOR UPDATE` clause to lock the server row, PostgreSQL raised a `FeatureNotSupported` error because it cannot apply a lock to the nullable side of an outer join. This fix changes the SQLAlchemy loading strategy from `joinedload` to `selectinload` for the `DbServer.email_team` relationship within the `set_server_state` method. `selectinload` resolves the issue by loading the relate `email_team` in a separate `SELECT` statement, thus avoiding the problematic `JOIN` in the initial `SELECT ... FOR UPDATE` query. Additionally, comprehensive unit tests have been added for both the `ServerService` and the admin panel routes to cover server activation, deactivation, and to verify that the fix works as expected, preventing future regressions. Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crmihai1@ie.ibm.com>
Signed-off-by: Mihai Criveti <crmihai1@ie.ibm.com>
Signed-off-by: Mihai Criveti <crmihai1@ie.ibm.com>
* tag view Signed-off-by: rakdutta <rakhibiswas@yahoo.com> * fix: Handle object tags in token details and improve fallback handling - Add object-to-string conversion for tags in showTokenDetailsModal (was missed in original PR) - Remove inconsistent blank line in viewGateway tag rendering - Add JSON.stringify fallback for malformed tag objects without id/label (defense-in-depth for edge cases like DB corruption) Part of the fix for #2267 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Signed-off-by: Mihai Criveti <crmihai1@ie.ibm.com> --------- Signed-off-by: rakdutta <rakhibiswas@yahoo.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Signed-off-by: Mihai Criveti <crmihai1@ie.ibm.com> Co-authored-by: Mihai Criveti <crmihai1@ie.ibm.com>
* Fix bump2version Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * bump2version Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * 1.0.0-BETA-2 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: optimize CPU usage in request logging middleware Add configuration options to reduce CPU and database overhead in detailed request logging: - log_detailed_skip_endpoints: List of path prefixes to skip from detailed logging (e.g., high-volume or low-value endpoints) - log_resolve_user_identity: Gate DB fallback for user identity resolution behind opt-in flag (default: false) - log_detailed_sample_rate: Sampling rate (0.0-1.0) to log only a fraction of requests when detailed logging is enabled These optimizations avoid expensive JSON parsing, masking, and identity lookups unless detailed logging is explicitly enabled and required. Closes #1865 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * docs: add documentation for logging CPU optimization options Document the new logging configuration options: - LOG_DETAILED_SKIP_ENDPOINTS: path prefixes to skip from logging - LOG_DETAILED_SAMPLE_RATE: sampling rate for detailed logging - LOG_RESOLVE_USER_IDENTITY: opt-in DB lookup for user identity Updated: - .env.example with new options and descriptions - README.md logging table and examples - Helm chart values.yaml and values.schema.json - charts/mcp-stack/README.md values table - docs/config.schema.json (regenerated from Pydantic model) - docs/docs/config.schema.json (regenerated from Pydantic model) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: add LOG_DETAILED_SKIP_ENDPOINTS to env list normalizer and add tests - Add LOG_DETAILED_SKIP_ENDPOINTS to _normalize_env_list_vars() to support CSV format and empty string values from environment variables - Add unit tests for skip endpoints, sampling rate, and user identity resolution gating in request logging middleware - Add settings field validation tests for new config options Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * doctest coverage Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * chore: lower doctest coverage threshold to 34% Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…patibility (#2342) - Use jsonschema.validators.validator_for to detect schema draft automatically - Support multiple JSON Schema drafts (Draft 4, 6, 7, 2019-09, 2020-12) - Log warnings for unsupported drafts or invalid schemas instead of raising errors - Handle None schemas gracefully - Apply consistent validation behavior to both tool and prompt schemas - Add comprehensive tests for different schema drafts - Add fallback validator logic in tool_service.py for runtime validation - Disable MCP SDK's built-in input validation which uses strict Draft 2020-12 Closes #2322 Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix(db): Guard against inactive transaction during async cleanup When registering MCP servers with long initialization times (like Moody's), a CancelledError can occur during the MCP session teardown (DELETE request). This causes the database transaction to become inactive before get_db() attempts to commit, resulting in: sqlalchemy.exc.InvalidRequestError: This transaction is inactive Add db.is_active checks before commit() and rollback() to handle cases where the transaction becomes inactive during async context manager cleanup. Closes #2341 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Add upsert logic for resources and prompts to prevent unique constraint violations When re-registering a gateway (e.g., after deletion or crash), orphaned resources and prompts from previous registrations could cause unique constraint violations on `(team_id, owner_email, uri)` for resources and `(team_id, owner_email, name)` for prompts. This fix adds upsert logic that: 1. Queries for existing resources/prompts matching the unique constraint 2. Updates existing records instead of creating duplicates 3. Creates new records only when no match exists This handles scenarios like: - Gateway deletion that didn't properly clean up resources (issue #2341) - Re-registration of the same MCP server under a new gateway name - Race conditions during concurrent operations Closes #2352 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * chore: Add cleanup script for orphaned resources/prompts/tools Adds a utility script to identify and remove database records that were left orphaned due to incomplete gateway deletions (e.g., #2341 crash). Usage: # Dry run (default) - shows what would be deleted python scripts/cleanup_orphaned_resources.py # Actually delete orphaned records python scripts/cleanup_orphaned_resources.py --execute # Filter by team or owner python scripts/cleanup_orphaned_resources.py --team-id <id> --owner-email <email> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Only upsert orphaned resources/prompts, add tests Addresses code review findings: 1. HIGH: Only update truly orphaned records (gateway_id IS NULL or points to non-existent gateway). Resources belonging to active gateways are no longer at risk of being reassigned. 2. MEDIUM: Use per-resource team/owner overrides when building lookup key, matching exactly what would be inserted to avoid constraint mismatches. 3. LOW: Added tests for orphaned resource upsert logic: - test_register_gateway_updates_orphaned_resources - test_register_gateway_does_not_update_active_gateway_resources Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Always call rollback() in exception handler, improve tests Addresses remaining code review findings: 1. ROLLBACK GUARD FIX: - REMOVED the `if db.is_active:` check before `db.rollback()` - Empirical testing proved that: * After IntegrityError, is_active becomes False * rollback() when is_active=False SUCCEEDS (doesn't fail!) * rollback() restores is_active to True, cleaning up the session * Skipping rollback when is_active=False leaves session unusable - The is_active guard for commit() is CORRECT (commit fails when False) - The is_active guard for rollback() was WRONG (rollback is always safe) 2. TEST ASSERTIONS: - Rewrote orphaned resource tests with proper assertions - Tests now directly verify: * Orphaned resources are detected and added to map * Resource fields are actually updated during upsert * Resources with deleted gateways are detected as orphaned * Resources with active gateways are NOT touched * Per-resource owner/team overrides are used in lookup key Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * chore: Add file encoding header to cleanup script Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: RinCodeForge927 <dangnhatrin90@gmail.com> Signed-off-by: RinZ27 <222222878+RinZ27@users.noreply.github.com>
Signed-off-by: RinCodeForge927 <dangnhatrin90@gmail.com> Signed-off-by: RinZ27 <222222878+RinZ27@users.noreply.github.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…ps (#2359) * fix(perf): resolve lock contention and CPU spin loop under high load (#2355) Phase 1: Replace cascading FOR UPDATE loops with bulk UPDATE statements in gateway_service.set_gateway_state() to eliminate lock contention when activating/deactivating gateways with many tools/servers/prompts/resources. Phase 2: Add nowait=True to get_for_update() calls in set_server_state() and set_tool_state() to fail fast on locked rows instead of blocking. Add ServerLockConflictError and ToolLockConflictError exceptions with 409 Conflict handlers in main.py and admin.py routers. Phase 3: Fix CPU spin loop in SSE transport by properly detecting client disconnection. Add request.is_disconnected() check, consecutive error counting, GeneratorExit handling, and ensure _client_gone is set in all exit paths. Results: - RPS improved from 173-600 to ~2000 under load - Failure rate reduced from 14-22% to 0.03-0.04% - Blocked queries reduced from 33-48 to 0 - CPU after load test: ~1% (was 800%+ spin loop) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(perf): add database lock timeout configuration (#2355) Add configurable timeout settings for database operations: - db_lock_timeout_ms: Maximum wait for row locks (default 5000ms) - db_statement_timeout_ms: Maximum statement execution (default 30000ms) These settings can be used with get_for_update() to prevent indefinite blocking under high concurrency scenarios. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * test: update tests for bulk UPDATE and SSE transport changes (#2355) Update gateway_service tests to use side_effect for multiple db.execute calls (SELECT + bulk UPDATEs) instead of single return_value. Update row_level_locking test to expect nowait=True parameter in get_for_update calls for set_tool_state. Update SSE transport tests to mock request.is_disconnected() and adjust error handling test to expect consecutive errors causing generator stop instead of error event emission. Add missing exception documentation for ServerLockConflictError and ToolLockConflictError in service docstrings (flake8 DAR401). Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(sse): add send_timeout to EventSourceResponse to prevent spin loops (#2355) When Granian ASGI server fails to send to a disconnected client, it logs "ASGI transport error: SendError" but doesn't raise an exception to our code. This causes rapid iteration of the generator without proper timeout handling. Add send_timeout=5.0 to EventSourceResponse to ensure sends time out if they fail, triggering sse_starlette's built-in error handling. Also enable sse_starlette's built-in ping mechanism when keepalive is enabled, which provides additional disconnect detection. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(sse): add rapid yield detection to prevent CPU spin loops (#2355) When clients disconnect abruptly, Granian may fail sends without raising Python exceptions. This adds rapid yield detection: if 50+ yields occur within 1 second, we assume client is disconnected and stop the generator. New configurable settings: - SSE_SEND_TIMEOUT: ASGI send timeout (default 30s) - SSE_RAPID_YIELD_WINDOW_MS: detection window (default 1000ms) - SSE_RAPID_YIELD_MAX: max yields before disconnect (default 50) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(sse): log rapid yield detection at ERROR level for visibility Changed from WARNING to ERROR so the detection message is visible even when LOG_LEVEL=ERROR. This is appropriate since rapid yield detection indicates a problem condition (client disconnect not reported by ASGI). Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: address review findings from ChatGPT analysis Finding 1: Fix lock conflict error propagation - Add explicit except handlers for ToolLockConflictError and ServerLockConflictError before the generic Exception handler - This allows 409 responses to propagate correctly instead of being wrapped as generic 400 errors Finding 3: Improve SSE rapid yield detection - Only track message yields, not keepalives - Reset the timestamp deque when timeout occurs (we actually waited) - This prevents false positives on high-throughput legitimate streams Finding 4: Remove unused db timeout settings - Remove db_lock_timeout_ms and db_statement_timeout_ms from config - These settings were defined but never wired into DB operations - Avoids false sense of protection Finding 2 (notifications) is intentional: gateway-level notifications are sent, and bulk UPDATE is used for performance under high load. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(perf): add nowait locks to prompt and resource state changes Extends the lock contention fix to prompt_service and resource_service: - Add PromptLockConflictError and ResourceLockConflictError classes - Use nowait=True in get_for_update to fail fast if row is locked - Add 409 Conflict handlers in main.py for both services - Re-raise specific errors before generic Exception handler This ensures consistent lock handling across all state change endpoints. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(sse): improve rapid yield detection to catch all spin scenarios - Track time since last yield as additional signal (<10ms is suspicious) - Check rapid yield after BOTH message and keepalive yields - Reset timestamps only after successful keepalive wait - Include time interval in error log for debugging Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(sse): add robust spin loop detection and update dependencies (#2355) SSE transport improvements: - Add consecutive rapid yield counter for simpler spin loop detection (triggers after 10 yields < 100ms apart) - Remove deque clearing after keepalives that prevented detection - Add client_close_handler_callable to detect disconnects that ASGI servers like granian may not propagate via request.is_disconnected() Test updates: - Update row-level locking tests to expect nowait=True for prompt and resource state changes Dependency updates: - Containerfile.lite: Update UBI base images to latest - gunicorn 23.0.0 -> 24.1.1 - sqlalchemy 2.0.45 -> 2.0.46 - langgraph 1.0.6 -> 1.0.7 - hypothesis 6.150.2 -> 6.150.3 - schemathesis 4.9.2 -> 4.9.4 - copier 9.11.1 -> 9.11.3 - pytest-html 4.1.1 -> 4.2.0 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * docs: add granian worker lifecycle options for SSE connection leak workaround Document GRANIAN_WORKERS_LIFETIME and GRANIAN_WORKERS_MAX_RSS options as commented-out configuration in docker-compose.yml and run-granian.sh. These options provide a workaround for granian issue #286 where SSE connections are not properly closed after client disconnect, causing CPU spin loops after load tests complete. Refs: #2357, #2358 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * docker-compose updates for GUNICORN Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * docker-compose updates for GUNICORN Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * docker-compose updates for GUNICORN Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Update pyproject.toml Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * lint Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: add sso_entra_admin_groups to list field validator - sso_entra_admin_groups now properly parses CSV/JSON from environment - Closes #2265 Signed-off-by: Akshay Shinde <akshayshinde@dhcp-9-162-244-59.mul.ie.ibm.com> * style: fix missing blank lines in test_config.py Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Akshay Shinde <akshayshinde@dhcp-9-162-244-59.mul.ie.ibm.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Akshay Shinde <akshayshinde@dhcp-9-162-244-59.mul.ie.ibm.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…scanners (#2200) - Convert cache.py to use async redis (redis.asyncio) for non-blocking I/O - Add parallel scanner execution using asyncio.gather in input/output filters - Add asyncio.to_thread for CPU-bound scanner operations - Quiet llm_guard logger to ERROR level to reduce noise - Fix tests to use prompt_id instead of deprecated name parameter - Update test to use environment variables for redis host/port Security: Scanner errors now fail-closed (is_valid=False) instead of being skipped, ensuring policy evaluation denies requests when scanners fail. Closes #1959 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
1. Replace custom CSS classes with native tailwind utility classes. 2. Add Chart.js theming for dark-mode graphs Signed-off-by: Gabriel Costa <gabrielcg@proton.me> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: plugin template test cases. Signed-off-by: Teryl Taylor <terylt@ibm.com> * fix: context passing in unit test Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> --------- Signed-off-by: Teryl Taylor <terylt@ibm.com> Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com> Co-authored-by: Teryl Taylor <terylt@ibm.com> Co-authored-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Precompile all regex patterns at module or configuration initialization time across 14 plugins, eliminating per-request compilation overhead. Closes #1834 Signed-off-by: Shoumi <shoumimukherjee@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* update jwt cli with more inputs Signed-off-by: Madhav Kandukuri <madhav165@gmail.com> * fix: prevent non-expiring tokens from invalid expires_in_days - Add ge=1 validation to TokenCreateRequest.expires_in_days schema - Add guard in _generate_token to reject expires_at in the past - Use math.ceil() and max(1, ...) to ensure exp is always set for sub-minute expirations (prevents rounding to 0) - Mark --secret and --algo CLI args as deprecated (always uses config) - Add tests for past expiry rejection and ceiling behavior This fixes a security regression where negative/zero expires_in_days could create permanent tokens instead of expired ones. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: restore --secret and --algo CLI options The --secret and --algo CLI parameters now work as optional overrides: - When provided, they override the configuration values - When not provided, JWT_SECRET_KEY and JWT_ALGORITHM from config are used This preserves backward compatibility while still defaulting to configuration-based signing for consistency. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: require --secret when --algo is specified Prevent invalid token generation by requiring --secret when --algo is provided. Using --algo alone would mix config-based keys with a different algorithm, potentially producing tokens that fail validation. Also fixes stale docstring that still referenced DEFAULT_SECRET/DEFAULT_ALGO instead of the new empty-string defaults with config fallback. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Madhav Kandukuri <madhav165@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Initial commit for filesystem server
- added stdio simple server
- implemented list_directory tool
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improved main runner
- Added argument handler
- improve tracing
- implemented streamable-http
Signed-off-by: cafalchio <maolivei@tcd.ie>
* added tracing info for each call
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Implemented search files recursively
- use glob patterns
- Added search to tools
- Handle errors
- Improve descriptions
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added files for new functions
- Updated cargo file for file search
Signed-off-by: cafalchio <maolivei@tcd.ie>
* implemented case insensitive search: Lowercase filenames and patterns
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Implemented read_file function and added to a server
- check for Max file size 1Mb
- check if the path is a file
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added tracing for read file, improved error description
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added get_file_info
- get size, created, modified and permissions
- Added to the server
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added Read multiple files
- use read file for each content
- read async
- added to server tools
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added write_file toold func
- Write to a tempfile uuid
- rename file to actual name
- remove tempfile
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added create_directory tool
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added create_directory to server and implemented placeholder for list_allowed_directories
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Changed release config to reduce bin size
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Implemented move file function.
- fails if destination exists
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added move_file to the server
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added edit file to the server
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added edit_file
- support dry_run
- use similar to get diffs
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Adding sandbox for path ccheck
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Apply fix to reduce TOCTOU vulnerability
- atomic write, no checks and write
Signed-off-by: cafalchio <maolivei@tcd.ie>
* improve read to reduce TOCTOU vulnerability
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Stopped to follow symlink on search (security)
Signed-off-by: cafalchio <maolivei@tcd.ie>
* removed unused import
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improved Sandbox for TOCTOU safety
- initialize sandbox once
- check if new folders are inside root
- resolve path inside root
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added get_roots for server list_allowed_directories
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Using sanbox resolve path before ger file info
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added sandox to write_file and create_directory
- validade parent folder
- clean tempfile after
- check new folder inside root
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added sandbox to write file
- canonicalize and check new folders
- check parent folders
- on create directory, check if exists
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added sandbox check for list_directory
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added sandbox check for edit_file and move_file
- check and canonicalize destination parent
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Apply sandbox checks for read_file and read_multiple_files
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Changed sandbox initialization from global to context
- removed global sandbox
- initialize sandbox in main and pass to each function
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added tests for searc_files and list_directories
- test for symlinks
- test for path outside roots
- > 95% coverage
- formatted using fmt
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Formatted files using cargo fmt
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added tests for get_file_info
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added test coverage for read_file and read_multiple_files
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added unit test coverage for write_file and create_directory
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improve test coverage for edit_file and move_file
Signed-off-by: cafalchio <maolivei@tcd.ie>
* format edit.rs
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added test coverage for sandbox
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improve server runner
- addded sever gracefully shutdown
- Declare Config values in main
- Improve server logs
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improved logs for list_allowed_directories and linted file
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improve test coverage for server
- iimproved logs in server
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Linted using cargo clippy
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Simplified main.rs and moved server code to lib.rs for integration tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added integration tests to simulate workflows
- file and folder manipulation workflow
- permission and metadata workflow
- search and organise workflow
- server tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* formatted and clippped integration tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* added result where it was missing in tool call result
- rename all outputs to result
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Normalized write file and create directory output
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Structured search and write outputs
- update tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Fixed write result not showing errors correctly
- server will return WriteResult
- create_directory return err or string
- fixed test create_directory tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Normalized output result for write_file
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Normalized tool result outpus
- Keep consistency between tools
- Return MPC error or success
- reorganised tools between files
- updated tests
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added server start banner
Signed-off-by: cafalchio <maolivei@tcd.ie>
* fixed main receiving multiple roots
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Clipped and formatted
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Removed justfile and added Makefile
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added correct readme
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Added dockerfile
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Improved tracing logs
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Update test coverage and binary size in readme
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Removed umused dependency
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Updated cargo dependencies
Signed-off-by: cafalchio <maolivei@tcd.ie>
* Updted deprecated InitializedRequestParam
Signed-off-by: cafalchio <maolivei@tcd.ie>
* fix: correct error messages and documentation in filesystem server
- Fix misleading error messages in server.rs that said "Error writing file"
when the actual operation was read, move, or get_file_info
- Update README to accurately reflect that move_file overwrites destination
(previously incorrectly stated it fails)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: cafalchio <maolivei@tcd.ie>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…ts (#2345) * Fix proxy authentication Signed-off-by: Mohan Lakshmaiah <mohalaks@in.ibm.com> * Fix pylint errors Signed-off-by: Mohan Lakshmaiah <mohalaks@in.ibm.com> * fix: Correct lint issues in proxy auth tests - Add missing blank line between test classes - Remove unused jwt import - Fix excess blank lines Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Include plugin context in proxy auth for cross-hook sharing Add plugin_context_table and plugin_global_context to proxy authentication paths, matching the JWT authentication path. This ensures HTTP_AUTH_CHECK_PERMISSION hooks can access context set by HTTP_PRE_REQUEST hooks when using proxy authentication. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Address security concerns in proxy authentication 1. RBAC now checks auth_required when proxy header missing - Returns 401 for API requests, 302 redirect for browsers - Aligns HTTP behavior with WebSocket auth 2. Block anonymous users from token management - Add auth_method=="anonymous" to _require_interactive_session - Prevents token access when proxy header missing 3. Lookup proxy user admin status from database - Check platform_admin_email for admin match - Query EmailUser table for is_admin status - Enables plugin permission hooks to work correctly Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: Align require_auth with RBAC proxy enforcement Update require_auth to check auth_required when proxy header is missing, matching the RBAC/WebSocket behavior. Previously returned "anonymous" even when auth_required=true. - Raise 401 when mcp_client_auth_enabled=false and no proxy header if auth_required=true - Update tests to cover both auth_required=true and false cases Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mohan Lakshmaiah <mohalaks@in.ibm.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mohan Lakshmaiah <mohalaks@in.ibm.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
…py (#2889) * fix: Implement pagination for gRPC listing in grpc_service and admin.py Branch: GrpcRegistrationUIFixes Signed-off-by: Gabe Goodhart <ghart@us.ibm.com> AI-usage: full * test: Update tests and add a new one for multi-page grpc results Branch: GrpcPagination Signed-off-by: Gabe Goodhart <ghart@us.ibm.com> AI-usage: full * test: Add coverage for gRPC service team name resolution and team_id-only filtering Signed-off-by: Gabe Goodhart <ghart@us.ibm.com> AI-usage: full * fix: add missing team field to GrpcServiceRead and error handling to conversion loop Add the `team` field to `GrpcServiceRead` schema to match all other Read schemas (ToolRead, ResourceRead, GatewayRead, PromptRead), so team name resolution actually populates the response. Add try/except around model_validate to gracefully skip corrupted records, consistent with other services. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add JWT claims extraction plugin (Issue #1439) Implements JWT claims and metadata extraction plugin for downstream authorization plugins (Cedar, OPA, etc.). Features: - Extracts standard JWT claims (sub, iss, aud, exp, iat, nbf, jti) - Extracts custom claims (roles, permissions, groups, attributes) - Supports RFC 9396 Rich Authorization Requests - Non-blocking permissive mode - Stores claims in global_context.metadata['jwt_claims'] Plugin hooks into HTTP_AUTH_RESOLVE_USER to extract claims after JWT verification and makes them available to policy enforcement plugins. Implementation: - Uses PluginContext correctly to access global_context - Proper hook method naming (http_auth_resolve_user) - Returns continue_processing=True for passthrough behavior - Error handling with graceful fallback Includes: - Plugin implementation with error handling - Configuration file (YAML) - Comprehensive test suite (5 tests, all passing) - Documentation with usage examples for Cedar/OPA Testing: - All 5 plugin tests passing - Linting clean (ruff + flake8) - Coverage: 90% Related to: - Issue #1439: JWT claims and metadata extraction plugin - Issue #1422: [EPIC] Agent and tool authentication and authorization - RFC 9396: Rich Authorization Requests Signed-off-by: yiannis2804 <yiannis2804@gmail.com> * fix: address PR code review feedback Addressed all 5 review comments from @[reviewer-name]: 1. Security documentation (verify_signature=False): - Added prominent SECURITY NOTE in module docstring - Added Security Considerations section to README - Documented token pre-verification assumption and risks 2. Logging sensitivity (PII leak prevention): - Changed logger.info() to logger.debug() for claim extraction - Only log claim count, not claim contents - Prevents sensitive data exposure in production logs 3. Safer header access pattern: - Replaced hasattr() with getattr(payload.headers, 'root', {}) - More robust against header model changes - Fails gracefully with empty dict default 4. Copyright year correction: - Updated from 2025 to 2026 in all files 5. Coverage badge deletion: - File is auto-generated (in .gitignore) - Not present in upstream/main - Correctly excluded from version control All tests passing (5/5), linting clean, ready for review. Related to: #1439 Signed-off-by: yiannis2804 <yiannis2804@gmail.com> * fix: align JWT claims extraction plugin with framework conventions - Move plugin from mcpgateway/plugins/ to plugins/ (correct location) - Rename plugin.py to jwt_claims_extraction.py, config.yaml to plugin-manifest.yaml (match naming conventions) - Fix critical security doc: http_auth_resolve_user fires BEFORE JWT verification, not after — document the actual security model - Write to global_context.state instead of metadata (metadata is read-only from plugin perspective per framework docs) - Add Pydantic config model (JwtClaimsExtractionConfig) with configurable context_key - Add __init__ constructor calling super().__init__(config) - Add from __future__ import annotations - Add Bearer scheme validation in _extract_token - Use lazy logging format strings instead of f-strings - Remove unnecessary hasattr check on metadata - Remove error string leak from result metadata - Fix copyright year consistency (2026 across all files) - Fix SPDX-License-Identifier: Apache-2.0 headers - Revert unnecessary MANIFEST.in additions - Move tests to tests/unit/plugins/ and add new test cases for non-Bearer scheme rejection and custom context_key config Closes #1439 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: register JWT claims extraction plugin in config.yaml Adds disabled-by-default registration entry so the plugin can be activated by setting mode to "permissive". Closes #1439 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: yiannis2804 <yiannis2804@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* chore: add linting-full workflow and pre-commit gate Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: bootstrap pre-commit with uv in pipless venvs Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: exclude known pre-commit false positives Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: support helm plugin verify toggle in linting target Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: resolve gosec findings and enforce linting gosec gate Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…ult for development testing (#2949) * feat(keycloak): add Keycloak SSO to docker-compose for development testing - Add Keycloak service to docker-compose.yml (pinned to v26.1) with pre-configured realm export for local SSO development - Add docker-compose.sso.yml overlay for SSO-specific configuration - Add "sso" to testing and inspector service profiles so --profile sso brings up the full dev stack (Keycloak + testing + inspector) - Implement SSO bootstrap utility for automatic Keycloak realm/client setup - Add Keycloak discovery helper with well-known endpoint support - Enhance SSO service with id_token claims fallback for split-host configurations (restricted to 401 + split-host detection only) - Improve admin.py SSO logout with proper id_token_hint RP-initiated flow - Skip expired id_token_hint in logout URL to avoid Keycloak rejection - Add cookie size validation for id_token storage (>3.8KB warning) - Use dynamic max_age for SSO cookies matching token_expiry setting - Improve error handling and logging throughout SSO flows - Add sso-keycloak-tutorial and developer workstation documentation - Add test-sso-flow.sh script for manual SSO flow verification - Add Makefile targets for Keycloak lifecycle management Closes #2949 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * test(keycloak): add and update tests for Keycloak SSO integration - Add SSO service tests for Keycloak userinfo fallback, callback error handling, personal team resolution, and user authentication flows - Add Keycloak discovery and SSO bootstrap unit tests - Update SSO router tests to use handle_oauth_callback_with_tokens - Update admin module tests for Keycloak-aware logout behavior with separate coverage for keycloak-enabled and keycloak-disabled paths - Add expired id_token_hint omission test - Add id_token cookie size and max_age validation tests Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add original_description column to tools table (#2893) When tools are registered, preserve the original description from the source MCP server in a new original_description field. This allows users to customize the tool description while retaining the original for reference. - Add original_description column to Tool model and ToolRead schema - Populate original_description at tool registration time - Include original_description in export service output - Add Alembic migration with data backfill from existing descriptions Closes #2893 Signed-off-by: Nithin Katta <Nithin.Katta@ibm.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: preserve custom descriptions during gateway refresh and harden original_description - Protect user-customized descriptions during gateway sync/refresh by only overwriting description when it matches original_description - Add default value (None) to ToolRead.original_description for cache compatibility across deployments - Add original_description to tool cache payload for consistency - Restore original_description during export/import cycle - Update tests to cover description preservation behavior Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: scope import original_description restore by batch ID and original_name - Use Tool.original_name (not computed Tool.name) for restore lookup - Generate import_batch_id to scope restore to newly created tools only - Skip restore when no tools were created (respects skip/fail semantics) - Add tests for restore-by-batch and skip-no-restore behaviors - Fix missing original_description on gateway service test mock Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: commit (not flush) import original_description restore db.flush() without a subsequent commit loses the restore changes since register_tools_bulk already committed its own transaction. Verified with E2E testing against running docker-compose cluster. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Nithin Katta <Nithin.Katta@ibm.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Nithin Katta <Nithin.Katta@ibm.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…tting (#2898) Enforce the existing REQUIRE_TOKEN_EXPIRATION config setting at token creation time. Previously the setting only validated incoming tokens but allowed creation of tokens without expiration. - Add validation in TokenCatalogService.create_token() to reject tokens without expiration when REQUIRE_TOKEN_EXPIRATION=true - Pass require_token_expiration flag to admin UI template context - Add conditional required field indicator and helper text in the token creation form - Update existing tests to provide expires_in_days where needed - Add new test cases covering policy enabled/disabled scenarios, team tokens, and edge cases (zero expiry days) Closes #2836 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(auth): implement password reset and recovery workflows Add self-service forgot/reset password APIs and admin UI flows with one-time reset tokens, SMTP notifications, account unlock actions, lockout expiry fixes, metrics, migration, docs, and recovery tooling. Closes #2542 Closes #2543 Closes #2628 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * lint fixes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Test coverage Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Fix password reset issues Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Rebase Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: mark hash_password utility executable Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
#2950) * fix(ui): standardize loading indicators across admin pages (#2946) Signed-off-by: Oriol Morros Vilaseca <OM368@student.aru.ac.uk> * fix(ui): standardize Users loading indicator to match pattern Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Oriol Morros Vilaseca <OM368@student.aru.ac.uk> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…2865) * feat: add slow-time-server for timeout, resilience, and load testing Implements #2783. A configurable-latency Go MCP server modelled on fast-time-server that introduces artificial delays on every tool call, serving as a testing target for gateway timeout enforcement, circuit breaker behaviour, session pool resilience, and load testing. Server features: - 5 MCP tools: get_slow_time, convert_slow_time, get_instant_time, get_timeout_time, get_flaky_time - 2 MCP resources: latency://config, latency://stats - 1 MCP prompt: test_timeout - 4 latency distributions: fixed, uniform, normal, exponential - Failure simulation with configurable rate and mode - Runtime reconfiguration via REST POST /api/v1/config - Invocation statistics with p50/p95/p99 percentiles - Multi-transport: stdio, SSE, Streamable HTTP, dual, REST - 32 unit tests with race detection, all passing Integration: - docker-compose.yml: testing profile (port 8889) + auto-registration - docker-compose-performance.yml: dedicated performance testing service - Locust load test with 4 scenarios (slow, timeout storm, mixed, circuit breaker) - Documentation in docs/docs/using/servers/go/slow-time-server.md Closes #2783 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: address review issues in slow-time-server PR - Fix healthcheck in docker-compose-performance.yml to use binary's built-in health check instead of curl (scratch image has no curl) - Replace mixed atomic+mutex with plain increments under mutex in invocationStats.record() for clarity - Remove dead generateTestTimeoutPrompt() function and unused strings import from rest_handlers.go - Remove unused json and uuid imports from locust test file - Align env var naming (SLOW_TIME_LATENCY) between compose files Closes #2783 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: move slow-time-server to dedicated resilience profile The slow-time-server deliberately introduces latency and failures, which breaks existing tests when included in the testing profile. Move it to a dedicated 'resilience' profile so it must be explicitly opted into. - docker-compose.yml: profiles ["testing"] -> ["resilience"] - Makefile: add resilience-up/down/logs targets - docs: update profile references Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * feat: add Makefile targets for resilience load testing Add dedicated targets for running Locust and JMeter tests against the slow-time-server, ensuring these tests only run when explicitly invoked rather than as part of the regular testing profile. New targets: - resilience-locust: headless Locust run (10 users, 120s) - resilience-locust-ui: Locust web UI on port 8090 - resilience-jmeter: JMeter baseline (20 threads, 5min) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…#2943) * fix: add alembic upgrade validation and migration compatibility fixes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(db): handle psycopg JSON deserialization in alembic migrations Closes #2955 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * format Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…ce suite (#2956) * feat: implement MCP 2025-11-25 compliance suite and dated make targets Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * format Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: exclude compliance tests from default pytest and resolve gosec findings - Add --ignore=tests/compliance to pytest addopts so compliance suite only runs via dedicated make targets (make 2025-11-25, etc.) - Lazy-import mcpgateway.main in compliance conftest to avoid triggering bootstrap_db during test collection - Fix gosec G114: replace bare http.ListenAndServe with http.Server using ReadHeaderTimeout in slow-time-server (4 instances) - Suppress gosec G404/G705 false positives with nosec annotations Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…2724) - Add AlreadyEncryptedError and NotEncryptedError (extend ValueError) for explicit validation in strict mode - Introduce v2: format prefix for unambiguous encrypted data detection - Add strict vs idempotent API modes (decrypt_secret vs decrypt_secret_or_plaintext) with backward-compatible async wrappers - Replace length heuristic in oauth_manager with explicit is_encrypted() - Add null checks after decryption in dcr_service update/delete - Migrate encryption tests to dedicated test_encryption_service.py - Add comprehensive test coverage for edge cases, concurrent operations, and real-world token formats (JWT, OAuth2, API keys) Closes #2405 Signed-off-by: Mohan Lakshmaiah <mohan.economist@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* chore: replace copier with cookiecutter for template scaffolding (#2361) Copier pulled jinja2-ansible-filters (GPL-licensed). Cookiecutter is a simpler, widely-adopted alternative with no problematic transitive deps. - Migrate all 4 template sets (Go server, Python server, native plugin, external plugin) from copier YAML configs to cookiecutter.json with {{cookiecutter.*}} directory conventions - Update mcpplugins CLI to use cookiecutter.main.cookiecutter() API, add --template_type flag to select native/external plugin templates - Replace copier>=9.11.3 with cookiecutter>=2.6.0 in pyproject.toml and tox.ini - Update scaffold shell scripts to invoke cookiecutter CLI - Update tests, AGENTS.md, llms docs, and roadmap Closes #2361 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: exclude cookiecutter template dirs from pre-commit linting Template files containing Jinja2 syntax ({{ cookiecutter.* }}, {% set %}) are invalid YAML/TOML/Python from linters' perspective. Add plugin_templates/ to the global exclude in both pre-commit configs and yamllint. Also restore executable bit on run-server.sh and update uv.lock. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: exclude cookiecutter templates from gosec and tomllint Template go.mod and pyproject.toml contain Jinja2 variables that are invalid from the perspective of Go tooling and TOML parsers. Skip template directories in gosec, govulncheck, and tomllint find commands. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: exclude cookiecutter templates from tomllint CI workflow The lint.yml workflow runs tomlcheck directly via find, not through the Makefile target. Add the same template directory exclusions. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(ui): unified admin search with tags + global search Implement a unified, consistent search experience across the Admin UI: - Standardize search endpoints with unified response shape (items, count, entity_type, query, filters_applied) while preserving legacy keys - Add tag filtering via comma=OR and plus=AND semantics with bounds (max 20 groups, 10 terms per group) - Server-side panel search for all entity panels via HTMX partial reloads with debounced input and namespaced URL params for shareability - Global search modal (Ctrl/Cmd+K) aggregating results across servers, gateways, tools, resources, prompts, agents, teams, and users - Escape SQL LIKE wildcards in all search filters to prevent injection - Consistent search field coverage between search and partial endpoints Closes #2076 Closes #2109 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(metrics): prevent duplicate Prometheus metric registration in multi-app tests Guard setup_metrics() against re-registration errors when multiple FastAPI apps are instantiated in the same process (e.g., test suites). Uses best-effort registry lookup via _get_registry_collector() to reuse existing collectors instead of crashing on ValueError. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(admin): eliminate redundant get_user_teams calls in unified search Add _get_user_team_ids helper with caching support. The unified search endpoint pre-fetches team IDs once and injects them into the user context, avoiding 6 redundant database lookups (one per entity type). Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(admin): use ESCAPE clause for LIKE queries and update Playwright search waits SQLAlchemy .contains() does not add an ESCAPE clause, so escaped wildcards (\_) are treated as literal two-character sequences on SQLite. Replace all .contains(_escape_like(...)) calls with _like_contains() which generates .like(..., escape='\\') for correct behaviour on all database backends. Update Playwright page objects and tests to wait for the HTMX partial responses now that search is server-side instead of client-side. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * format Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(tests): use networkidle wait for HTMX search in Playwright tests Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(admin): format replaceChild call for Prettier compliance Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Code review Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Code review Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Code review Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Test networkwait fix Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Linting and permissions updates Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Linting and permissions updates Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Linting and test updates + fix UI logout issue Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * flake8 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(ui): flexible admin UI sections for embedded contexts Add env controls to hide UI sections/header items (including embedded mode defaults). Support per-request ?ui_hide= overrides persisted via cookie and prevent hidden sections from loading data. Update Helm values/schema, docker-compose, docs, and add unit + JS tests. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(ui): restore default tab fallback Prefer the Overview tab when available and fall back to Gateways for minimal DOM states. Align JS tab tests with the restored default tab behavior. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(ui): review fixes for admin UI section visibility Deduplicate UI_HIDABLE_SECTIONS, UI_HIDABLE_HEADER_ITEMS, and UI_HIDE_SECTION_ALIASES constants — define once in config.py, import in admin.py. Harden normalizeTabName() against CSS selector injection by restricting to [a-z0-9-] characters. Add max_age (30 days) to the ui_hide_sections cookie so preferences persist across browser sessions. Filter global search results to exclude hidden sections. Expand JS test coverage: normalizeTabName edge cases, admin-only tab blocking, idempotency guard, getDefaultTabName priority, isTabAvailable, getUiHiddenSections/Tabs normalization, and global search filtering. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * test(ui): add comprehensive test coverage for admin UI section visibility Cover _normalize_ui_hide_values, cookie set/delete behavior, section-aware data loading, config validators, isTabHidden, and resolveTabForNavigation. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(config): use NoDecode class instead of instance for env parsing NoDecode() (instance) in Annotated metadata is not found by pydantic-settings' `NoDecode in field.metadata` check, causing json.loads to be called on raw CSV strings. Use NoDecode (class) which matches correctly. Also adds ADR-040 and admin UI customization guide. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Fixes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(ui): expand admin section hiding coverage Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * test(metrics): stabilize db engine detection setup test Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(playwright): stabilize admin ui tab and search flows Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Fixes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
- S7502: store asyncio task references to prevent GC (resource_cache, token_catalog_service, email_auth_service) - S5655: replace SimpleNamespace with SSOProviderContext dataclass (sso_service) - S5886: fix return type lies with Optional (gateway_service, services_auth) - S5890: add missing Optional for None defaults (cedar/opa schemas) - S3923: remove dead if/else branches (cache, translate) - S3457: fix logger format string (validate_signature) - S1871: merge duplicate branches (toon, schemas) - S1854: remove dead stores for current_admin_origin (sso_service) - S5247: remove 16 autoescape-false blocks, replace 2 |safe patterns with CSS word-spacing (admin.html, 11 partial templates) Closes #2981 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: update API token last_used and log usage stats Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * flake8 fix Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * additional changes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * remove duplicate last_used update in token usage logging Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * additional test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * added exception handling Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * fix coverage Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * fix: optimize token tracking with rate-limiting and raw ASGI middleware Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * additional test fixes Signed-off-by: Shoumi <shoumimukherjee@gmail.com> * fix: address review findings in token usage tracking - Store JTI in request.state.jti for standard API tokens in _set_auth_method_from_payload, ensuring middleware can access it without re-decoding the JWT on cached/batched auth paths - Remove redundant db.commit() in TokenUsageMiddleware since log_token_usage() already commits the transaction - Add bounded eviction (max 1024 entries) to in-memory rate-limit cache in _update_api_token_last_used_sync to prevent unbounded growth - Add test coverage for cache eviction and JTI propagation - Fix formatting (missing space, unused import, black/isort) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: patch hidden sections env leakage in admin UI tests Two admin tests failed because .env sets MCPGATEWAY_UI_HIDE_SECTIONS which leaks into test execution, causing section-dependent code paths (resource loading, gRPC services) to be skipped. Patch settings to ensure no sections are hidden during these tests. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix: resolve DI, retry storm, race, and opaque token tracking issues - Fix get_current_user request param type (Optional[object] → Request) so FastAPI auto-injects it, enabling last_used and usage logging - Add 30s backoff in _get_sync_redis_client after connection failures to prevent retry storms when Redis is down - Move in-memory cache from hasattr-based function attributes to module-level globals, eliminating initialization race condition - Store user_email in request state for DB-fallback opaque tokens so usage middleware can log them without JWT decode Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Shoumi <shoumimukherjee@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Add secure, configurable identity propagation from the gateway to upstream MCP servers. When enabled, the authenticated user's identity (email, groups, roles, admin status, auth method) is forwarded via HTTP headers (X-Forwarded-User-*) and/or MCP _meta fields. Key changes: - UserContext model on GlobalContext, always populated from all auth paths - Identity propagation utility (headers, meta, HMAC signing, filtering) - Per-gateway config overrides via identity_propagation JSON field - Audit trail enriched with auth_method, acting_as, delegation_chain - RFC 8693 token exchange on OAuthManager for on-behalf-of flows - Session pool identity isolation via forwarded user headers - Plugin convenience helpers (user_context, user_email, user_groups) - ADR-041, dedicated docs page, full configuration surface coverage - 77 unit tests with comprehensive diff coverage Closes #1436 Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Member
Author
|
Reopened as #3152. CI/CD will re-run on the new PR. You are still credited as the author. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
X-Forwarded-User-*) and/or MCP_metafieldsUserContextmodel populated unconditionally from all auth paths (JWT, API key, basic, SSO, proxy), removing theinclude_user_infogateauth_method,acting_as, anddelegation_chainfieldsIDENTITY_PROPAGATION_ENABLED(default:false) — zero behavioral change for existing deploymentsChanges
Core Implementation (9 phases)
GlobalContext+ always-on identity population from all auth pathsconfig.py, per-gateway JSON override onGatewaymodelmcpgateway/utils/identity_propagation.py): headers, meta, HMAC signing, filteringtool_service,resource_service,streamablehttp_transport(all code paths)auth_method,acting_as,delegation_chain) + 2 Alembic migrationsOAuthManagerPluginContext.user_context,.user_email,.user_groupsDEFAULT_IDENTITY_HEADERSfor user isolationDocumentation & Configuration Surfaces
docs/docs/manage/identity-propagation.mdconfiguration.mdconfig.schema.jsonfilesvalues.yaml+values.schema.jsondocker-compose.yml+.env.exampleTests
Closes #1436