This is an automated email from the ASF dual-hosted git repository.

xcsnx pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shenyu-website.git


The following commit(s) were added to refs/heads/main by this push:
     new 66c49cc0236 add:mcpServer plugin analysis blog (#1086)
66c49cc0236 is described below

commit 66c49cc023660aa68dd6c3379823a37dfdb55f0c
Author: Yu Siheng <[email protected]>
AuthorDate: Sat Oct 18 14:49:35 2025 +0800

    add:mcpServer plugin analysis blog (#1086)
    
    * add:mcpServer plugin analysis blog
    
    * fix
    
    * fix
---
 ...Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md | 497 ++++++++++++++++++++
 i18n/zh/code.json                                  |   3 +
 ...Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md | 522 +++++++++++++++++++++
 src/data/blogInfo.js                               |  10 +
 .../Mcp-server-execute-en.png                      | Bin 0 -> 16794 bytes
 .../Mcp-server-execute-zh.png                      | Bin 0 -> 24826 bytes
 .../Mcp-server-register-en.png                     | Bin 0 -> 27125 bytes
 .../Mcp-server-register-zh.png                     | Bin 0 -> 28758 bytes
 .../Mcp-server-tool-call-en.png                    | Bin 0 -> 4947 bytes
 .../Mcp-server-tool-call-zh.png                    | Bin 0 -> 6829 bytes
 static/img/blog/yusiheng.png                       | Bin 0 -> 72550 bytes
 11 files changed, 1032 insertions(+)

diff --git a/blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md 
b/blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md
new file mode 100644
index 00000000000..46054118732
--- /dev/null
+++ b/blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md
@@ -0,0 +1,497 @@
+---
+title: McpServer Plugin Source Code Analysis  
+author: yusiheng
+author_title: Apache ShenYu Contributor
+author_url: https://github.com/478320
+tags: [plugin,mcp,Apache ShenYu]
+---
+
+In the Shenyu gateway, when you start this plugin, Shenyu becomes a 
fully-featured McpServer.  
+You can easily register a service as a tool within the Shenyu gateway by 
simple configuration and use the extended functions the gateway offers.
+
+> This article is based on version `shenyu-2.7.0.2`. Here, I will track the 
Shenyu Mcp plugin chain and analyze the source code of its SSE communication.
+
+### Introduction
+
+> The Shenyu gateway's Mcp plugin is built on top of the spring-ai-mcp 
extension. To better understand how the Mcp plugin works, I’ll briefly 
introduce how some official Mcp Java classes collaborate within its JDK.
+
+I want to start by introducing three key official Mcp Java classes:
+
+>1. **McpServer**  
+    > This class manages resources like tools, Resource, promote, etc.
+>2. **TransportProvider**  
+    > Provides corresponding communication methods based on client-server 
communication protocols.
+>3. **Session**  
+    > Handles request data, response data, and notifications, offers some 
basic methods and corresponding handlers, and executes tool queries and calls 
here.
+
+### 1. Service Registration
+
+In Shenyu Admin, after filling in endpoint and tool information for the 
McpServer plugin, this info is automatically registered into Shenyu bootstrap.  
+You can refer to the official [websocket data sync source 
code](https://shenyu.incubator.apache.org/blog/DataSync-SourceCode-Analysis-WebSocket-Data-Sync)
 for details.
+
+Shenyu bootstrap receives the data synced from admin in the `handler()` method 
of `McpServerPluginDataHandler`.
+
+- `handlerSelector()` receives URL data and creates McpServer.
+- `handlerRule()` receives tool info and registers tools.
+
+These two methods together form the service registration part of the Shenyu 
Mcp plugin. Below, I will analyze these two methods in detail.
+
+#### 1.1 Transport and McpServer Registration
+
+Let’s analyze the `handlerSelector()` method, which handles McpServer 
registration.
+
+* What `handlerSelector()` does:
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    @Override
+    public void handlerSelector(final SelectorData selectorData) {
+        // Get URI
+        String uri = selectorData.getConditionList().stream()
+                .filter(condition -> 
Constants.URI.equals(condition.getParamType()))
+                .map(ConditionData::getParamValue)
+                .findFirst()
+                .orElse(null);
+        
+        // Build McpServer
+        ShenyuMcpServer shenyuMcpServer = 
GsonUtils.getInstance().fromJson(Objects.isNull(selectorData.getHandle()) ? 
DEFAULT_MESSAGE_ENDPOINT : selectorData.getHandle(), ShenyuMcpServer.class);
+        shenyuMcpServer.setPath(path);
+        // Cache shenyuMcpServer
+        CACHED_SERVER.get().cachedHandle(
+                selectorData.getId(),
+                shenyuMcpServer);
+        String messageEndpoint = shenyuMcpServer.getMessageEndpoint();
+        // Try to get or register transportProvider
+        shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, 
messageEndpoint);
+    }
+    
+}
+```
+
+> `ShenyuMcpServerManager` is the management center of McpServer in Shenyu. It 
not only stores `McpAsyncServer`, `CompositeTransportProvider`, etc., but also 
contains methods to register Transport and McpServer.
+
+* The `getOrCreateMcpServerTransport()` method works as follows:
+
+```java
+@Component
+public class ShenyuMcpServerManager {
+    public ShenyuSseServerTransportProvider 
getOrCreateMcpServerTransport(final String uri, final String messageEndPoint) {
+        // Remove /streamablehttp and /message suffixes
+        String normalizedPath = processPath(uri);
+        return getOrCreateTransport(normalizedPath, SSE_PROTOCOL,
+                () -> createSseTransport(normalizedPath, messageEndPoint));
+    }
+    
+    private <T> T getOrCreateTransport(final String normalizedPath, final 
String protocol,
+                                       final java.util.function.Supplier<T> 
transportFactory) {
+        // Get composite Transport instance
+        CompositeTransportProvider compositeTransport = 
getOrCreateCompositeTransport(normalizedPath);
+
+        T transport = switch (protocol) {
+            case SSE_PROTOCOL -> (T) compositeTransport.getSseTransport();
+            case STREAMABLE_HTTP_PROTOCOL -> (T) 
compositeTransport.getStreamableHttpTransport();
+            default -> null;
+        };
+        // If instance is missing in cache, create a new one
+        if (Objects.isNull(transport)) {
+            // Call createSseTransport() to create and store a new transport
+            transport = transportFactory.get();
+            // Create McpAsyncServer and register the transport
+            addTransportToSharedServer(normalizedPath, protocol, transport);
+        }
+
+        return transport;
+    }
+}
+```
+
+##### 1.1.1 Transport Registration
+
+* `createSseTransport()` method
+> This method is called within `getOrCreateMcpServerTransport()` and is used 
to create a Transport
+
+```java
+@Component
+public class ShenyuMcpServerManager {
+    private ShenyuSseServerTransportProvider createSseTransport(final String 
normalizedPath, final String messageEndPoint) {
+        String messageEndpoint = normalizedPath + messageEndPoint;
+        ShenyuSseServerTransportProvider transportProvider = 
ShenyuSseServerTransportProvider.builder()
+                .objectMapper(objectMapper)
+                .sseEndpoint(normalizedPath)
+                .messageEndpoint(messageEndpoint)
+                .build();
+        // Register the two functions of transportProvider to the Manager's 
routeMap
+        registerRoutes(normalizedPath, messageEndpoint, 
transportProvider::handleSseConnection, transportProvider::handleMessage);
+        return transportProvider;
+    }
+}
+```
+
+##### 1.1.2 McpServer Registration
+
+* `addTransportToSharedServer()` method
+> This method is called within `getOrCreateMcpServerTransport()` and is used 
to create and save McpServer
+
+This method creates a new McpServer, stores it in `sharedServerMap`, and saves 
the TransportProvider obtained above into `compositeTransportMap`.
+
+```java
+@Component
+public class ShenyuMcpServerManager {
+    private void addTransportToSharedServer(final String normalizedPath, final 
String protocol, final Object transportProvider) {
+        // Get or create and register McpServer
+        getOrCreateSharedServer(normalizedPath);
+
+        // Save the new transport protocol into compositeTransportMap
+        compositeTransport.addTransport(protocol, transportProvider);
+        
+    }
+
+    private McpAsyncServer getOrCreateSharedServer(final String 
normalizedPath) {
+        return sharedServerMap.computeIfAbsent(normalizedPath, path -> {
+            // Get transport protocols
+            CompositeTransportProvider compositeTransport = 
getOrCreateCompositeTransport(path);
+
+            // Select server capabilities
+            var capabilities = McpSchema.ServerCapabilities.builder()
+                    .tools(true)
+                    .logging()
+                    .build();
+
+            // Create and store McpServer
+            McpAsyncServer server = McpServer
+                    .async(compositeTransport)
+                    .serverInfo("MCP Shenyu Server (Multi-Protocol)", "1.0.0")
+                    .capabilities(capabilities)
+                    .tools(Lists.newArrayList())
+                    .build();
+            
+            return server;
+        });
+    }
+}
+```
+
+#### 1.2 Tools Registration
+
+* `handlerRule()` method works as follows:
+
+1. Captures the tool configuration info users fill in for the Tool, all used 
to build the tool
+2. Deserializes to create `ShenyuMcpServerTool` and obtains tool info
+
+> Note: `ShenyuMcpServerTool` is also a Shenyu-side object for storing tool 
info, unrelated by inheritance to `McpServerTool`
+
+3. Calls `addTool()` method to create the tool using this info and registers 
the tool to the matching McpServer based on SelectorId
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    @Override
+    public void handlerRule(final RuleData ruleData) {
+        Optional.ofNullable(ruleData.getHandle()).ifPresent(s -> {
+            // Deserialize a new ShenyuMcpServerTool
+            ShenyuMcpServerTool mcpServerTool = 
GsonUtils.getInstance().fromJson(s, ShenyuMcpServerTool.class);
+            // Cache mcpServerTool
+            
CACHED_TOOL.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData), 
mcpServerTool);
+            // Build MCP schema
+            List<McpServerToolParameter> parameters = 
mcpServerTool.getParameters();
+            String inputSchema = 
JsonSchemaUtil.createParameterSchema(parameters);
+            ShenyuMcpServer server = 
CACHED_SERVER.get().obtainHandle(ruleData.getSelectorId());
+            if (Objects.nonNull(server)) {
+                // Save tool info into Manager's sharedServerMap
+                shenyuMcpServerManager.addTool(server.getPath(),
+                        StringUtils.isBlank(mcpServerTool.getName()) ? 
ruleData.getName()
+                                : mcpServerTool.getName(),
+                        mcpServerTool.getDescription(),
+                        mcpServerTool.getRequestConfig(),
+                        inputSchema);
+            }
+        });
+    }
+}
+```
+
+* `addTool()` method
+> This method is called by `handlerRule()` to add a new tool
+
+This method performs:
+
+1. Converts the previous tool info into a `shenyuToolDefinition` object
+2. Creates a `ShenyuToolCallback` object using the converted 
`shenyuToolDefinition`
+> `ShenyuToolCallback` overrides the `call()` method of `ToolCallBack` and 
registers this overridden method to `AsyncToolSpecification`, so calling the 
tool's `call()` will actually invoke this overridden method
+
+3. Converts `ShenyuToolCallback` to `AsyncToolSpecification` and registers it 
to the corresponding McpServer
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    public void addTool(final String serverPath, final String name, final 
String description,
+                        final String requestTemplate, final String 
inputSchema) {
+        String normalizedPath = normalizeServerPath(serverPath);
+        // Build Definition object
+        ToolDefinition shenyuToolDefinition = ShenyuToolDefinition.builder()
+                .name(name)
+                .description(description)
+                .requestConfig(requestTemplate)
+                .inputSchema(inputSchema)
+                .build();
+        
+        ShenyuToolCallback shenyuToolCallback = new 
ShenyuToolCallback(shenyuToolDefinition);
+
+        // Get previously registered McpServer and register the Tool
+        McpAsyncServer sharedServer = sharedServerMap.get(normalizedPath);
+        for (AsyncToolSpecification asyncToolSpecification : 
McpToolUtils.toAsyncToolSpecifications(shenyuToolCallback)) {
+            sharedServer.addTool(asyncToolSpecification).block();
+        }        
+    }
+}
+```
+
+With this, service registration analysis is complete.
+
+Service registration overview diagram  
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-en.png)
+
+### 2. Plugin Execution
+
+Clients will send two types of messages with `/sse` and `/message` suffixes. 
These messages are captured by the Shenyu McpServer plugin, which handles them 
differently. When receiving `/sse` messages, the plugin creates and saves a 
session object, then returns a session id for `/message` usage. When receiving 
`/message` messages, the plugin executes methods based on the method info 
carried by the `/message` message, such as fetching work lists, tool 
invocation, and resource lists.
+
+* `doExecute()` method works as follows:
+
+1. Matches the path and checks if the Mcp plugin registered it
+2. Calls `routeByProtocol()` to choose the appropriate handling plan based on 
the request protocol
+
+> This article focuses on the SSE request mode, so we enter the 
`handleSseRequest()` method
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    @Override
+    protected Mono<Void> doExecute(final ServerWebExchange exchange,
+                                   final ShenyuPluginChain chain,
+                                   final SelectorData selector,
+                                   final RuleData rule) {
+        final String uri = exchange.getRequest().getURI().getRawPath();
+        // Check if Mcp plugin registered this route; if not, continue chain 
without handling
+        if (!shenyuMcpServerManager.canRoute(uri)) {
+            return chain.execute(exchange);
+        }
+        final ServerRequest request = ServerRequest.create(exchange, 
messageReaders);
+        // Choose handling method based on URI protocol
+        return routeByProtocol(exchange, chain, request, selector, uri);
+    }
+
+    private Mono<Void> routeByProtocol(final ServerWebExchange exchange,
+                                       final ShenyuPluginChain chain,
+                                       final ServerRequest request,
+                                       final SelectorData selector,
+                                       final String uri) {
+
+        if (isStreamableHttpProtocol(uri)) {
+            return handleStreamableHttpRequest(exchange, chain, request, uri);
+        } else if (isSseProtocol(uri)) {
+            // Handle SSE requests
+            return handleSseRequest(exchange, chain, request, selector, uri);
+        } 
+    }
+}
+```
+
+* `handleSseRequest()` method
+> Called by `routeByProtocol()` to determine if the client wants to create a 
session or call a tool based on URI suffix
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleSseRequest(final ServerWebExchange exchange,
+                                        final ShenyuPluginChain chain,
+                                        final ServerRequest request,
+                                        final SelectorData selector,
+                                        final String uri) {
+        ShenyuMcpServer server = 
McpServerPluginDataHandler.CACHED_SERVER.get().obtainHandle(selector.getId());
+        String messageEndpoint = server.getMessageEndpoint();
+        // Get the transport provider
+        ShenyuSseServerTransportProvider transportProvider
+                = shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, 
messageEndpoint);
+        // Determine if the request is an SSE connection or a message call
+        if (uri.endsWith(messageEndpoint)) {
+            setupSessionContext(exchange, chain);
+            return handleMessageEndpoint(exchange, transportProvider, request);
+        } else {
+            return handleSseEndpoint(exchange, transportProvider, request);
+        }
+    }
+}
+```
+
+#### 2.1 Client Sends SSE Request
+
+> If the client sends a request ending with `/sse`, the `handleSseEndpoint()` 
method is executed
+
+* `handleSseEndpoint()` mainly does:
+
+1. Sets SSE request headers
+2. Calls `ShenyuSseServerTransportProvider.createSseFlux()` to create the SSE 
stream
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleSseEndpoint(final ServerWebExchange exchange,
+                                         final 
ShenyuSseServerTransportProvider transportProvider,
+                                         final ServerRequest request) {
+        // Configure SSE request headers
+        configureSseHeaders(exchange);
+
+        // Create SSE stream
+        return exchange.getResponse()
+                .writeWith(transportProvider
+                        .createSseFlux(request));
+    }
+}
+```
+
+* `createSseFlux()` method
+> Called by `handleSseEndpoint()`; mainly used to create and save a session
+> 1. Creates session; the session factory registers a series of handlers, 
which are the objects actually executing tool calls
+> 2. Saves the session for reuse
+> 3. Sends the session id as a parameter of the endpoint URL back to the 
client, to be used when calling the message endpoint
+
+```java
+public class ShenyuSseServerTransportProvider implements 
McpServerTransportProvider {
+    public Flux<ServerSentEvent<?>> createSseFlux(final ServerRequest request) 
{
+        return Flux.<ServerSentEvent<?>>create(sink -> {
+                    WebFluxMcpSessionTransport sessionTransport = new 
WebFluxMcpSessionTransport(sink);
+                    // Create McpServerSession and temporarily store plugin 
chain info
+                    McpServerSession session = 
sessionFactory.create(sessionTransport);
+                    String sessionId = session.getId();
+                    sessions.put(sessionId, session);
+
+                    // Send session id info back to client
+                    String endpointUrl = this.baseUrl + this.messageEndpoint + 
"?sessionId=" + sessionId;
+                    ServerSentEvent<String> endpointEvent = 
ServerSentEvent.<String>builder()
+                            .event(ENDPOINT_EVENT_TYPE)
+                            .data(endpointUrl)
+                            .build();
+                }).doOnSubscribe(subscription -> LOGGER.info("SSE Flux 
subscribed"))
+                .doOnRequest(n -> LOGGER.debug("SSE Flux requested {} items", 
n));
+    }
+}
+```
+
+#### 2.2 Client Sends Message Request
+
+> If the client sends a request ending with `/message`, the current 
`ShenyuPluginChain` info is saved into the session, and 
`handleMessageEndpoint()` is called.  
+> Subsequent tool calls continue executing this plugin chain, so plugins after 
the Mcp plugin will affect tool requests.
+
+* `handleMessageEndpoint()` method, calls 
`ShenyuSseServerTransportProvider.handleMessageEndpoint()` to process
+
+```
+if (uri.endsWith(messageEndpoint)) {
+       setupSessionContext(exchange, chain);
+       return handleMessageEndpoint(exchange, transportProvider, request);
+} 
+```
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleMessageEndpoint(final ServerWebExchange exchange,
+                                             final 
ShenyuSseServerTransportProvider transportProvider,
+                                             final ServerRequest request) {
+        // Handle message requests
+        return transportProvider.handleMessageEndpoint(request)
+                .flatMap(result -> {
+                    return exchange.getResponse()
+                            
.writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseBody.getBytes())));
+                });
+    }
+}
+```
+
+* `handleMessageEndpoint()` method
+> Called by `McpServerPlugin.handleMessageEndpoint()`, hands over the request 
to the session for processing
+
+The session's `handler()` method performs different actions depending on the 
message.  
+For example, when the method in the message is `"tools/call"`, the tool 
invocation handler executes the `call()` method to call the tool.  
+The related source is omitted here.
+
+```java
+public class ShenyuSseServerTransportProvider implements 
McpServerTransportProvider {
+    public Mono<MessageHandlingResult> handleMessageEndpoint(final 
ServerRequest request) {
+        // Get session
+        String sessionId = request.queryParam("sessionId").get();
+        McpServerSession session = sessions.get(sessionId);
+        return request.bodyToMono(String.class)
+                .flatMap(body -> {
+                    McpSchema.JSONRPCMessage message = 
McpSchema.deserializeJsonRpcMessage(objectMapper, body);
+                    return session.handle(message);
+                });
+    }
+}
+```
+
+At this point, the Shenyu Mcp Plugin service invocation source code analysis 
is complete.
+
+Process flow overview  
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-en.png)
+
+### 3. Tool Invocation
+
+> If the client sends a message to invoke a tool, the session will use the 
tool invocation handler to execute the tool’s `call()` method.  
+> From service registration, we know the tool call actually runs the `call()` 
method of `ShenyuToolCallback`.
+
+Therefore, the tool invocation executes the following:
+
+* `call()` method mainly does:
+
+1. Gets session id
+2. Gets `requestTemplate`, the extra configuration provided by Shenyu
+3. Gets the previously stored Shenyu plugin chain and passes the tool call 
info to the chain for continued execution
+4. Asynchronously waits for the tool response
+
+After the plugin chain completes, the tool call request is actually sent to 
the service hosting the tool.
+
+```java
+public class ShenyuToolCallback implements ToolCallback {
+    @NonNull
+    @Override
+    public String call(@NonNull final String input, final ToolContext 
toolContext) {
+        // Extract sessionId from MCP request
+        final McpSyncServerExchange mcpExchange = 
extractMcpExchange(toolContext);
+        final String sessionId = extractSessionId(mcpExchange);
+        // Extract requestTemplate info
+        final String configStr = extractRequestConfig(shenyuTool);
+
+        // Get the previously stored plugin chain by sessionId
+        final ServerWebExchange originExchange = getOriginExchange(sessionId);
+        final ShenyuPluginChain chain = getPluginChain(originExchange);
+        
+        // Execute the tool call
+        return executeToolCall(originExchange, chain, sessionId, configStr, 
input);
+    }
+
+    private String executeToolCall(final ServerWebExchange originExchange,
+                                   final ShenyuPluginChain chain,
+                                   final String sessionId,
+                                   final String configStr,
+                                   final String input) {
+
+        final CompletableFuture<String> responseFuture = new 
CompletableFuture<>();
+        final ServerWebExchange decoratedExchange = buildDecoratedExchange(
+                originExchange, responseFuture, sessionId, configStr, input);
+        // Execute plugin chain, call the actual tool
+        chain.execute(decoratedExchange)
+                .subscribe();
+
+        // Wait for response
+        final String result = responseFuture.get(DEFAULT_TIMEOUT_SECONDS, 
TimeUnit.SECONDS);
+        return result;
+    }
+}
+```
+
+This concludes the Shenyu MCP Plugin tool invocation analysis.
+
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-en.png)
+
+---
+
+### 4. Summary
+
+This article analyzed the source code from Mcp service registration, through 
Mcp plugin service invocation, to tool invocation.  
+The McpServer plugin makes Shenyu a powerful and centralized McpServer.
+
+---
diff --git a/i18n/zh/code.json b/i18n/zh/code.json
index 330146bed2c..bd988da5096 100755
--- a/i18n/zh/code.json
+++ b/i18n/zh/code.json
@@ -406,6 +406,9 @@
   "Code Analysis For Divide Plugin": {
     "message": "Divide插件源码分析"
   },
+  "Code Analysis For McpServer Plugin": {
+    "message": "McpServer插件源码分析"
+  },
   "E2e Test": {
     "message": "E2e测试"
   },
diff --git 
a/i18n/zh/docusaurus-plugin-content-blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md
 
b/i18n/zh/docusaurus-plugin-content-blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md
new file mode 100644
index 00000000000..55f268999ff
--- /dev/null
+++ 
b/i18n/zh/docusaurus-plugin-content-blog/Plugin-SourceCode-Analysis-Mcp-Server-Plugin.md
@@ -0,0 +1,522 @@
+---
+title: McpServer 插件源码分析
+author: yusiheng
+author_title: Apache ShenYu Contributor
+author_url: https://github.com/478320
+tags: [plugin,mcp,Apache ShenYu]
+---
+
+在 shenyu 网关中,启动该插件,shenyu 将成为一个功能丰富的 mcpServer,
+你可以通过简单配置来将一个服务作为 tool 注册到 shenyu 网关中,并使用网关提供的扩展功能。
+
+> 本文基于`shenyu-2.7.0.2`版本进行源码分析, 在本篇中我将追踪 Shenyu Mcp 插件链路,对 Mcp 插件的 sse 
通信方式进行源码分析
+
+### 前言
+
+> shenyu 网关的 mcp 插件基于 spring-ai-mcp 扩展而来,为了更好的了解 mcp 插件的工作原理
+> ,我将简单介绍 mcp 官方提供的 jdk 中各个 java 类是如何协同运作的
+
+我想先简单介绍三个 Mcp 官方提供的 java 类
+
+>1. `McpServer`
+>
+>该类负责管理,tool,Resource,promote 等资源
+>
+>2. `TransportProvider`
+>
+>根据客户端和服务端之间通信协议,提供之相对应的通讯方法
+>
+>3. `Session`
+>
+>处理请求数据、响应数据和通知数据,提供一些基本方法和其对应的处理器,查询工具,调用工具都在此处执行
+
+### 1. 服务注册
+
+在 shenyu admin 的 McpServer 中插件填写 endpoint 和 tool 信息后,这些信息将自动注册到 shenyu 
bootstrap 中,
+数据同步源码可以参考官网[websocket数据同步](https://shenyu.incubator.apache.org/zh/blog/DataSync-SourceCode-Analysis-WebSocket-Data-Sync)
+
+shenyu bootstrap 将在 `McpServerPluginDataHandler` 的 `handler()` 方法中接收到 admin 
同步来的数据。
+
+`handlerSelector()` 方法接收 url 数据创建 McpServer
+
+`handlerRule()` 方法接收 tool 信息,注册 tool
+
+这两个方法共同组成了 Shenyu Mcp 插件的服务注册部分,下面我将对这个两个方法,详细展开分析
+
+#### 1.1 Transport,McpServer注册
+
+我们先来分析 `handlerSelector()` 方法,也就是 McpServer 的注册
+
+* `handlerSelector()` 方法 工作内容如下
+
+1. 捕捉用户在 Selector 上的填写的 url,这个 url 将作为一个 key 存储 McpServer TransportProvider 等信息
+2. 序列化创建 `ShenyuMcpServer`,`ShenyuMcpServer` 将 SelectorId 和这些 url 也就是这些 key 
绑定,以此来实现 selectorId 和 key 的绑定。
+
+> 注意 `ShenyuMcpServer` 是 Shenyu 用于绑定 SelectorId 和 url 的对象,和 `McpServer` 
没有继承关系,功能也完全不同
+
+3. 调用 `ShenyuMcpServerManager` 的 `getOrCreateMcpServerTransport()` 方法注册 
McpServer TransportProvider
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    @Override
+    public void handlerSelector(final SelectorData selectorData) {
+        // 获取URI
+        String uri = selectorData.getConditionList().stream()
+                .filter(condition -> 
Constants.URI.equals(condition.getParamType()))
+                .map(ConditionData::getParamValue)
+                .findFirst()
+                .orElse(null);
+        
+        // 构建McpServer
+        ShenyuMcpServer shenyuMcpServer = 
GsonUtils.getInstance().fromJson(Objects.isNull(selectorData.getHandle()) ? 
DEFAULT_MESSAGE_ENDPOINT : selectorData.getHandle(), ShenyuMcpServer.class);
+        shenyuMcpServer.setPath(path);
+        // 缓存shenyuMcpServer
+        CACHED_SERVER.get().cachedHandle(
+                selectorData.getId(),
+                shenyuMcpServer);
+        String messageEndpoint = shenyuMcpServer.getMessageEndpoint();
+        // 尝试获取或者注册transportProvider
+        shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, 
messageEndpoint);
+    }
+    
+}
+```
+
+> `ShenyuMcpServerManager` 该类是 ShenYu 中 McpServer 的管理中心,不仅储存了 `McpAsyncServer` 
`CompositeTransportProvider` 等内容,注册 Transport 和 McpServer
+的方法也在其中
+* `getOrCreateMcpServerTransport()` 方法工作内容具体如下
+
+1. 处理传递来的 url 去除/streamablehttp 以及 /message后缀 使其恢复为原始的 url
+2. 尝试获取 `CompositeTransportProvider` 对象,该对象是 Transport 的复合对象,包含了多种协议对应的 
Transport
+3. 如果没有获取到,则调用 `createSseTransport()` 方法创建 `CompositeTransportProvider` 对象
+4. 创建 `McpAsyncServer` 对象,保存 Transport 对象到 Map 中,将 Transport 注册到 
`McpAsyncServer` 中
+
+```java
+@Component
+public class ShenyuMcpServerManager {
+    public ShenyuSseServerTransportProvider 
getOrCreateMcpServerTransport(final String uri, final String messageEndPoint) {
+        // 去除/streamablehttp 以及 /message后缀
+        String normalizedPath = processPath(uri);
+        return getOrCreateTransport(normalizedPath, SSE_PROTOCOL,
+                () -> createSseTransport(normalizedPath, messageEndPoint));
+    }
+    
+    private <T> T getOrCreateTransport(final String normalizedPath, final 
String protocol,
+                                       final java.util.function.Supplier<T> 
transportFactory) {
+        // 获取复合Transport实例
+        CompositeTransportProvider compositeTransport = 
getOrCreateCompositeTransport(normalizedPath);
+
+        T transport = switch (protocol) {
+            case SSE_PROTOCOL -> (T) compositeTransport.getSseTransport();
+            case STREAMABLE_HTTP_PROTOCOL -> (T) 
compositeTransport.getStreamableHttpTransport();
+            default -> null;
+        };
+        // 如果缓存中没有该实例,则需要重新创建
+        if (Objects.isNull(transport)) {
+            // 调用createSseTransport()方法,创建一个新的transport并存储
+            transport = transportFactory.get();
+            // 创建McpAsyncServer,并注册transport
+            addTransportToSharedServer(normalizedPath, protocol, transport);
+        }
+
+        return transport;
+    }
+}
+```
+
+##### 1.1.1 Transport注册
+
+* `createSseTransport()` 方法
+> 该方法在 `getOrCreateMcpServerTransport()` 方法被调用,用于创建 Transport
+
+```java
+
+@Component
+public class ShenyuMcpServerManager {
+    private ShenyuSseServerTransportProvider createSseTransport(final String 
normalizedPath, final String messageEndPoint) {
+        String messageEndpoint = normalizedPath + messageEndPoint;
+        ShenyuSseServerTransportProvider transportProvider = 
ShenyuSseServerTransportProvider.builder()
+                .objectMapper(objectMapper)
+                .sseEndpoint(normalizedPath)
+                .messageEndpoint(messageEndpoint)
+                .build();
+        // 向Manager的routeMap中注册transportProvider的两个函数
+        registerRoutes(normalizedPath, messageEndpoint, 
transportProvider::handleSseConnection, transportProvider::handleMessage);
+        return transportProvider;
+    }
+}
+```
+
+##### 1.1.2 mcpServer注册
+
+* `addTransportToSharedServer()` 方法
+> 该方法在 `getOrCreateMcpServerTransport()` 方法被调用,用于创建 McpServer 并保存
+
+该方法创建了一个新的 McpServer并存储到 `sharedServerMap` 中,并将上一步得到的 TransportProvider 存入 
`compositeTransportMap` 中
+
+```java
+@Component
+public class ShenyuMcpServerManager {
+    private void addTransportToSharedServer(final String normalizedPath, final 
String protocol, final Object transportProvider) {
+        // 获取或者创建并注册 McpServer
+        getOrCreateSharedServer(normalizedPath);
+
+        // 将新增的传输协议存进compositeTransportMap中
+        compositeTransport.addTransport(protocol, transportProvider);
+        
+    }
+
+    private McpAsyncServer getOrCreateSharedServer(final String 
normalizedPath) {
+        return sharedServerMap.computeIfAbsent(normalizedPath, path -> {
+            // 获取传输协议
+            CompositeTransportProvider compositeTransport = 
getOrCreateCompositeTransport(path);
+
+            // 选择Server拥有的能力
+            var capabilities = McpSchema.ServerCapabilities.builder()
+                    .tools(true)
+                    .logging()
+                    .build();
+
+            // 创建McpServer并存储
+            McpAsyncServer server = McpServer
+                    .async(compositeTransport)
+                    .serverInfo("MCP Shenyu Server (Multi-Protocol)", "1.0.0")
+                    .capabilities(capabilities)
+                    .tools(Lists.newArrayList())
+                    .build();
+            
+            return server;
+        });
+    }
+}
+```
+
+#### 1.2 Tools注册
+
+* `handlerRule()` 方法 工作内容如下
+
+1. 捕捉用户在 Tool 上的填写的 tool 配置信息,这些信息将全部用于 tool 的构建
+2. 序列化创建 `ShenyuMcpServerTool` 获取 tool 信息
+
+> 注意 `ShenyuMcpServerTool` 也是 Shenyu 存储 tool 信息的工具,和 `McpServerTool` 没有继承关系
+
+3. 调用 `addTool()` 方法, 利用该 tool 信息创建 tool,并根据 SelectorId 将 tool 注册到与之匹配的 
McpServer 中
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    @Override
+    public void handlerRule(final RuleData ruleData) {
+        Optional.ofNullable(ruleData.getHandle()).ifPresent(s -> {
+            // 序列化一个新的 ShenyuMcpServerTool
+            ShenyuMcpServerTool mcpServerTool = 
GsonUtils.getInstance().fromJson(s, ShenyuMcpServerTool.class);
+            // 缓存mcpServerTool
+            
CACHED_TOOL.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData), 
mcpServerTool);
+            // 获取并构建 mcp schema
+            List<McpServerToolParameter> parameters = 
mcpServerTool.getParameters();
+            String inputSchema = 
JsonSchemaUtil.createParameterSchema(parameters);
+            ShenyuMcpServer server = 
CACHED_SERVER.get().obtainHandle(ruleData.getSelectorId());
+            if (Objects.nonNull(server)) {
+                // 向Manager的sharedServerMap中存储Tool信息
+                shenyuMcpServerManager.addTool(server.getPath(),
+                        StringUtils.isBlank(mcpServerTool.getName()) ? 
ruleData.getName()
+                                : mcpServerTool.getName(),
+                        mcpServerTool.getDescription(),
+                        mcpServerTool.getRequestConfig(),
+                        inputSchema);
+            }
+        });
+    }
+}
+```
+
+* `addTool()`方法
+> 该方法被 `handlerRule()` 方法调用,用于新增工具
+
+该方法做了下述工作
+1. 将上一步传来的 tool 信息转换为 `shenyuToolDefinition` 对象
+2. 利用转换来的 `shenyuToolDefinition` 对象创建 `ShenyuToolCallback` 对象
+> `ShenyuToolCallback` 重写了 `ToolCallBack` 的 `call()` 方法,并将该 `call()` 方法注册到了 
`AsyncToolSpecification` 中,
+> 此后调用 tool 的 `call()` 方法,则实际会调用这个重写的 `call()` 方法
+
+3. 将 `ShenyuToolCallback` 转换为 `AsyncToolSpecification` 并注册到相关的 mcpServer 中
+
+```java
+public class McpServerPluginDataHandler implements PluginDataHandler {
+    public void addTool(final String serverPath, final String name, final 
String description,
+                        final String requestTemplate, final String 
inputSchema) {
+        String normalizedPath = normalizeServerPath(serverPath);
+        // 构建Definition对象
+        ToolDefinition shenyuToolDefinition = ShenyuToolDefinition.builder()
+                .name(name)
+                .description(description)
+                .requestConfig(requestTemplate)
+                .inputSchema(inputSchema)
+                .build();
+        
+        ShenyuToolCallback shenyuToolCallback = new 
ShenyuToolCallback(shenyuToolDefinition);
+
+        // 获取到先前注册的 McpServer, 并向其中注册Tool
+        McpAsyncServer sharedServer = sharedServerMap.get(normalizedPath);
+        for (AsyncToolSpecification asyncToolSpecification : 
McpToolUtils.toAsyncToolSpecifications(shenyuToolCallback)) {
+            sharedServer.addTool(asyncToolSpecification).block();
+        }        
+    }
+}
+```
+
+到此为止,服务注册分析完毕
+
+服务注册一图览
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-zh.png)
+
+### 2. 插件调用
+
+客户端先后会发送后缀为 `/sse` 和 `/message` 的两种消息,这两种消息都会被 `Shenyu McpServer plugin` 
捕捉,`Shenyu McpServer plugin`
+会对 `/sse` 消息和 `/message` 消息做不同处理。收到 `/sse` 消息时 plugin 会创建 session 对象并保存,最后返回 
session id
+供 message 消息使用。收到 `/message` 消息时,会根据 `/message` 消息携带的 method 信息,选择执行的方法
+如:获取工作列表,工具调用,获取资源列表等等
+
+* `doExecute()` 方法 工作内容如下
+
+1. 路径匹配,判断 mcp plugin 是否注册该路径
+2. 调用 `routeByProtocol()` 方法,根据请求协议选择合适的处理方案
+
+> 本篇是对 sse 请求方式的解析,因此接着进入 `handleSseRequest()` 方法
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    @Override
+    protected Mono<Void> doExecute(final ServerWebExchange exchange,
+                                   final ShenyuPluginChain chain,
+                                   final SelectorData selector,
+                                   final RuleData rule) {
+        final String uri = exchange.getRequest().getURI().getRawPath();
+        // 判断 Mcp 插件是否注册了该路由规则,没有则不执行
+        if (!shenyuMcpServerManager.canRoute(uri)) {
+            return chain.execute(exchange);
+        }
+        final ServerRequest request = ServerRequest.create(exchange, 
messageReaders);
+        // 根据 uri 判断路由协议,选择对应的处理方案
+        return routeByProtocol(exchange, chain, request, selector, uri);
+    }
+
+    private Mono<Void> routeByProtocol(final ServerWebExchange exchange,
+                                       final ShenyuPluginChain chain,
+                                       final ServerRequest request,
+                                       final SelectorData selector,
+                                       final String uri) {
+
+        if (isStreamableHttpProtocol(uri)) {
+            return handleStreamableHttpRequest(exchange, chain, request, uri);
+        } else if (isSseProtocol(uri)) {
+            // 处理sse请求
+            return handleSseRequest(exchange, chain, request, selector, uri);
+        } 
+    }
+}
+```
+
+`handlerSseRequest()` 方法
+> 该方法由 `routeByProtocol()` 方法调用,根据请求后缀判断客户端是要创建 session 还是调用工具
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleSseRequest(final ServerWebExchange exchange,
+                                        final ShenyuPluginChain chain,
+                                        final ServerRequest request,
+                                        final SelectorData selector,
+                                        final String uri) {
+        ShenyuMcpServer server = 
McpServerPluginDataHandler.CACHED_SERVER.get().obtainHandle(selector.getId());
+        String messageEndpoint = server.getMessageEndpoint();
+        // 获取传输者
+        ShenyuSseServerTransportProvider transportProvider
+                = shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, 
messageEndpoint);
+        // 根据请求的后缀判断是 sse 连接请求还是 message 调用请求
+        if (uri.endsWith(messageEndpoint)) {
+            setupSessionContext(exchange, chain);
+            return handleMessageEndpoint(exchange, transportProvider, request);
+        } else {
+            return handleSseEndpoint(exchange, transportProvider, request);
+        }
+    }
+}
+```
+
+#### 2.1 客户端发送 sse 请求
+
+> 如果我们客户端发送的是后缀为 `/sse` 的请求,那么将会执行 `handleSseEndpoint()` 方法
+
+* `handleSseEndpoint()` 方法主要做了如下工作
+
+1. 配置 sse 请求头
+2. 调用 `ShenyuSseServerTransportProvider` 的 `createSseFlux()` 创建 sse 流
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleSseEndpoint(final ServerWebExchange exchange,
+                                         final 
ShenyuSseServerTransportProvider transportProvider,
+                                         final ServerRequest request) {
+        // 配置 sse 请求头
+        configureSseHeaders(exchange);
+
+        // 创建 sse 流
+        return exchange.getResponse()
+                .writeWith(transportProvider
+                        .createSseFlux(request));
+    }
+}
+```
+
+* `createSseFlux()` 方法
+> 该方法被 `handleSseEndpoint()`调用 主要用于创建并保存 session
+1. 创建 session ,创建 session 的工厂在创建 session 时会将一系列 handler 注册到 session 中,这些 
handler 是真正执行
+   callTool 的对象
+2. 将 session 保存,session复用
+3. 将 session id 作为 endpoint 的请求参数返回给客户端,在调用 message 方法时会使用该 endpoint
+
+```java
+public class ShenyuSseServerTransportProvider implements 
McpServerTransportProvider {
+    public Flux<ServerSentEvent<?>> createSseFlux(final ServerRequest request) 
{
+        return Flux.<ServerSentEvent<?>>create(sink -> {
+                    WebFluxMcpSessionTransport sessionTransport = new 
WebFluxMcpSessionTransport(sink);
+                    // 创建 McpServerSession 并暂存插件链信息
+                    McpServerSession session = 
sessionFactory.create(sessionTransport);
+                    String sessionId = session.getId();
+                    sessions.put(sessionId, session);
+
+                    // 将 session id等信息传递回客户端
+                    String endpointUrl = this.baseUrl + this.messageEndpoint + 
"?sessionId=" + sessionId;
+                    ServerSentEvent<String> endpointEvent = 
ServerSentEvent.<String>builder()
+                            .event(ENDPOINT_EVENT_TYPE)
+                            .data(endpointUrl)
+                            .build();
+                }).doOnSubscribe(subscription -> LOGGER.info("SSE Flux 
subscribed"))
+                .doOnRequest(n -> LOGGER.debug("SSE Flux requested {} items", 
n));
+    }
+}
+```
+
+#### 2.2 客户端发送 message 请求
+
+> 如果我们客户端发送的是后缀为 `/message` 的请求,那么将会执行 把当前 `ShenyuPluginChain` 信息存入 session 
中,并调用 `handleMessageEndpoint()` 方法,
+> 后续工具调用时会继续执行该插件链,因此 mcp plugin 后的插件会对进入 tool 的请求造成影响
+
+* `handleMessageEndpoint()` 方法,调用 `ShenyuSseServerTransportProvider` 的 
`handleMessageEndpoint()` 方法
+
+```
+if (uri.endsWith(messageEndpoint)) {
+       setupSessionContext(exchange, chain);
+       return handleMessageEndpoint(exchange, transportProvider, request);
+} 
+```
+
+```java
+public class McpServerPlugin extends AbstractShenyuPlugin {
+    private Mono<Void> handleMessageEndpoint(final ServerWebExchange exchange,
+                                             final 
ShenyuSseServerTransportProvider transportProvider,
+                                             final ServerRequest request) {
+        // 处理message请求
+        return transportProvider.handleMessageEndpoint(request)
+                .flatMap(result -> {
+                    return exchange.getResponse()
+                            
.writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseBody.getBytes())));
+                });
+    }
+}
+```
+
+* `handleMessageEndpoint()` 方法
+> 该方法由 `McpServerPlugin.handleMessageEndpoint()` 调用,将请求交给 session 处理
+
+session 的 `handler()` 方法会对 message 的不同,而进行对应的操作
+例如 : 当 message 中 method 是 "tools/call" 时,则会使用工具调用的 handler() 执行 `call()` 方法调用工具
+相关源码在此不过多赘述
+
+```java
+public class ShenyuSseServerTransportProvider implements 
McpServerTransportProvider {
+    public Mono<MessageHandlingResult> handleMessageEndpoint(final 
ServerRequest request) {
+        // 获取到session
+        String sessionId = request.queryParam("sessionId").get();
+        McpServerSession session = sessions.get(sessionId);
+        return request.bodyToMono(String.class)
+                .flatMap(body -> {
+                    McpSchema.JSONRPCMessage message = 
McpSchema.deserializeJsonRpcMessage(objectMapper, body);
+                    return session.handle(message);
+                });
+    }
+}
+```
+
+至此 `Shenyu Mcp Plugin` 服务调用源码分析完毕
+
+流程图一览
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-zh.png)
+
+### 3. 工具调用
+
+> 如果客户端传递的消息是调用工具的消息,那么 session 将使用工具调用的 handler() 并执行 tool 的 `call()` 方法,
+> 在服务注册中,我们说明了 tool 在被调用时,实际执行的是 `ShenyuToolCallback()` 的 `call()` 方法
+
+因此执行工具调用时会执行以下方法
+* `call()` 主要工作内容如下
+
+1. 获取 session id
+2. 获取 `requestTemplate` 即 shenyu 提供的额外功能的配置信息
+3. 获取上一步暂存的 shenyu 插件链,并将工具调用的信息交给插件链继续执行
+4. 异步等待工具响应
+
+插件链执行完成后,会将调用 tool 请求真正的发送到 tool 所在的服务之中
+
+```java
+public class ShenyuToolCallback implements ToolCallback {
+    @NonNull
+    @Override
+    public String call(@NonNull final String input, final ToolContext 
toolContext) {
+        // 从 mcp 请求中提取 sessionId
+        final McpSyncServerExchange mcpExchange = 
extractMcpExchange(toolContext);
+        final String sessionId = extractSessionId(mcpExchange);
+        // 提取requestTemplate信息
+        final String configStr = extractRequestConfig(shenyuTool);
+
+        // 利用sessionId 获取到先前暂存的插件执行链
+        final ServerWebExchange originExchange = getOriginExchange(sessionId);
+        final ShenyuPluginChain chain = getPluginChain(originExchange);
+
+        // 执行工具调用
+        return executeToolCall(originExchange, chain, sessionId, configStr, 
input);
+
+    }
+
+    private String executeToolCall(final ServerWebExchange originExchange,
+                                   final ShenyuPluginChain chain,
+                                   final String sessionId,
+                                   final String configStr,
+                                   final String input) {
+
+        final CompletableFuture<String> responseFuture = new 
CompletableFuture<>();
+        final ServerWebExchange decoratedExchange = buildDecoratedExchange(
+                originExchange, responseFuture, sessionId, configStr, input);
+        // 执行插件链,调用实际工具
+        chain.execute(decoratedExchange)
+                .subscribe();
+
+        // 等待响应
+        final String result = responseFuture.get(DEFAULT_TIMEOUT_SECONDS, 
TimeUnit.SECONDS);
+        return result;
+
+    }
+}
+```
+
+至此Shenyu MCP Plugin 工具调用分析完毕
+
+![](/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-zh.png)
+
+---
+
+### 4. 小结
+
+本文源码分析从 mcp 服务注册开始,到 mcp 插件的服务调用,再到 tool 的调用。
+mcpServer 插件让 shenyu 成为一个功能强大,集中管理的 mcpServer。
+
+---
diff --git a/src/data/blogInfo.js b/src/data/blogInfo.js
index e586cc36970..508bb828a66 100644
--- a/src/data/blogInfo.js
+++ b/src/data/blogInfo.js
@@ -231,6 +231,16 @@ export default [
                 date: '2024-02-04',
                 abs:<Translate>Code Analysis Ext Plugin Loader</Translate>
             },
