feat(api): add Virtual Meta Server implementation#3653
feat(api): add Virtual Meta Server implementation#3653
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces “meta-server / meta-tool” functionality to MCP Gateway so clients can discover, describe, and execute tools via dedicated meta endpoints/handlers, with additional transport-layer support for meta-tool dispatch and session-affinity forwarding behavior.
Changes:
- Add meta-tool API surface (FastAPI
/meta/*routes) and service-layer logic for tool description/execution with schema validation. - Update Streamable HTTP transport to support meta-server behavior (meta-tool short-circuiting, meta-tool-only listing) and adjust forwarding/logging/rehydration behavior.
- Add unit tests for
MetaToolServicecovering describe/execute paths and error handling.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
mcpgateway/main.py |
Adds new behaviors (e.g., semantic search rate-limiting) and adjusts session-affinity/internal-forwarding logic. |
mcpgateway/transports/streamablehttp_transport.py |
Implements meta-tool dispatch/listing for meta-servers and modifies request forwarding and content rehydration behavior. |
mcpgateway/routers/meta_router.py |
Adds /meta/* FastAPI endpoints for meta-tool operations (describe/execute/search/list/similar). |
mcpgateway/services/meta_tool_service.py |
Implements meta-tool describe/execute logic, including tool resolution and JSON-schema validation. |
mcpgateway/services/server_service.py |
Extends server read/create/update handling for meta-server fields and OAuth config handling. |
tests/unit/mcpgateway/services/test_meta_tool_service.py |
Adds unit tests validating meta-tool service behavior for success and error cases. |
Comments suppressed due to low confidence (1)
mcpgateway/main.py:5863
- These root endpoints are no longer protected by
@require_permission(["admin.system_config"]), which means any authenticated caller could potentially list and modify root server configuration (and possibly unauthenticated callers depending on global auth settings). If this de-authorization is unintentional, restore the permission guard (or replace with an equivalent authorization check).
@root_router.get("", response_model=List[Root])
@root_router.get("/", response_model=List[Root])
async def list_roots(
user=Depends(get_current_user_with_permissions),
) -> List[Root]:
"""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| def decorator(func_to_wrap): | ||
| """Decorator that wraps the function with rate limiting logic.""" | ||
|
|
||
| @wraps(func_to_wrap) | ||
| async def wrapper(*args, request: Optional[Request] = None, **kwargs): | ||
| """Execute the wrapped function with rate limiting enforcement.""" |
There was a problem hiding this comment.
semantic_search_rate_limit() uses @wraps(func_to_wrap) but wraps is not imported anywhere in this module, which will raise a NameError the first time this decorator is evaluated (likely at import time). Import wraps from functools (or remove the decorator) to avoid breaking app startup.
| headers = {k.lower(): v for k, v in request.headers.items()} | ||
| # Session ID can come from two sources: | ||
| # 1. MCP-Session-Id (mcp-session-id) - MCP protocol header from Streamable HTTP clients | ||
| # 2. x-mcp-session-id - our internal header from SSE session_registry calls | ||
| mcp_session_id = headers.get("mcp-session-id") or headers.get("x-mcp-session-id") | ||
| # Only trust x-forwarded-internally from loopback to prevent external spoofing | ||
| _rpc_client_host = request.client.host if request.client else None | ||
| _rpc_from_loopback = _rpc_client_host in ("127.0.0.1", "::1") if _rpc_client_host else False | ||
| is_internally_forwarded = _rpc_from_loopback and headers.get("x-forwarded-internally") == "true" | ||
| is_internally_forwarded = headers.get("x-forwarded-internally") == "true" | ||
|
|
There was a problem hiding this comment.
is_internally_forwarded now trusts x-forwarded-internally: true without validating the request origin (e.g., loopback/sidecar or another trusted hop). This allows external clients to set the header and potentially bypass internal-only protections/flows. Please reintroduce a source validation (remote addr / trusted proxy list / mTLS) before treating a request as internally forwarded.
| is_internally_forwarded = headers.get("x-forwarded-internally") == "true" | ||
|
|
There was a problem hiding this comment.
is_internally_forwarded is now determined solely by the x-forwarded-internally header value. Without verifying the request actually came from a trusted internal forwarder, external clients can spoof this header and trigger internal-forward paths (potentially bypassing auth/session-affinity rules). Add an origin/trust check (loopback/allowlist/mTLS) before trusting this header.
| is_internally_forwarded = headers.get("x-forwarded-internally") == "true" | |
| client = scope.get("client") | |
| is_trusted_internal_origin = False | |
| if isinstance(client, tuple) and len(client) >= 1: | |
| client_host = client[0] | |
| # Only treat x-forwarded-internally as authoritative when the request | |
| # originates from a trusted internal source (loopback). | |
| if client_host in ("127.0.0.1", "::1"): | |
| is_trusted_internal_origin = True | |
| is_internally_forwarded = ( | |
| headers.get("x-forwarded-internally") == "true" | |
| and is_trusted_internal_origin | |
| ) |
| else: | ||
| converted.append(types.TextContent(type="text", text=item if isinstance(item, str) else orjson.dumps(item).decode())) | ||
| converted.append(types.TextContent(type="text", text=str(item))) | ||
| except Exception: | ||
| converted.append(types.TextContent(type="text", text=item if isinstance(item, str) else orjson.dumps(item).decode())) | ||
| converted.append(types.TextContent(type="text", text=str(item))) |
There was a problem hiding this comment.
_rehydrate_content_items() now falls back to str(item) for unknown content types. For dict/list payloads this produces non-JSON (single quotes, Python repr), which can break downstream MCP clients expecting JSON-compatible text. Prefer encoding via orjson.dumps(...) (with a safe fallback) so the representation is stable and JSON.
| else: | ||
| # Unknown content type - convert to text representation | ||
| unstructured.append(types.TextContent(type="text", text=orjson.dumps(content.model_dump(by_alias=True, mode="json")).decode())) | ||
| unstructured.append(types.TextContent(type="text", text=str(content.model_dump(by_alias=True, mode="json")))) |
There was a problem hiding this comment.
For unknown content types, _convert_meta() now uses str(content.model_dump(...)), which again yields a Python dict repr (not JSON) and can make client parsing inconsistent. Prefer serializing the model dump as JSON (e.g., orjson.dumps(...)) to keep the wire representation stable.
| unstructured.append(types.TextContent(type="text", text=str(content.model_dump(by_alias=True, mode="json")))) | |
| if hasattr(content, "model_dump"): | |
| dumped = content.model_dump(by_alias=True, mode="json") | |
| text_value = orjson.dumps(dumped).decode("utf-8") | |
| else: | |
| text_value = str(content) | |
| unstructured.append(types.TextContent(type="text", text=text_value)) |
| if hasattr(server_update, "model_fields_set") and "oauth_config" in server_update.model_fields_set: | ||
| server.oauth_config = await protect_oauth_config_for_storage(server_update.oauth_config, existing_oauth_config=server.oauth_config) | ||
| server.oauth_config = server_update.oauth_config | ||
| elif server_update.oauth_config is not None: | ||
| server.oauth_config = await protect_oauth_config_for_storage(server_update.oauth_config, existing_oauth_config=server.oauth_config) | ||
| server.oauth_config = server_update.oauth_config |
There was a problem hiding this comment.
update_server() assigns server.oauth_config = oauth_config directly. If oauth_config contains client secrets/refresh tokens, this stores them without applying protect_oauth_config_for_storage() (which is used elsewhere) and also interacts with the removal of ServerRead.masked() to increase exposure risk. Please ensure server OAuth config is protected at rest and never returned unmasked to unauthorized callers.
| @router.post("/describe_tool", response_model=DescribeToolResponse) | ||
| async def describe_tool( | ||
| req: DescribeToolRequest, | ||
| request: Request, | ||
| current_user_ctx: dict = Depends(get_current_user_with_permissions), | ||
| db: Session = Depends(get_db), | ||
| x_scope: Optional[str] = Header(None, alias="X-Scope"), | ||
| ) -> DescribeToolResponse: |
There was a problem hiding this comment.
Meta-tool endpoints are only gated by get_current_user_with_permissions but do not require specific permissions. As written, any authenticated user could potentially describe/execute tools (and depending on global auth settings, possibly unauthenticated users). These routes should enforce RBAC explicitly (e.g., tools.read for describe/search/list, and tools.execute for execute).
| @router.post("/search_tools", response_model=SearchToolsResponse) | ||
| async def search_tools( | ||
| req: SearchToolsRequest, | ||
| request: Request, | ||
| current_user_ctx: dict = Depends(get_current_user_with_permissions), | ||
| db: Session = Depends(get_db), | ||
| x_scope: Optional[str] = Header(None, alias="X-Scope"), | ||
| ) -> SearchToolsResponse: |
There was a problem hiding this comment.
/meta/search_tools (and related endpoints) likewise lack explicit permission checks beyond authentication. Given these can enumerate tool metadata across the system, please add RBAC enforcement consistent with the rest of the API (e.g., require tools.read).
| x_scope: Optional[str] = Header(None, alias="X-Scope"), | ||
| ) -> DescribeToolResponse: | ||
| """Get detailed information about a specific tool including schema and metadata. | ||
|
|
||
| Args: | ||
| req: Describe tool request | ||
| request: FastAPI request object | ||
| current_user_ctx: Current user context with permissions | ||
| db: Database session | ||
| x_scope: Optional scope header for filtering | ||
|
|
||
| Returns: | ||
| DescribeToolResponse: Tool details | ||
|
|
||
| Raises: | ||
| HTTPException: If tool is not found or access is denied | ||
| """ | ||
| try: | ||
| service = MetaToolService(db) | ||
| user_email = current_user_ctx.get("email") | ||
| token_teams = current_user_ctx.get("teams") | ||
| is_admin = current_user_ctx.get("is_admin", False) | ||
|
|
||
| response = await service.describe_tool( | ||
| tool_name=req.tool_name, | ||
| include_metrics=req.include_metrics, | ||
| user_email=user_email, | ||
| token_teams=token_teams, | ||
| is_admin=is_admin, | ||
| scope=x_scope, | ||
| ) | ||
| return response |
There was a problem hiding this comment.
X-Scope is treated inconsistently across meta endpoints: describe_tool/execute_tool pass the raw header string into MetaToolService (which expects values like public, team:<id>, private), while search_tools/list_tools/get_similar_tools parse X-Scope as JSON into a dict-based scope. This mismatch makes the API confusing and easy to misuse (e.g., clients sending JSON scope to describe/execute will silently lose scoping). Consider standardizing the X-Scope format across all meta endpoints.
| from sqlalchemy import or_ | ||
|
|
||
| query = query.where(or_(DbTool.visibility == "public", DbTool.team_id.in_(token_teams))) |
There was a problem hiding this comment.
Team-based filtering in _resolve_tool() includes any tool with team_id in token_teams regardless of the tool's visibility. This means a tool marked visibility="private" could still be returned to any member of that team. Please restrict the team clause to team-visible tools (e.g., visibility in {"public","team"}) and keep private tools owner-only.
| from sqlalchemy import or_ | |
| query = query.where(or_(DbTool.visibility == "public", DbTool.team_id.in_(token_teams))) | |
| from sqlalchemy import and_, or_ | |
| query = query.where( | |
| or_( | |
| DbTool.visibility == "public", | |
| and_(DbTool.visibility == "team", DbTool.team_id.in_(token_teams)), | |
| ) | |
| ) |
Based on PR IBM#3653 (AbdulR11/Virtual-Meta-Server-Implementation). Surgically extracted and rebased onto current main. Adds 6 meta-tools (search_tools, list_tools, describe_tool, execute_tool, get_tool_categories, get_similar_tools) that abstract over underlying tools, addressing the VS Code 128-tool limit (issue IBM#2230). Includes: - Meta-server schemas, service, router, and admin UI - Alembic migration for server_type/meta_config/meta_scope fields - ToolEmbedding model in db.py (pgvector-ready) - Compatibility stubs for semantic/vector search and embedding service (keyword search works; semantic search requires pgvector + embeddings) - Security: preserves loopback SSRF validation, OAuth secret protection, orjson serialization, and functools.wraps import Refs: IBM#2230, PR IBM#3653
Based on PR IBM#3653 (AbdulR11/Virtual-Meta-Server-Implementation). Surgically extracted and rebased onto current main. Adds 6 meta-tools (search_tools, list_tools, describe_tool, execute_tool, get_tool_categories, get_similar_tools) that abstract over underlying tools, addressing the VS Code 128-tool limit (issue IBM#2230). Includes: - Meta-server schemas, service, router, and admin UI - Alembic migration for server_type/meta_config/meta_scope fields - ToolEmbedding model in db.py (pgvector-ready) - Compatibility stubs for semantic/vector search and embedding service (keyword search works; semantic search requires pgvector + embeddings) - Security: preserves loopback SSRF validation, OAuth secret protection, orjson serialization, and functools.wraps import Refs: IBM#2230, PR IBM#3653
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. This is a comprehensive alternative to PR IBM#3653 with additional features, security fixes, and production-tested reliability. ## Core Meta-Tools (6 — same as IBM#3653) - 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 ## Additional Meta-Tools (6 — new) - 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 ## Key Improvements Over IBM#3653 - OAuth integration: propagates user identity through the call chain so execute_tool works with OAuth-protected gateways - Authorize meta-tools: AI agents can trigger OAuth flows interactively - Chained OAuth flow: authorize-all endpoint chains multiple gateway authorizations via cookie-based flow - camelCase normalization: MCP clients send camelCase, handlers expect snake_case — automatic conversion - Flat argument tolerance: Copilot Studio sends arguments flat instead of nested — detected and restructured automatically - Post-login redirect: SSO login preserves original destination (e.g. OAuth authorize URL) via cookie - Observability: prompt.render and resource.read spans for the admin dashboard - JSON serialization: uses orjson.dumps() instead of str() for proper JSON output to MCP clients ## Security (fixes vs IBM#3653) - Preserves loopback validation for x-forwarded-internally (SSRF) - Preserves protect_oauth_config_for_storage() on server update - Preserves orjson serialization (not str() which breaks JSON) - Includes wraps import (prevents NameError at startup) - RBAC enforcement via middleware on all meta endpoints - Login redirect uses safe path validation (no open redirect) ## Files - New: mcpgateway/meta_server/ (package with schemas + service) - New: mcpgateway/routers/meta_router.py (HTTP endpoints) - New: mcpgateway/services/meta_tool_service.py (business logic) - New: mcpgateway/services/{semantic,vector}_search_service.py (stubs) - New: mcpgateway/services/embedding_service.py (stub) - New: mcpgateway/utils/pgvector.py (compatibility shim) - New: mcpgateway/alembic/versions/5126ced48fd0 (migration) - Modified: schemas, db, transport, admin, rbac, oauth, sso, config - Tests: 2400+ lines covering all meta-tools and edge cases Closes IBM#2230 Supersedes IBM#3653
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. This is a comprehensive alternative to PR IBM#3653 with additional features, security fixes, and production-tested reliability. ## Core Meta-Tools (6 — same as IBM#3653) - 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 ## Additional Meta-Tools (6 — new) - 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 ## Key Improvements Over IBM#3653 - OAuth integration: propagates user identity through the call chain so execute_tool works with OAuth-protected gateways - Authorize meta-tools: AI agents can trigger OAuth flows interactively - Chained OAuth flow: authorize-all endpoint chains multiple gateway authorizations via cookie-based flow - camelCase normalization: MCP clients send camelCase, handlers expect snake_case — automatic conversion - Flat argument tolerance: Copilot Studio sends arguments flat instead of nested — detected and restructured automatically - Post-login redirect: SSO login preserves original destination (e.g. OAuth authorize URL) via cookie - Observability: prompt.render and resource.read spans for the admin dashboard - JSON serialization: uses orjson.dumps() instead of str() for proper JSON output to MCP clients ## Security (fixes vs IBM#3653) - Preserves loopback validation for x-forwarded-internally (SSRF) - Preserves protect_oauth_config_for_storage() on server update - Preserves orjson serialization (not str() which breaks JSON) - Includes wraps import (prevents NameError at startup) - RBAC enforcement via middleware on all meta endpoints - Login redirect uses safe path validation (no open redirect) ## Files - New: mcpgateway/meta_server/ (package with schemas + service) - New: mcpgateway/routers/meta_router.py (HTTP endpoints) - New: mcpgateway/services/meta_tool_service.py (business logic) - New: mcpgateway/services/{semantic,vector}_search_service.py (stubs) - New: mcpgateway/services/embedding_service.py (stub) - New: mcpgateway/utils/pgvector.py (compatibility shim) - New: mcpgateway/alembic/versions/5126ced48fd0 (migration) - Modified: schemas, db, transport, admin, rbac, oauth, sso, config - Tests: 2400+ lines covering all meta-tools and edge cases Closes IBM#2230 Supersedes IBM#3653 Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
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. This is a comprehensive alternative to PR IBM#3653 with additional features, security fixes, and production-tested reliability. - 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 - OAuth integration: propagates user identity through the call chain so execute_tool works with OAuth-protected gateways - Authorize meta-tools: AI agents can trigger OAuth flows interactively - Chained OAuth flow: authorize-all endpoint chains multiple gateway authorizations via cookie-based flow - camelCase normalization: MCP clients send camelCase, handlers expect snake_case — automatic conversion - Flat argument tolerance: Copilot Studio sends arguments flat instead of nested — detected and restructured automatically - Post-login redirect: SSO login preserves original destination (e.g. OAuth authorize URL) via cookie - Observability: prompt.render and resource.read spans for the admin dashboard - JSON serialization: uses orjson.dumps() instead of str() for proper JSON output to MCP clients - Preserves loopback validation for x-forwarded-internally (SSRF) - Preserves protect_oauth_config_for_storage() on server update - Preserves orjson serialization (not str() which breaks JSON) - Includes wraps import (prevents NameError at startup) - RBAC enforcement via middleware on all meta endpoints - Login redirect uses safe path validation (no open redirect) - New: mcpgateway/meta_server/ (package with schemas + service) - New: mcpgateway/routers/meta_router.py (HTTP endpoints) - New: mcpgateway/services/meta_tool_service.py (business logic) - New: mcpgateway/services/{semantic,vector}_search_service.py (stubs) - New: mcpgateway/services/embedding_service.py (stub) - New: mcpgateway/utils/pgvector.py (compatibility shim) - New: mcpgateway/alembic/versions/5126ced48fd0 (migration) - Modified: schemas, db, transport, admin, rbac, oauth, sso, config - Tests: 2400+ lines covering all meta-tools and edge cases Closes IBM#2230 Supersedes IBM#3653 Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
|
Deferred to #3978 |
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. This is a comprehensive alternative to PR IBM#3653 with additional features, security fixes, and production-tested reliability. - 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 - OAuth integration: propagates user identity through the call chain so execute_tool works with OAuth-protected gateways - Authorize meta-tools: AI agents can trigger OAuth flows interactively - Chained OAuth flow: authorize-all endpoint chains multiple gateway authorizations via cookie-based flow - camelCase normalization: MCP clients send camelCase, handlers expect snake_case — automatic conversion - Flat argument tolerance: Copilot Studio sends arguments flat instead of nested — detected and restructured automatically - Post-login redirect: SSO login preserves original destination (e.g. OAuth authorize URL) via cookie - Observability: prompt.render and resource.read spans for the admin dashboard - JSON serialization: uses orjson.dumps() instead of str() for proper JSON output to MCP clients - Preserves loopback validation for x-forwarded-internally (SSRF) - Preserves protect_oauth_config_for_storage() on server update - Preserves orjson serialization (not str() which breaks JSON) - Includes wraps import (prevents NameError at startup) - RBAC enforcement via middleware on all meta endpoints - Login redirect uses safe path validation (no open redirect) - New: mcpgateway/meta_server/ (package with schemas + service) - New: mcpgateway/routers/meta_router.py (HTTP endpoints) - New: mcpgateway/services/meta_tool_service.py (business logic) - New: mcpgateway/services/{semantic,vector}_search_service.py (stubs) - New: mcpgateway/services/embedding_service.py (stub) - New: mcpgateway/utils/pgvector.py (compatibility shim) - New: mcpgateway/alembic/versions/5126ced48fd0 (migration) - Modified: schemas, db, transport, admin, rbac, oauth, sso, config - Tests: 2400+ lines covering all meta-tools and edge cases Closes IBM#2230 Supersedes IBM#3653 Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
Based on PR IBM#3653 (AbdulR11/Virtual-Meta-Server-Implementation). Surgically extracted and rebased onto current main. Adds 6 meta-tools (search_tools, list_tools, describe_tool, execute_tool, get_tool_categories, get_similar_tools) that abstract over underlying tools, addressing the VS Code 128-tool limit (issue IBM#2230). Includes: - Meta-server schemas, service, router, and admin UI - Alembic migration for server_type/meta_config/meta_scope fields - ToolEmbedding model in db.py (pgvector-ready) - Compatibility stubs for semantic/vector search and embedding service (keyword search works; semantic search requires pgvector + embeddings) - Security: preserves loopback SSRF validation, OAuth secret protection, orjson serialization, and functools.wraps import Refs: IBM#2230, PR IBM#3653
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>
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>
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>
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>
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>
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>
🔗 Related Issue
Closes #2230
📝 Summary
This PR introduces the Virtual Meta-Server feature, a powerful abstraction layer that enables AI agents to discover and invoke thousands of underlying tools through a unified, simple interface. The meta-server exposes only 6 meta-tools to the agent while completely hiding the complexity of the underlying tool ecosystem.
Problem Statement
Traditional MCP architectures expose all tools directly to AI agents, leading to:
Solution
search_tools,list_tools,describe_tool,execute_tool,get_tool_categories,get_similar_tools)server_type='meta'Key Components
mcpgateway/meta_server/package with schemas and servicestreamablehttp_transport.py/meta/*ToolEmbeddingmodel andserver_typefield on servers table🏷️ Type of Change
Test Coverage
📓 Notes
Files Changed
Core Implementation
mcpgateway/meta_server/__init__.py- Package initializationmcpgateway/meta_server/schemas.py- Pydantic models (ServerType, MetaToolScope, MetaConfig, all request/response schemas)mcpgateway/meta_server/service.py- MetaServerService with all 6 meta-tool implementationsmcpgateway/routers/meta_router.py- HTTP endpoints for meta-toolsmcpgateway/services/meta_tool_service.py- Meta-tool coordination layermcpgateway/services/server_service.py- Meta-server field handlingmcpgateway/services/dynamic_server_service.py- Semantic search integrationmcpgateway/transports/streamablehttp_transport.py- Tool hiding & meta-tool routingDatabase & Schemas
mcpgateway/db.py- ToolEmbedding model, server table extensionsmcpgateway/alembic/versions/5126ced48fd0_add_meta_server_fields.py- Migrationmcpgateway/schemas.py- ServerCreate/Update/Read with meta-server fieldsApplication Integration
mcpgateway/main.py- Router registrationTests
tests/unit/mcpgateway/test_meta_server.py- Comprehensive meta-server teststests/unit/mcpgateway/services/test_meta_tool_service.py- Meta-tool service testsUsage Example
Creating a Meta-Server:
POST /servers { "name": "Production Meta-Server", "server_type": "meta", "hide_underlying_tools": true, "meta_scope": { "include_tags": ["production"], "exclude_tags": ["deprecated"], "include_visibility": ["team", "public"] }, "meta_config": { "enable_semantic_search": true, "default_search_limit": 25, "similarity_threshold": 0.7 } }Using Meta-Tools:
Benefits
For AI Agents:
For Platform Operators:
Migration & Compatibility
server_type='standard'server_type='meta'Configuration