From ba8e5668f7378f08d5d5b7ecc5b8d80a6444f881 Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Wed, 26 Nov 2025 16:26:01 +0000 Subject: [PATCH 1/6] Add configuration for MCP router filter Signed-off-by: Yan Avlasov --- api/BUILD | 1 + .../filters/http/mcp_router/v3/BUILD | 12 ++++++ .../http/mcp_router/v3/mcp_router.proto | 38 +++++++++++++++++++ api/versioning/BUILD | 1 + tools/spelling/spelling_dictionary.txt | 1 + 5 files changed, 53 insertions(+) create mode 100644 api/envoy/extensions/filters/http/mcp_router/v3/BUILD create mode 100644 api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto 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..340c6ac3a98a5 --- /dev/null +++ b/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.mcp_router.v3; + +import "envoy/config/core/v3/http_uri.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.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 { + // 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 config.core.v3.HttpUri mcp_server_uri = 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/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 From ff990a78caafa391b7a82de052e16f6ba69ce711 Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Thu, 27 Nov 2025 01:20:45 +0000 Subject: [PATCH 2/6] Address comments Signed-off-by: Yan Avlasov --- .../filters/http/mcp_router/v3/mcp_router.proto | 13 +++++++++++-- source/extensions/extensions_metadata.yaml | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) 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 index 340c6ac3a98a5..8cef47b7a3273 100644 --- a/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto +++ b/api/envoy/extensions/filters/http/mcp_router/v3/mcp_router.proto @@ -5,7 +5,6 @@ package envoy.extensions.filters.http.mcp_router.v3; import "envoy/config/core/v3/http_uri.proto"; import "udpa/annotations/status.proto"; -import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.mcp_router.v3"; option java_outer_classname = "McpRouterProto"; @@ -31,8 +30,18 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // * :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 config.core.v3.HttpUri mcp_server_uri = 1; + repeated McpBackend servers = 1; } 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 From ea3aa5ebc4f1fa20d1af2d1cc976d223ae386131 Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Thu, 27 Nov 2025 01:58:14 +0000 Subject: [PATCH 3/6] Fix build Signed-off-by: Yan Avlasov --- source/extensions/extensions_metadata.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 44dca55b803f3..8d5b04d781e0f 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -572,13 +572,6 @@ 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 From 130c96a7e34b4285047173df9014973f47c2b31b Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Thu, 27 Nov 2025 03:39:41 +0000 Subject: [PATCH 4/6] Add boilerplate to satisfy doc Signed-off-by: Yan Avlasov --- .../http/http_filters/http_filters.rst | 1 + .../http/http_filters/mcp_router_filter.rst | 18 +++++++++++ source/extensions/extensions_build_config.bzl | 1 + source/extensions/extensions_metadata.yaml | 7 +++++ .../extensions/filters/http/mcp_router/BUILD | 20 +++++++++++++ .../filters/http/mcp_router/config.cc | 26 ++++++++++++++++ .../filters/http/mcp_router/config.h | 30 +++++++++++++++++++ 7 files changed, 103 insertions(+) create mode 100644 docs/root/configuration/http/http_filters/mcp_router_filter.rst create mode 100644 source/extensions/filters/http/mcp_router/BUILD create mode 100644 source/extensions/filters/http/mcp_router/config.cc create mode 100644 source/extensions/filters/http/mcp_router/config.h 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..3aa36148ef292 --- /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 \ No newline at end of file From 317ff074ecd529f2be8bf8551e0c568f1385a2c7 Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Thu, 27 Nov 2025 03:58:12 +0000 Subject: [PATCH 5/6] format Signed-off-by: Yan Avlasov --- source/extensions/filters/http/mcp_router/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/mcp_router/config.h b/source/extensions/filters/http/mcp_router/config.h index 3aa36148ef292..93cf7b29f1277 100644 --- a/source/extensions/filters/http/mcp_router/config.h +++ b/source/extensions/filters/http/mcp_router/config.h @@ -27,4 +27,4 @@ class McpRouterFilterConfigFactory } // namespace McpRouter } // namespace HttpFilters } // namespace Extensions -} // namespace Envoy \ No newline at end of file +} // namespace Envoy From 1bb0f39a9bf00d56c3995938c16454b986870bfe Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Thu, 27 Nov 2025 11:22:12 +0000 Subject: [PATCH 6/6] Bypass coverage to get boilerplate committed Signed-off-by: Yan Avlasov --- CODEOWNERS | 2 ++ test/coverage.yaml | 1 + 2 files changed, 3 insertions(+) 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/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