feat(api): comprehensive Virtual Meta-Server with 12 meta-tools#3978
Open
ecthelion77 wants to merge 9 commits intoIBM:mainfrom
Open
feat(api): comprehensive Virtual Meta-Server with 12 meta-tools#3978ecthelion77 wants to merge 9 commits intoIBM:mainfrom
ecthelion77 wants to merge 9 commits intoIBM:mainfrom
Conversation
de459da to
f6712e6
Compare
6 tasks
f6712e6 to
096d1a3
Compare
Contributor
Author
|
Suggested labels: |
096d1a3 to
ccccaab
Compare
096d1a3 to
9611672
Compare
1f9d503 to
f1f0df9
Compare
5 tasks
added 3 commits
April 27, 2026 11:35
Implements the Virtual Meta-Server feature (IBM#2230) — a tool aggregation layer that enables AI agents to discover and invoke thousands of underlying tools through a unified interface. Meta-tools: - search_tools: hybrid semantic + keyword search with scope filtering - list_tools: paginated tool listing with sorting and filtering - describe_tool: detailed tool info with schema and metadata - execute_tool: tool execution with JSON schema validation and routing - get_tool_categories: aggregated categories with counts - get_similar_tools: vector similarity search for related tools - authorize_gateway: interactive OAuth authorization with token refresh - authorize_all_gateways: one-click authorization for all OAuth gateways - list_resources: paginated MCP resource listing - read_resource: read MCP resource content by URI - list_prompts: paginated MCP prompt listing - get_prompt: prompt template retrieval with optional rendering Features: - OAuth integration: propagates user identity through the call chain - Chained OAuth flow: authorize-all endpoint chains multiple gateways - camelCase normalization for MCP clients - Flat argument tolerance for Copilot Studio - Post-login redirect via cookie with safe path validation - Observability: prompt.render and resource.read spans - JSON serialization: orjson.dumps() for proper JSON output - Admin UI: meta-server checkbox and hide-underlying-tools in server forms - Preserves protect_oauth_config_for_storage() on server update - RBAC enforcement via middleware on all meta endpoints Closes IBM#2230 Supersedes IBM#3653 Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
All 12 meta-server tool handlers now enforce visibility-based access control using user_email and token_teams from the JWT context. Previously fixed (execute_tool, authorize_gateway, authorize_all_gateways) already passed user context correctly. The following handlers were bypassing access control entirely: - _list_tools: now passes user_email/token_teams to ToolService.list_tools() - _search_tools: replaced raw db.query(Tool) with ToolService.list_tools() - _stub_describe_tool: passes user_email/token_teams to MetaToolService - _get_similar_tools: filters vector search results against accessible tool set via ToolService.list_tools() - _list_resources: applies ResourceService._apply_access_control() to query - _read_resource: checks ResourceService._check_resource_access() before returning content - _list_prompts: applies PromptService._apply_access_control() to query - _get_prompt: checks PromptService._check_prompt_access() before returning content Adds _extract_user_context() static helper for consistent extraction of (user_email, token_teams, request_headers) from handler kwargs. _get_tool_categories remains hardcoded to actor_scope=public_user which is already restrictive by default. Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
Move the meta-server checkbox above the tool/resource/prompt selection sections in both the create and edit virtual server modals. When meta-server mode is enabled, the selectors are hidden and replaced with an info banner explaining that a meta-server dynamically exposes all items based on the user's access rights. Changes: - admin.html: move meta-server checkbox before tool selection in both create and edit modals; wrap tools/resources/prompts sections in togglable container divs; add info banner - servers.js: toggle items section and info banner when populating the edit modal based on server_type Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
added 6 commits
April 27, 2026 11:35
Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
… tests - Remove duplicate meta-server sections in CREATE and EDIT modals (admin.html) - Remove dead imports: ToolEmbedding, top-level or_ (service.py) - Fix docstring: meta-server exposes 12 tools, not 6 (service.py) - Factor _resolve_effective_email() helper for JWT extraction (service.py) - Factor _apply_scope_header() helper for X-Scope processing (meta_router.py) - Move inline 'import json' to module level (meta_router.py) - Fix test assertion: len(defs) == 12, add new meta-tool assertions (test_meta_server.py) Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
Replace single-block substring matching with tokenized multi-word search. Query is split on whitespace/hyphens/underscores into tokens, each matched independently against tool name tokens and description. Scoring weights name matches higher than description matches: - 1.0: exact full name match - 0.95: all tokens match, majority in name - 0.85: all tokens match, mostly in description - 0.6-0.8: partial match with name hits - 0.3-0.6: partial match, description only This fixes queries like 'compile context compiler' that previously returned 0 results because the entire string was used as one block. Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
… search limit The keyword search was passing the search result limit (default 50) to ToolService.list_tools, causing it to only scan the first 50 tools instead of the full catalog (200+). Use limit=0 to fetch all tools for matching, then paginate after scoring. Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
…arity - list_tools: implement sort_by name/created_at with sort_order asc/desc - describe_tool: use gateway_id as primary source for serverId (not M2M servers) - tag inheritance: tools with no tags inherit from their parent Gateway - get_similar_tools: keyword-based Jaccard fallback when no embeddings available (same-gateway boost) Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
Replace ToolService.get_tool_categories delegation with direct DB query that applies the same tag inheritance logic as other meta-tools: when a tool has no tags of its own, inherit from the parent Gateway. Categories are aggregated by tag name with tool counts. Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
4ef3fce to
562354a
Compare
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.
🔗 Related Issue
Closes #2230
Supersedes #3653
📝 Summary
This PR implements the Virtual Meta-Server feature — a comprehensive tool aggregation layer that enables AI agents to discover and invoke thousands of underlying tools through a unified interface. It exposes 12 meta-tools to the agent while completely hiding the complexity of the underlying tool ecosystem.
This is a production-tested alternative to PR #3653, with additional features, security hardening, and 2+ weeks of real-world testing with VS Code Copilot and Copilot Studio.
Problem Statement
Traditional MCP architectures expose all tools directly to AI agents, leading to:
Solution — 12 Meta-Tools
Core (same as #3653)
search_tools: hybrid semantic + keyword search with scope filteringlist_tools: paginated tool listing with sorting and filteringdescribe_tool: detailed tool info with schema and metadataexecute_tool: tool execution with JSON schema validation and routingget_tool_categories: aggregated categories with countsget_similar_tools: vector similarity search for related toolsNew (beyond #3653)
authorize_gateway: interactive OAuth authorization with token refreshauthorize_all_gateways: one-click authorization for all OAuth gatewayslist_resources: paginated MCP resource listingread_resource: read MCP resource content by URIlist_prompts: paginated MCP prompt listingget_prompt: prompt template retrieval with optional renderingKey Improvements Over #3653
Security Fixes (vs #3653)
The original PR #3653 introduced several security regressions that this PR avoids:
x-forwarded-internallyheader validation against loopback IP is maintained (the original PR removed this check)protect_oauth_config_for_storage()is called on server updates (the original PR removed this)orjson.dumps()instead of Pythonstr()which produces invalid JSON with single quoteswrapsfromfunctoolsis properly imported (the original PR had aNameErrorat startup)_is_safe_local_path()validation to prevent open redirect attacks🏷️ Type of Change
Test Coverage
📓 Notes
Files Changed (26 files, +6584/-23)
New Files
mcpgateway/meta_server/__init__.py— Package initializationmcpgateway/meta_server/schemas.py— Pydantic models for all 12 meta-toolsmcpgateway/meta_server/service.py— MetaServerService with all handler implementationsmcpgateway/routers/meta_router.py— HTTP endpoints under/meta/*mcpgateway/services/meta_tool_service.py— Business logic for describe/executemcpgateway/services/semantic_search_service.py— Semantic search stub (graceful fallback)mcpgateway/services/vector_search_service.py— Vector search with numpy fallbackmcpgateway/services/embedding_service.py— Embedding stub (no-op without model)mcpgateway/utils/pgvector.py— pgvector compatibility shimmcpgateway/alembic/versions/5126ced48fd0— Migration for meta-server fieldsModified Files
mcpgateway/schemas.py— server_type, hide_underlying_tools, meta_config, meta_scope fieldsmcpgateway/db.py— ToolEmbedding model + meta-server fields on Servermcpgateway/transports/streamablehttp_transport.py— Tool hiding + meta-tool routingmcpgateway/middleware/rbac.py— Login redirect with ?next= preservationmcpgateway/routers/oauth_router.py— Chained OAuth flow + authorize-all endpointmcpgateway/routers/sso.py— Post-login redirect cookie handlingmcpgateway/services/server_service.py— Meta-server field handlingmcpgateway/services/tool_service.py— Absolute OAuth authorize URLmcpgateway/admin.py— Meta-server fields in admin UImcpgateway/static/admin.js— Meta-server JS configmcpgateway/templates/admin.html— Meta-server form fieldsmcpgateway/templates/gateways_partial.html— Authorize button for non-admin usersmcpgateway/main.py— Router registrationmcpgateway/config.py— semantic_search_rate_limit settingTests
tests/unit/mcpgateway/test_meta_server.py— 2257 lines covering all meta-toolstests/unit/mcpgateway/services/test_meta_tool_service.py— 180 lines for tool serviceMigration & Compatibility
server_type="standard"server_type="meta"