+            {
+                title: <Translate>Code Analysis For McpServer 
Plugin</Translate>,
+                author: "yusiheng (contributor)",
+                autImg: "/img/blog/yusiheng.png",
+                autPage: "https://github.com/478320";,
+                src: "Plugin-SourceCode-Analysis-Mcp-Server-Plugin",
+                cover: "/img/logo.svg",
+                date: '2025-09-22',
+                abs:<Translate>Code Analysis For McpServer Plugin</Translate>
+            },
         ]
     },
     {
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-en.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-en.png
new file mode 100644
index 00000000000..d4b71962c7b
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-en.png
 differ
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-zh.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-zh.png
new file mode 100644
index 00000000000..f2b31c5b858
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-execute-zh.png
 differ
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-en.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-en.png
new file mode 100644
index 00000000000..d8c97f9248a
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-en.png
 differ
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-zh.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-zh.png
new file mode 100644
index 00000000000..15006ce4bb4
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-register-zh.png
 differ
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-en.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-en.png
new file mode 100644
index 00000000000..7a6f372bdeb
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-en.png
 differ
diff --git 
a/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-zh.png
 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-zh.png
new file mode 100644
index 00000000000..502703d02da
Binary files /dev/null and 
b/static/img/activities/code-analysis-mcp-server-plugin/Mcp-server-tool-call-zh.png
 differ
diff --git a/static/img/blog/yusiheng.png b/static/img/blog/yusiheng.png
new file mode 100644
index 00000000000..4f8057c2f3c
Binary files /dev/null and b/static/img/blog/yusiheng.png differ

Reply via email to