Skip to content

feat(api): add Virtual Meta Server implementation#3653

Closed
AbdulR11 wants to merge 1 commit intoIBM:mainfrom
AbdulR11:Virtual-Meta-Server-Implementation
Closed

feat(api): add Virtual Meta Server implementation#3653
AbdulR11 wants to merge 1 commit intoIBM:mainfrom
AbdulR11:Virtual-Meta-Server-Implementation

Conversation

@AbdulR11
Copy link
Copy Markdown

🔗 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:

  • Context window exhaustion with hundreds or thousands of tools
  • Poor agent decision-making due to overwhelming tool choices
  • Lack of dynamic tool discovery - agents must know tools upfront
  • No semantic search capabilities - keyword matching only

Solution

  • 6 unified meta-tools for discovery and execution (search_tools, list_tools, describe_tool, execute_tool, get_tool_categories, get_similar_tools)
  • Semantic search via embedding-based similarity
  • Hybrid search combining semantic + keyword (BM25) ranking
  • Tool hiding - underlying tools invisible to agents when server_type='meta'
  • Scope-based filtering - fine-grained access control
  • Enterprise-grade observability - full audit trail

Key Components

  • Meta-Server Module: New mcpgateway/meta_server/ package with schemas and service
  • Embedding Service: Tool embedding generation with pgvector support
  • Semantic Search: Hybrid search combining semantic similarity + BM25 keyword ranking
  • Vector Search: Cosine similarity search (pgvector for PostgreSQL, numpy fallback for SQLite)
  • Transport Integration: Tool hiding and meta-tool routing in streamablehttp_transport.py
  • API Endpoints: 6 new endpoints under /meta/*
  • Database: New ToolEmbedding model and server_type field on servers table

🏷️ Type of Change

  • Bug fix
  • Feature / Enhancement
  • Documentation
  • Refactor
  • Chore (deps, CI, tooling)
  • Other (describe below)

Test Coverage

  • ✅ ServerType enum validation, MetaToolScope filtering (all filter types)
  • ✅ All 6 meta-tool request/response schemas
  • ✅ MetaServerService business logic, tool hiding enforcement
  • ✅ Scope evaluation (AND semantics), edge cases
  • ✅ Meta-server creation end-to-end
  • ✅ Meta-tool execution via HTTP endpoints
  • ✅ Performance tests with 1000+ tools

📓 Notes

Files Changed

Core Implementation

  • mcpgateway/meta_server/__init__.py - Package initialization
  • mcpgateway/meta_server/schemas.py - Pydantic models (ServerType, MetaToolScope, MetaConfig, all request/response schemas)
  • mcpgateway/meta_server/service.py - MetaServerService with all 6 meta-tool implementations
  • mcpgateway/routers/meta_router.py - HTTP endpoints for meta-tools
  • mcpgateway/services/meta_tool_service.py - Meta-tool coordination layer
  • mcpgateway/services/server_service.py - Meta-server field handling
  • mcpgateway/services/dynamic_server_service.py - Semantic search integration
  • mcpgateway/transports/streamablehttp_transport.py - Tool hiding & meta-tool routing

Database & Schemas

  • mcpgateway/db.py - ToolEmbedding model, server table extensions
  • mcpgateway/alembic/versions/5126ced48fd0_add_meta_server_fields.py - Migration
  • mcpgateway/schemas.py - ServerCreate/Update/Read with meta-server fields

Application Integration

  • mcpgateway/main.py - Router registration

Tests

  • tests/unit/mcpgateway/test_meta_server.py - Comprehensive meta-server tests
  • tests/unit/mcpgateway/services/test_meta_tool_service.py - Meta-tool service tests

Usage 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:

# Semantic search for tools
POST /meta/search_tools
{
  "query": "analyze customer data",
  "limit": 10,
  "filters": {"include_tags": ["analytics"]}
}

# Execute discovered tool
POST /meta/execute_tool
{
  "tool_name": "analyze_customer_churn",
  "arguments": {"dataset": "customers_2024"}
}

Benefits

For AI Agents:

  • Reduced context usage: 6 meta-tools vs. 1000+ real tools
  • Dynamic discovery via semantic search
  • Better decision-making with focused tool set

For Platform Operators:

  • Centralized governance via scopes
  • Full observability and audit trail
  • Scalability for thousands of tools
  • RBAC + scope-based access control

Migration & Compatibility

  • Backward compatible: Existing servers default to server_type='standard'
  • Additive changes: No breaking changes to existing endpoints
  • Opt-in feature: Meta-servers explicitly created with server_type='meta'
  • Graceful degradation: Semantic search falls back to keyword search if embeddings unavailable

Configuration

# Embedding Service
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_PROVIDER=openai
OPENAI_API_KEY=sk-...

# Vector Search  
DATABASE_URL=postgresql+psycopg://user:pass@host/db  # pgvector support

@AbdulR11 AbdulR11 marked this pull request as ready for review March 12, 2026 13:23
Copilot AI review requested due to automatic review settings March 12, 2026 13:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 MetaToolService covering 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.

Comment thread mcpgateway/main.py
Comment on lines +1226 to +1231
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."""
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread mcpgateway/main.py
Comment on lines 6116 to 6122
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"

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +2292 to 2293
is_internally_forwarded = headers.get("x-forwarded-internally") == "true"

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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
)

Copilot uses AI. Check for mistakes.
Comment on lines 1135 to +1138
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)))
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_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.

Copilot uses AI. Check for mistakes.
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"))))
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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))

Copilot uses AI. Check for mistakes.
Comment on lines 1304 to +1307
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
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +62
@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:
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +164 to +171
@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:
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/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).

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +92
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
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +270 to +272
from sqlalchemy import or_

query = query.where(or_(DbTool.visibility == "public", DbTool.team_id.in_(token_teams)))
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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)),
)
)

Copilot uses AI. Check for mistakes.
@brian-hussey brian-hussey added the tcd SwEng Projects label Mar 13, 2026
@crivetimihai crivetimihai added this to the Release 1.2.0 milestone Mar 14, 2026
ecthelion77 pushed a commit to ecthelion77/mcp-context-forge that referenced this pull request Mar 17, 2026
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
ecthelion77 pushed a commit to ecthelion77/mcp-context-forge that referenced this pull request Mar 18, 2026
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
@crivetimihai crivetimihai changed the title add Virtual Meta Server Implementation feat(api): add Virtual Meta Server implementation Mar 20, 2026
@crivetimihai crivetimihai added the COULD P3: Nice-to-have features with minimal impact if left out; included if time permits label Mar 20, 2026
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 1, 2026
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
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 1, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 3, 2026
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>
@jonpspri
Copy link
Copy Markdown
Collaborator

jonpspri commented Apr 9, 2026

Deferred to #3978

@jonpspri jonpspri closed this Apr 9, 2026
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 13, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 14, 2026
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
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 14, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 14, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 14, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 14, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 16, 2026
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>
ecthelion77 pushed a commit to forterro/mcp-context-forge that referenced this pull request Apr 16, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

COULD P3: Nice-to-have features with minimal impact if left out; included if time permits tcd SwEng Projects

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Virtual meta-server - Comprehensive tool discovery and execution layer

5 participants