diff --git a/CODEOWNERS b/CODEOWNERS index 2d30fa8a9222f..f8ca47ac312cc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -234,6 +234,8 @@ extensions/upstreams/tcp @ggreenway @mattklein123 /*/extensions/filters/http/api_key_auth @wbpcode @sanposhiho # HTTP MCP filter /*/extensions/filters/http/mcp @botengyao @yanavlasov +# MCP router filter +/*/extensions/filters/http/mcp_router @botengyao @yanavlasov @wdauchy # Original IP detection /*/extensions/http/original_ip_detection/custom_header @ryantheoptimist @mattklein123 /*/extensions/http/original_ip_detection/xff @yanavlasov @mattklein123 diff --git a/api/BUILD b/api/BUILD index 49ecae5f3e52d..8f9b1da5faada 100644 --- a/api/BUILD +++ b/api/BUILD @@ -213,6 +213,7 @@ proto_library( "//envoy/extensions/filters/http/local_ratelimit/v3:pkg", "//envoy/extensions/filters/http/lua/v3:pkg", "//envoy/extensions/filters/http/mcp/v3:pkg", + "//envoy/extensions/filters/http/mcp_router/v3:pkg", "//envoy/extensions/filters/http/oauth2/v3:pkg", "//envoy/extensions/filters/http/on_demand/v3:pkg", "//envoy/extensions/filters/http/original_src/v3:pkg", diff --git a/api/envoy/extensions/filters/http/mcp_router/v3/BUILD b/api/envoy/extensions/filters/http/mcp_router/v3/BUILD new file mode 100644 index 0000000000000..09a37ad16b837 --- /dev/null +++ b/api/envoy/extensions/filters/http/mcp_router/v3/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto b/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto new file mode 100644 index 0000000000000..8cef47b7a3273 --- /dev/null +++ b/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.mcp_router.v3; + +import "envoy/config/core/v3/http_uri.proto"; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.mcp_router.v3"; +option java_outer_classname = "McpRouterProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/mcp_router/v3;mcp_routerv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: MCP Multiplexer/Demultiplexer] +// [#extension: envoy.filters.http.mcp_router] + +// Configuration for the MCP Multiplexer/Demultiplexer. +// +// This extension aggregates capabilities, tools and resources of remote MCP servers and presents Envoy +// as a singe MCP server to the client. This allows a unified policy to be applied to multiple remote +// servers and abstracts multiple MCP servers as a single one. +// +// This filter must be a terminal filter in the filter chain and replaces the HTTP router filter. +// +// Not all route level policies are applicable to this filter. +// Specifically the following policies are ignored: +// * :ref:`route ` +// * :ref:`redirect ` +// * :ref:`direct_response ` +// +message McpRouter { + // Specification of the MCP server. + message McpBackend { + // User visible name of the server for logging or error messages. If the name is empty the + // :ref:`URI ` value is used. + string name = 1; + + // Specification of the remote MCP server. + config.core.v3.HttpUri http_uri = 2; + } + + // A list of remote MCP servers. MCP router aggregates capabilities, tools and resources from remote MCP servers + // and presents itself as single MCP server to the client. All remote MCP servers are sent the same capabilities + // that the client presented to Envoy. + repeated McpBackend servers = 1; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 1be54a0698bb4..d9b4072898fda 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -152,6 +152,7 @@ proto_library( "//envoy/extensions/filters/http/local_ratelimit/v3:pkg", "//envoy/extensions/filters/http/lua/v3:pkg", "//envoy/extensions/filters/http/mcp/v3:pkg", + "//envoy/extensions/filters/http/mcp_router/v3:pkg", "//envoy/extensions/filters/http/oauth2/v3:pkg", "//envoy/extensions/filters/http/on_demand/v3:pkg", "//envoy/extensions/filters/http/original_src/v3:pkg", diff --git a/docs/root/configuration/http/http_filters/http_filters.rst b/docs/root/configuration/http/http_filters/http_filters.rst index e9bbdf9de6bb0..c1484a8f8e552 100644 --- a/docs/root/configuration/http/http_filters/http_filters.rst +++ b/docs/root/configuration/http/http_filters/http_filters.rst @@ -53,6 +53,7 @@ HTTP filters local_rate_limit_filter lua_filter mcp_filter + mcp_router_filter oauth2_filter on_demand_updates_filter original_src_filter diff --git a/docs/root/configuration/http/http_filters/mcp_router_filter.rst b/docs/root/configuration/http/http_filters/mcp_router_filter.rst new file mode 100644 index 0000000000000..cda9d832148ce --- /dev/null +++ b/docs/root/configuration/http/http_filters/mcp_router_filter.rst @@ -0,0 +1,18 @@ +.. _config_http_filters_mcp_router: + +MCP Router +========== + +The MCP router filter provides aggregatrion of multiple Model Context Protocol (MCP) servers. + +Configuration +------------- + +Example configuration: + +.. code-block:: yaml + + http_filters: + - name: envoy.filters.http.mcp_router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.mcp.v3.McpRouter diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index c3337ef7932d9..8d7415b0912f4 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -189,6 +189,7 @@ EXTENSIONS = { "envoy.filters.http.json_to_metadata": "//source/extensions/filters/http/json_to_metadata:config", "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", "envoy.filters.http.mcp": "//source/extensions/filters/http/mcp:config", + "envoy.filters.http.mcp_router": "//source/extensions/filters/http/mcp_router:config", "envoy.filters.http.rate_limit_quota": "//source/extensions/filters/http/rate_limit_quota:config", # Disabled by default. kill_request is not built into most prebuilt images. # For instructions for building with disabled-by-default filters enabled, see diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 8d5b04d781e0f..44dca55b803f3 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -572,6 +572,13 @@ envoy.filters.http.mcp: type_urls: - envoy.extensions.filters.http.mcp.v3.Mcp - envoy.extensions.filters.http.mcp.v3.McpOverride +envoy.filters.http.mcp_router: + categories: + - envoy.filters.http + security_posture: unknown + status: alpha + type_urls: + - envoy.extensions.filters.http.mcp_router.v3.McpRouter envoy.filters.http.oauth2: categories: - envoy.filters.http diff --git a/source/extensions/filters/http/mcp_router/BUILD b/source/extensions/filters/http/mcp_router/BUILD new file mode 100644 index 0000000000000..d026080631cf3 --- /dev/null +++ b/source/extensions/filters/http/mcp_router/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + "//envoy/registry", + "//source/extensions/filters/http/common:factory_base_lib", + "@envoy_api//envoy/extensions/filters/http/mcp_router/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/filters/http/mcp_router/config.cc b/source/extensions/filters/http/mcp_router/config.cc new file mode 100644 index 0000000000000..517d63fed5090 --- /dev/null +++ b/source/extensions/filters/http/mcp_router/config.cc @@ -0,0 +1,26 @@ +#include "source/extensions/filters/http/mcp_router/config.h" + +#include "envoy/extensions/filters/http/mcp_router/v3/mcp_router.pb.h" +#include "envoy/registry/registry.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace McpRouter { + +Http::FilterFactoryCb McpRouterFilterConfigFactory::createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::http::mcp_router::v3::McpRouter&, const std::string&, + Server::Configuration::FactoryContext&) { + + return [](Http::FilterChainFactoryCallbacks&) -> void {}; +} + +/** + * Static registration for the MCP router filter. @see RegisterFactory. + */ +REGISTER_FACTORY(McpRouterFilterConfigFactory, Server::Configuration::NamedHttpFilterConfigFactory); + +} // namespace McpRouter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/mcp_router/config.h b/source/extensions/filters/http/mcp_router/config.h new file mode 100644 index 0000000000000..93cf7b29f1277 --- /dev/null +++ b/source/extensions/filters/http/mcp_router/config.h @@ -0,0 +1,30 @@ +#pragma once + +#include "envoy/extensions/filters/http/mcp_router/v3/mcp_router.pb.h" +#include "envoy/extensions/filters/http/mcp_router/v3/mcp_router.pb.validate.h" + +#include "source/extensions/filters/http/common/factory_base.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace McpRouter { + +/** + * Config factory for MCP router filter. + */ +class McpRouterFilterConfigFactory + : public Common::FactoryBase { +public: + McpRouterFilterConfigFactory() : FactoryBase("envoy.filters.http.mcp_router") {} + +private: + Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::http::mcp_router::v3::McpRouter& proto_config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; +}; + +} // namespace McpRouter +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/coverage.yaml b/test/coverage.yaml index d0c3b20023635..2513f308f364d 100644 --- a/test/coverage.yaml +++ b/test/coverage.yaml @@ -46,6 +46,7 @@ directories: source/extensions/filters/http/grpc_json_transcoder: 94.0 # TODO(#28232) source/extensions/filters/http/ip_tagging: 95.9 source/extensions/filters/http/kill_request: 91.7 # Death tests don't report LCOV + source/extensions/filters/http/mcp_router: 25 # TODO(yanavlasov): adjust up. Overriden to just scheck-in boilerplate. source/extensions/filters/http/oauth2: 97.6 source/extensions/filters/listener: 96.5 source/extensions/filters/listener/original_src: 92.1 diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 0207674becec3..83412905d97ba 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -746,6 +746,7 @@ deflater deletable deleter delim +demultiplexer denylist deque dep