You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by li...@apache.org on 2025/07/21 08:40:45 UTC

(shenyu) branch master updated: [feat] mcp streamable http (#6061)

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

liuhongyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 7de2558788 [feat] mcp streamable http (#6061)
7de2558788 is described below

commit 7de25587883c26ce9e55f2c37d551a2308679ba6
Author: 有若旭 <48...@users.noreply.github.com>
AuthorDate: Mon Jul 21 16:40:40 2025 +0800

    [feat] mcp streamable http (#6061)
    
    * [feat] mcp server plugin, init to support streamable http.
    
    * [feat] mcp server plugin, let streamable http and sse use single McpAsyncServer.
    
    * [feat] mcp server plugin, remove default tool for McpAsyncServer.
    
    * [feat] mcp server plugin, session reconnection and stateless session support for Streamable http.
    
    * [feat] mcp server plugin, debug session reconnection and stateless session support for Streamable http.
    
    * [feat] mcp server plugin, Remove handle noSessionId exchange for ShenyuToolCallback .
    
    * [feat] mcp server plugin, Add tools config examples .
    
    * [feat] mcp server plugin, Modify Body writer for POST request .
    
    * [feat] mcp server plugin, Modify rpc type when need do call.
    
    * [feat] mcp server plugin, Modify parameter format & support grpc.
    
    * [feat] mcp server plugin, fix streamable http after support custom message endpoint.
    
    * [feat] mcp server plugin, Modify javadoc for more.
    
    * [feat] mcp server plugin, fix check style.
    
    * [feat] mcp server plugin, use compositeTransportMap remove sseTransportMap & streamableHttpTransportMap.
    
    * [feat] mcp server plugin, reuse ObjectMapper & remove protocolMap.
    
    * [feat] mcp server plugin, fix mistakes for Copilot check.
---
 .../shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES.md  | 492 ++++++++++
 .../MCP_TOOL_EXAMPLES_EN.md                        | 499 +++++++++++
 .../shenyu/plugin/mcp/server/McpServerPlugin.java  | 659 +++++++++++---
 .../mcp/server/callback/ShenyuToolCallback.java    | 681 +++++++++++---
 .../server/handler/McpServerPluginDataHandler.java |  48 +-
 .../mcp/server/manager/ShenyuMcpServerManager.java | 584 +++++++++---
 .../mcp/server/request/BodyWriterExchange.java     |  90 +-
 .../mcp/server/request/ParameterFormatter.java     |  60 ++
 .../mcp/server/request/RequestConfigHelper.java    | 196 ++--
 .../NonCommittingMcpResponseDecorator.java         | 276 ++++++
 .../server/transport/MessageHandlingResult.java    |  99 ++
 ...henyuStreamableHttpServerTransportProvider.java | 996 +++++++++++++++++++++
 .../transport/StreamableHttpProviderBuilder.java   |  81 ++
 .../plugin/mcp/server/utils/JsonSchemaUtil.java    |   4 +-
 14 files changed, 4272 insertions(+), 493 deletions(-)

diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES.md b/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES.md
new file mode 100644
index 0000000000..78a029ab12
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES.md
@@ -0,0 +1,492 @@
+# MCP Tool 配置示例大全
+
+本文档基于 **shenyu-examples-http** 项目中的真实接口,提供了 Shenyu MCP Server Plugin 的各种工具配置示例,涵盖不同的 HTTP 请求方法、参数类型和配置方式。
+
+## 1. 简单 GET 请求示例
+
+### 1.1 无参数 GET 请求
+```json
+{
+  "name": "hello",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/hello\",\"method\":\"GET\",\"headers\":[],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "Shenyu 客户端问候接口"
+}
+```
+
+### 1.2 带参数的 GET 请求
+```json
+{
+  "name": "hi",
+  "parameters": [
+    {
+      "name": "name",
+      "type": "string",
+      "description": "用户名",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/hi\",\"method\":\"GET\",\"headers\":[],\"timeout\":30000,\"queryParams\":[{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"name\":\"query\"}}",
+  "description": "Shenyu 客户端带参数问候接口"
+}
+```
+
+## 2. 用户相关 GET 请求示例
+
+### 2.1 根据用户ID查找用户
+```json
+{
+  "name": "findByUserId",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByUserId\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"userId\",\"value\":\"${userId}\"}]},\"argsPosition\":{\"userId\":\"query\"}}",
+  "description": "根据用户ID查找用户信息"
+}
+```
+
+### 2.2 根据用户ID和姓名查找用户
+```json
+{
+  "name": "findByUserIdName",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "用户姓名",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByUserIdName\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"userId\",\"value\":\"${userId}\"},{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"userId\":\"query\",\"name\":\"query\"}}",
+  "description": "根据用户ID和姓名查找用户"
+}
+```
+
+### 2.3 分页查找用户
+```json
+{
+  "name": "findByPage",
+  "parameters": [
+    {
+      "name": "keyword",
+      "type": "string",
+      "description": "搜索关键词",
+      "required": false
+    },
+    {
+      "name": "page",
+      "type": "integer",
+      "description": "页码",
+      "required": false
+    },
+    {
+      "name": "pageSize",
+      "type": "integer",
+      "description": "每页大小",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByPage\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"keyword\",\"value\":\"${keyword}\"},{\"key\":\"page\",\"value\":\"${page}\"},{\"key\":\"pageSize\",\"value\":\"${pageSize}\"}]},\"argsPosition\":{\"keyword\":\"query\",\"page\":\"query\",\"pageSize\":\"query\"}}",
+  "description": "分页查找用户"
+}
+```
+
+### 2.4 通过路径参数获取用户信息
+```json
+{
+  "name": "getUserByPath",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "用户姓名",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/path/{{.id}}\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"id\":\"path\",\"name\":\"query\"}}",
+  "description": "通过路径参数获取用户信息"
+}
+```
+
+## 3. POST 请求示例
+
+### 3.1 用户支付接口
+```json
+{
+  "name": "payment",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    },
+    {
+      "name": "userName",
+      "type": "string",
+      "description": "用户姓名",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/payment\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"userId\":\"body\",\"userName\":\"body\"}}",
+  "description": "用户支付接口"
+}
+```
+
+### 3.2 WAF 通过测试
+```json
+{
+  "name": "wafPass",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/waf/pass\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "WAF 通过测试"
+}
+```
+
+### 3.3 Shenyu 客户端 POST 请求
+```json
+{
+  "name": "postHi",
+  "parameters": [
+    {
+      "name": "name",
+      "type": "string",
+      "description": "用户名",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/post/hi\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/x-www-form-urlencoded\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"name\":\"body\"}}",
+  "description": "Shenyu 客户端 POST 问候接口"
+}
+```
+
+## 4. PUT 请求示例
+
+### 4.1 更新用户信息
+```json
+{
+  "name": "updateUser",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    },
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "用户ID",
+      "required": true
+    },
+    {
+      "name": "userName",
+      "type": "string",
+      "description": "用户姓名",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/putPathBody/{{.id}}\",\"method\":\"PUT\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"id\":\"path\",\"userId\":\"body\",\"userName\":\"body\"}}",
+  "description": "更新用户信息"
+}
+```
+
+## 5. 订单相关示例
+
+### 5.1 保存订单
+```json
+{
+  "name": "saveOrder",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "订单ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "订单名称",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/save\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"id\":\"body\",\"name\":\"body\"}}",
+  "description": "保存订单"
+}
+```
+
+### 5.2 根据ID查找订单
+```json
+{
+  "name": "findOrderById",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "订单ID",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/findById\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"id\",\"value\":\"${id}\"}]},\"argsPosition\":{\"id\":\"query\"}}",
+  "description": "根据ID查找订单"
+}
+```
+
+### 5.3 RESTful 风格订单查询
+```json
+{
+  "name": "getOrderByPath",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "订单ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "订单名称",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/path/{{.id}}/{{.name}}\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{\"id\":\"path\",\"name\":\"path\"}}",
+  "description": "RESTful 风格订单查询"
+}
+```
+
+## 6. 请求处理示例
+
+### 6.1 请求头测试
+```json
+{
+  "name": "testRequestHeader",
+  "parameters": [
+    {
+      "name": "headerKey1",
+      "type": "string",
+      "description": "请求头参数",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/header\",\"method\":\"GET\",\"headers\":[{\"key\":\"header_key1\",\"value\":\"{{.headerKey1}}\"}],\"timeout\":30000},\"argsPosition\":{\"headerKey1\":\"query\"}}",
+  "description": "请求头测试"
+}
+```
+
+### 6.2 请求参数测试
+```json
+{
+  "name": "testRequestParameter",
+  "parameters": [
+    {
+      "name": "parameterKey1",
+      "type": "string",
+      "description": "请求参数",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/parameter\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/x-www-form-urlencoded\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"parameterKey1\":\"body\"}}",
+  "description": "请求参数测试"
+}
+```
+
+### 6.3 Cookie 测试
+```json
+{
+  "name": "testRequestCookie",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "用户ID(Cookie值)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/cookie\",\"method\":\"GET\",\"headers\":[{\"key\":\"Cookie\",\"value\":\"userId={{.userId}}\"}],\"timeout\":30000},\"argsPosition\":{\"userId\":\"query\"}}",
+  "description": "Cookie 测试"
+}
+```
+
+## 7. 文件上传示例
+
+### 7.1 单文件上传
+```json
+{
+  "name": "uploadSingleFile",
+  "parameters": [
+    {
+      "name": "file",
+      "type": "string",
+      "description": "文件内容(Base64编码)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/upload/webFluxSingle\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"multipart/form-data\"}],\"timeout\":60000,\"argsToJsonBody\":true},\"argsPosition\":{\"file\":\"body\"}}",
+  "description": "单文件上传"
+}
+```
+
+### 7.2 多文件上传
+```json
+{
+  "name": "uploadMultipleFiles",
+  "parameters": [
+    {
+      "name": "files",
+      "type": "array",
+      "description": "多个文件内容(Base64编码数组)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/upload/webFluxFiles\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"multipart/form-data\"}],\"timeout\":60000,\"argsToJsonBody\":true},\"argsPosition\":{\"files\":\"body\"}}",
+  "description": "多文件上传"
+}
+```
+
+## 8. 高级功能示例
+
+### 8.1 OAuth2 认证测试
+```json
+{
+  "name": "testOAuth2",
+  "parameters": [
+    {
+      "name": "token",
+      "type": "string",
+      "description": "OAuth2 访问令牌",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/oauth2/test\",\"method\":\"GET\",\"headers\":[{\"key\":\"Authorization\",\"value\":\"Bearer {{.token}}\"}],\"timeout\":30000},\"argsPosition\":{\"token\":\"query\"}}",
+  "description": "OAuth2 认证测试"
+}
+```
+
+### 8.2 缓存测试
+```json
+{
+  "name": "testCache",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/cache\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "缓存测试"
+}
+```
+
+### 8.3 大对象响应测试
+```json
+{
+  "name": "testBigObject",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/bigObject\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":60000},\"argsPosition\":{}}",
+  "description": "大对象响应测试"
+}
+```
+
+## 参数位置映射说明 (argsPosition)
+
+### 支持的映射位置:
+
+1. **URL 路径参数**: `"path"` - 对应 URL 模板中的 `{{.paramName}}`
+2. **查询参数**: `"query"` - 对应 `queryParams` 数组中的参数
+3. **请求体**: `"body"` - 配合 `argsToJsonBody: true` 使用
+
+### 示例映射:
+```json
+{
+  "argsPosition": {
+    "id": "path",                        // 路径参数 /test/path/{{.id}}
+    "userId": "query",                   // 查询参数 queryParams: [{"key": "userId", "value": "${userId}"}]
+    "userName": "body"                   // JSON Body (需设置 argsToJsonBody: true)
+  }
+}
+```
+
+### 配置结构说明:
+
+#### requestTemplate 配置项:
+- `url`: URL 模板,路径参数支持 `{{.paramName}}` 占位符
+- `method`: HTTP 方法 (GET, POST, PUT, DELETE)
+- `headers`: 请求头数组,每个元素包含 `key` 和 `value`
+- `queryParams`: 查询参数数组,每个元素包含 `key` 和 `value`
+- `timeout`: 超时时间(毫秒)
+- `argsToJsonBody`: 是否将参数转换为 JSON 请求体
+
+#### 头部配置格式:
+```json
+{
+  "headers": [
+    {
+      "key": "Content-Type",
+      "value": "application/json"
+    },
+    {
+      "key": "Authorization",
+      "value": "Bearer {{.token}}"
+    }
+  ]
+}
+```
+
+#### 查询参数配置格式:
+```json
+{
+  "queryParams": [
+    {
+      "key": "userId",
+      "value": "${userId}"
+    },
+    {
+      "key": "page",
+      "value": "${page}"
+    }
+  ]
+}
+```
+
+## 使用说明
+
+1. **参数类型**: 支持 `string`, `integer`, `number`, `boolean`, `array`, `object`
+2. **必填参数**: 通过 `required` 字段控制
+3. **超时设置**: 通过 `timeout` 字段设置(毫秒)
+4. **请求头**: 使用 `key` 和 `value` 字段配置
+5. **查询参数**: 使用 `queryParams` 数组配置,值使用 `${paramName}` 格式
+6. **路径参数**: 在 URL 中使用 `{{.paramName}}` 格式
+7. **参数映射**: 通过 `argsPosition` 指定参数位置:`path`、`query`、`body`
+
+## 最佳实践
+
+1. **从简单开始**: 先配置基本的 GET/POST 示例,逐步增加复杂度
+2. **超时配置**: 
+   - 简单查询: 30秒
+   - 复杂操作: 45-120秒
+   - 文件上传: 60秒以上
+3. **错误处理**: 在业务层实现重试机制和合适的错误响应
+4. **安全性**: 
+   - 生产环境必须使用 HTTPS
+   - 访问安全接口时包含认证头
+   - 验证输入参数
+5. **性能优化**: 
+   - 设置合适的超时时间
+   - 高频调用使用连接池
+   - 监控和记录API性能
+
+这些示例基于 **shenyu-examples-http** 项目中的真实接口,可以直接在 Shenyu 环境中使用和测试。
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES_EN.md b/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES_EN.md
new file mode 100644
index 0000000000..6dfb50f5ce
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES_EN.md
@@ -0,0 +1,499 @@
+# MCP Tool Configuration Examples
+
+This document provides comprehensive examples of tool configurations for the Shenyu MCP Server Plugin based on **real interfaces from the shenyu-examples-http project**, covering different HTTP request methods, parameter types, and configuration patterns.
+
+## 1. Simple GET Request Examples
+
+### 1.1 GET Request with No Parameters
+```json
+{
+  "name": "hello",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/hello\",\"method\":\"GET\",\"headers\":[],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "Shenyu client hello endpoint"
+}
+```
+
+### 1.2 GET Request with Parameters
+```json
+{
+  "name": "hi",
+  "parameters": [
+    {
+      "name": "name",
+      "type": "string",
+      "description": "User name",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/hi\",\"method\":\"GET\",\"headers\":[],\"timeout\":30000,\"queryParams\":[{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"name\":\"query\"}}",
+  "description": "Shenyu client hi endpoint with parameter"
+}
+```
+
+## 2. User-Related GET Request Examples
+
+### 2.1 Find User by ID
+```json
+{
+  "name": "findByUserId",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByUserId\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"userId\",\"value\":\"${userId}\"}]},\"argsPosition\":{\"userId\":\"query\"}}",
+  "description": "Find user information by user ID"
+}
+```
+
+### 2.2 Find User by ID and Name
+```json
+{
+  "name": "findByUserIdName",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "User name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByUserIdName\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"userId\",\"value\":\"${userId}\"},{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"userId\":\"query\",\"name\":\"query\"}}",
+  "description": "Find user by ID and name"
+}
+```
+
+### 2.3 Find Users with Pagination
+```json
+{
+  "name": "findByPage",
+  "parameters": [
+    {
+      "name": "keyword",
+      "type": "string",
+      "description": "Search keyword",
+      "required": false
+    },
+    {
+      "name": "page",
+      "type": "integer",
+      "description": "Page number",
+      "required": false
+    },
+    {
+      "name": "pageSize",
+      "type": "integer",
+      "description": "Page size",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/findByPage\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"keyword\",\"value\":\"${keyword}\"},{\"key\":\"page\",\"value\":\"${page}\"},{\"key\":\"pageSize\",\"value\":\"${pageSize}\"}]},\"argsPosition\":{\"keyword\":\"query\",\"page\":\"query\",\"pageSize\":\"query\"}}",
+  "description": "Find users with pagination"
+}
+```
+
+### 2.4 Get User by Path Parameter
+```json
+{
+  "name": "getUserByPath",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "User name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/path/{{.id}}\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"name\",\"value\":\"${name}\"}]},\"argsPosition\":{\"id\":\"path\",\"name\":\"query\"}}",
+  "description": "Get user information by path parameter"
+}
+```
+
+## 3. POST Request Examples
+
+### 3.1 User Payment Interface
+```json
+{
+  "name": "payment",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    },
+    {
+      "name": "userName",
+      "type": "string",
+      "description": "User name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/payment\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"userId\":\"body\",\"userName\":\"body\"}}",
+  "description": "User payment interface"
+}
+```
+
+### 3.2 WAF Pass Test
+```json
+{
+  "name": "wafPass",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/waf/pass\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "WAF pass test"
+}
+```
+
+### 3.3 Shenyu Client POST Request
+```json
+{
+  "name": "postHi",
+  "parameters": [
+    {
+      "name": "name",
+      "type": "string",
+      "description": "User name",
+      "required": false
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/shenyu/client/post/hi\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/x-www-form-urlencoded\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"name\":\"body\"}}",
+  "description": "Shenyu client POST hi endpoint"
+}
+```
+
+## 4. PUT Request Examples
+
+### 4.1 Update User Information
+```json
+{
+  "name": "updateUser",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    },
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "User ID",
+      "required": true
+    },
+    {
+      "name": "userName",
+      "type": "string",
+      "description": "User name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/putPathBody/{{.id}}\",\"method\":\"PUT\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"id\":\"path\",\"userId\":\"body\",\"userName\":\"body\"}}",
+  "description": "Update user information"
+}
+```
+
+## 5. Order-Related Examples
+
+### 5.1 Save Order
+```json
+{
+  "name": "saveOrder",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "Order ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "Order name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/save\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"id\":\"body\",\"name\":\"body\"}}",
+  "description": "Save order"
+}
+```
+
+### 5.2 Find Order by ID
+```json
+{
+  "name": "findOrderById",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "Order ID",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/findById\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000,\"queryParams\":[{\"key\":\"id\",\"value\":\"${id}\"}]},\"argsPosition\":{\"id\":\"query\"}}",
+  "description": "Find order by ID"
+}
+```
+
+### 5.3 RESTful Order Query
+```json
+{
+  "name": "getOrderByPath",
+  "parameters": [
+    {
+      "name": "id",
+      "type": "string",
+      "description": "Order ID",
+      "required": true
+    },
+    {
+      "name": "name",
+      "type": "string",
+      "description": "Order name",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/path/{{.id}}/{{.name}}\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{\"id\":\"path\",\"name\":\"path\"}}",
+  "description": "RESTful style order query"
+}
+```
+
+## 6. Request Processing Examples
+
+### 6.1 Request Header Test
+```json
+{
+  "name": "testRequestHeader",
+  "parameters": [
+    {
+      "name": "headerKey1",
+      "type": "string",
+      "description": "Request header parameter",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/header\",\"method\":\"GET\",\"headers\":[{\"key\":\"header_key1\",\"value\":\"{{.headerKey1}}\"}],\"timeout\":30000},\"argsPosition\":{\"headerKey1\":\"query\"}}",
+  "description": "Request header test"
+}
+```
+
+### 6.2 Request Parameter Test
+```json
+{
+  "name": "testRequestParameter",
+  "parameters": [
+    {
+      "name": "parameterKey1",
+      "type": "string",
+      "description": "Request parameter",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/parameter\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/x-www-form-urlencoded\"}],\"timeout\":30000,\"argsToJsonBody\":true},\"argsPosition\":{\"parameterKey1\":\"body\"}}",
+  "description": "Request parameter test"
+}
+```
+
+### 6.3 Cookie Test
+```json
+{
+  "name": "testRequestCookie",
+  "parameters": [
+    {
+      "name": "userId",
+      "type": "string",
+      "description": "User ID (Cookie value)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/request/cookie\",\"method\":\"GET\",\"headers\":[{\"key\":\"Cookie\",\"value\":\"userId={{.userId}}\"}],\"timeout\":30000},\"argsPosition\":{\"userId\":\"query\"}}",
+  "description": "Cookie test"
+}
+```
+
+## 7. File Upload Examples
+
+### 7.1 Single File Upload
+```json
+{
+  "name": "uploadSingleFile",
+  "parameters": [
+    {
+      "name": "file",
+      "type": "string",
+      "description": "File content (Base64 encoded)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/upload/webFluxSingle\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"multipart/form-data\"}],\"timeout\":60000,\"argsToJsonBody\":true},\"argsPosition\":{\"file\":\"body\"}}",
+  "description": "Single file upload"
+}
+```
+
+### 7.2 Multiple Files Upload
+```json
+{
+  "name": "uploadMultipleFiles",
+  "parameters": [
+    {
+      "name": "files",
+      "type": "array",
+      "description": "Multiple file contents (Base64 encoded array)",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/upload/webFluxFiles\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"multipart/form-data\"}],\"timeout\":60000,\"argsToJsonBody\":true},\"argsPosition\":{\"files\":\"body\"}}",
+  "description": "Multiple files upload"
+}
+```
+
+## 8. Advanced Feature Examples
+
+### 8.1 OAuth2 Authentication Test
+```json
+{
+  "name": "testOAuth2",
+  "parameters": [
+    {
+      "name": "token",
+      "type": "string",
+      "description": "OAuth2 access token",
+      "required": true
+    }
+  ],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/order/oauth2/test\",\"method\":\"GET\",\"headers\":[{\"key\":\"Authorization\",\"value\":\"Bearer {{.token}}\"}],\"timeout\":30000},\"argsPosition\":{\"token\":\"query\"}}",
+  "description": "OAuth2 authentication test"
+}
+```
+
+### 8.2 Cache Test
+```json
+{
+  "name": "testCache",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/cache\",\"method\":\"GET\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":30000},\"argsPosition\":{}}",
+  "description": "Cache test"
+}
+```
+
+### 8.3 Big Object Response Test
+```json
+{
+  "name": "testBigObject",
+  "parameters": [],
+  "requestConfig": "{\"requestTemplate\":{\"url\":\"/test/bigObject\",\"method\":\"POST\",\"headers\":[{\"key\":\"Content-Type\",\"value\":\"application/json\"}],\"timeout\":60000},\"argsPosition\":{}}",
+  "description": "Big object response test"
+}
+```
+
+## Parameter Position Mapping (argsPosition)
+
+### Supported Mapping Locations:
+
+1. **URL Path Parameters**: `"path"` - Corresponds to `{{.paramName}}` in URL template
+2. **Query Parameters**: `"query"` - Corresponds to parameters in `queryParams` array
+3. **Request Body**: `"body"` - Used with `argsToJsonBody: true`
+
+### Mapping Examples:
+```json
+{
+  "argsPosition": {
+    "id": "path",                        // Path parameter /test/path/{{.id}}
+    "userId": "query",                   // Query parameter queryParams: [{"key": "userId", "value": "${userId}"}]
+    "userName": "body"                   // JSON Body (requires argsToJsonBody: true)
+  }
+}
+```
+
+### Configuration Structure:
+
+#### requestTemplate Configuration Items:
+- `url`: URL template with path parameter support using `{{.paramName}}` placeholders
+- `method`: HTTP method (GET, POST, PUT, DELETE)
+- `headers`: Headers array, each element contains `key` and `value`
+- `queryParams`: Query parameters array, each element contains `key` and `value`
+- `timeout`: Timeout in milliseconds
+- `argsToJsonBody`: Whether to convert parameters to JSON request body
+
+#### Header Configuration Format:
+```json
+{
+  "headers": [
+    {
+      "key": "Content-Type",
+      "value": "application/json"
+    },
+    {
+      "key": "Authorization",
+      "value": "Bearer {{.token}}"
+    }
+  ]
+}
+```
+
+#### Query Parameters Configuration Format:
+```json
+{
+  "queryParams": [
+    {
+      "key": "userId",
+      "value": "${userId}"
+    },
+    {
+      "key": "page",
+      "value": "${page}"
+    }
+  ]
+}
+```
+
+## Usage Guidelines
+
+1. **Parameter Types**: Supports `string`, `integer`, `number`, `boolean`, `array`, `object`
+2. **Required Parameters**: Controlled by the `required` field
+3. **Timeout Settings**: Configure timeout values in milliseconds via the `timeout` field
+4. **Request Headers**: Configure using `key` and `value` fields
+5. **Query Parameters**: Configure using `queryParams` array with `${paramName}` format values
+6. **Path Parameters**: Use `{{.paramName}}` format in URL
+7. **Parameter Mapping**: Use `argsPosition` to specify parameter location: `path`, `query`, `body`
+
+## Best Practices
+
+### 1. **Start Simple**
+Begin with basic GET/POST examples and gradually add complexity.
+
+### 2. **Timeout Configuration**
+- Simple queries: 30 seconds
+- Complex operations: 45-120 seconds
+- File uploads: 60+ seconds
+
+### 3. **Error Handling**
+Implement retry mechanisms and proper error responses at the business layer.
+
+### 4. **Security**
+- Always use HTTPS in production
+- Include authentication headers when accessing secure endpoints
+- Validate input parameters
+
+### 5. **Performance**
+- Set appropriate timeouts
+- Use connection pooling for high-frequency calls
+- Monitor and log API performance
+
+These examples are based on **real interfaces from the shenyu-examples-http project** and can be used and tested directly in a Shenyu environment.
+
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java
index 0ac672d156..fce5ac904c 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java
@@ -31,7 +31,9 @@ import org.apache.shenyu.plugin.mcp.server.holder.ShenyuMcpExchangeHolder;
 import org.apache.shenyu.plugin.mcp.server.manager.ShenyuMcpServerManager;
 import org.apache.shenyu.plugin.mcp.server.model.ShenyuMcpServer;
 import org.apache.shenyu.plugin.mcp.server.transport.ShenyuSseServerTransportProvider;
+import org.apache.shenyu.plugin.mcp.server.transport.ShenyuStreamableHttpServerTransportProvider;
 import org.apache.shenyu.plugin.mcp.server.transport.SseEventFormatter;
+import org.apache.shenyu.plugin.mcp.server.transport.MessageHandlingResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
@@ -39,187 +41,604 @@ import org.springframework.http.codec.HttpMessageReader;
 import org.springframework.web.reactive.function.server.ServerRequest;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.nio.charset.StandardCharsets;
 
 /**
- * MCP Server Plugin.
- * Handles MCP (Message Control Protocol) server-side functionality.
+ * MCP (Model Context Protocol) Server Plugin for Shenyu Gateway.
+ *
+ * <p>Provides MCP server functionality supporting both SSE and Streamable HTTP transport protocols.
+ * Enables AI models to interact with Shenyu Gateway services through standardized MCP tool definitions.</p>
+ *
+ * @see org.apache.shenyu.plugin.base.AbstractShenyuPlugin
+ * @see org.apache.shenyu.plugin.mcp.server.manager.ShenyuMcpServerManager
+ * @since 2.7.0.2
  */
 public class McpServerPlugin extends AbstractShenyuPlugin {
-    
+
     private static final Logger LOG = LoggerFactory.getLogger(McpServerPlugin.class);
-    
+
+    /**
+     * Standard message endpoint path.
+     */
     private static final String MESSAGE_ENDPOINT = "/message";
-    
+
+    /**
+     * Streamable HTTP protocol path identifier.
+     */
+    private static final String STREAMABLE_HTTP_PATH = "/streamablehttp";
+
+    /**
+     * SSE protocol path identifier.
+     */
+    private static final String SSE_PATH = "/sse";
+
+    /**
+     * MCP tool call prevention attribute.
+     */
+    private static final String MCP_TOOL_CALL_ATTR = "MCP_TOOL_CALL";
+
+    /**
+     * MCP session ID attribute key.
+     */
+    private static final String MCP_SESSION_ID_ATTR = "MCP_SESSION_ID";
+
+    /**
+     * Session ID query parameter name.
+     */
+    private static final String SESSION_ID_PARAM = "sessionId";
+
+    /**
+     * Session ID header names (in order of preference).
+     */
+    private static final String[] SESSION_ID_HEADERS = {
+            "X-Session-Id", "Mcp-Session-Id",
+    };
+
+    /**
+     * Authorization header name.
+     */
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+
+    /**
+     * Bearer token prefix.
+     */
+    private static final String BEARER_PREFIX = "Bearer ";
+
     private final ShenyuMcpServerManager shenyuMcpServerManager;
-    
+
     private final List<HttpMessageReader<?>> messageReaders;
-    
-    public McpServerPlugin(final ShenyuMcpServerManager shenyuMcpServerManager, final List<HttpMessageReader<?>> messageReaders) {
+
+    /**
+     * Constructs a new MCP server plugin.
+     *
+     * @param shenyuMcpServerManager the MCP server manager for handling transport providers
+     * @param messageReaders         the HTTP message readers for request processing
+     */
+    public McpServerPlugin(final ShenyuMcpServerManager shenyuMcpServerManager,
+                           final List<HttpMessageReader<?>> messageReaders) {
         this.shenyuMcpServerManager = shenyuMcpServerManager;
         this.messageReaders = messageReaders;
     }
-    
+
     @Override
     protected String getRawPath(final ServerWebExchange exchange) {
         return RequestUrlUtils.getRewrittenRawPath(exchange);
     }
-    
+
     @Override
-    protected Mono<Void> doExecute(final ServerWebExchange exchange, final ShenyuPluginChain chain, final SelectorData selector, final RuleData rule) {
-        ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
+    protected Mono<Void> doExecute(final ServerWebExchange exchange,
+                                   final ShenyuPluginChain chain,
+                                   final SelectorData selector,
+                                   final RuleData rule) {
+
+        final ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
         Objects.requireNonNull(shenyuContext, "ShenyuContext must not be null");
-        
-        ShenyuMcpServer server = McpServerPluginDataHandler.CACHED_SERVER.get().obtainHandle(selector.getId());
-        
-        if (Objects.isNull(server)) {
-            return chain.execute(exchange);
-        }
-        
-        String uri = exchange.getRequest().getURI().getRawPath();
-        LOG.info("Processing request with URI: {}", uri);
-        
+
+        final String uri = exchange.getRequest().getURI().getRawPath();
+        LOG.debug("Processing MCP request with URI: {}", uri);
+
         if (!shenyuMcpServerManager.canRoute(uri)) {
-            // Continue the chain - the WebFlux infrastructure will route to the appropriate
-            // handler
+            LOG.debug("URI not handled by MCP server, continuing chain: {}", uri);
             return chain.execute(exchange);
         }
-        
-        LOG.info("Handling MCP request for URI: {}", uri);
-        
-        // Extract sessionId from request if available
-        ServerRequest request = ServerRequest.create(exchange, messageReaders);
-        
-        // Handle MCP SSE/message requests by delegating to the transport provider
-        // directly
-        String messageEndpoint = server.getMessageEndpoint();
-        
-        ShenyuSseServerTransportProvider transportProvider
-                = shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, messageEndpoint);
-        
-        // Mark exchange to prevent further plugin processing
-        shenyuContext.setRpcType(RpcTypeEnum.AI.getName());
-        exchange.getAttributes().put(Constants.CONTEXT, shenyuContext);
-        
-        if (uri.endsWith(messageEndpoint)) {
-            String sessionId = extractSessionId(exchange);
-            if (Objects.nonNull(sessionId)) {
-                exchange.getAttributes().put("MCP_SESSION_ID", sessionId);
-                exchange.getAttributes().put(Constants.CHAIN, chain);
-                // Store sessionId in exchange attributes for later use
-                ShenyuMcpExchangeHolder.put(sessionId, exchange);
-                LOG.debug("Extracted sessionId: {}", sessionId);
-            }
-            // Handle JSON-RPC message endpoint
-            LOG.debug("Handling MCP message endpoint for URI: {} with sessionId: {}", uri, sessionId);
-            return handleMessageEndpoint(exchange, transportProvider, request)
-                    .doFinally(signalType -> ShenyuMcpExchangeHolder.remove(sessionId));
-        } else {
-            // Handle SSE connection endpoint
-            LOG.debug("Handling MCP SSE connection for URI: {} ", uri);
-            
-            // Create a custom SSE handler that bypasses the ServerResponse complexity
-            return handleSseEndpoint(exchange, transportProvider, request);
-        }
-        
+
+        LOG.debug("Handling MCP request for URI: {}", uri);
+
+        // Create server request for processing
+        final ServerRequest request = ServerRequest.create(exchange, messageReaders);
+
+        // Route based on protocol type
+        return routeByProtocol(exchange, chain, request, selector, uri);
     }
-    
+
     @Override
     public String named() {
         return PluginEnum.MCP_SERVER.getName();
     }
-    
+
     @Override
     public boolean skip(final ServerWebExchange exchange) {
+        // Skip MCP plugin for MCP tool calls to prevent infinite loops
+        final Boolean isMcpToolCall = exchange.getAttribute(MCP_TOOL_CALL_ATTR);
+        if (Boolean.TRUE.equals(isMcpToolCall)) {
+            LOG.debug("Skipping MCP plugin for tool call to prevent infinite loop");
+            return true;
+        }
+
         return skipExcept(exchange, RpcTypeEnum.HTTP);
     }
-    
+
     @Override
     public int getOrder() {
         return PluginEnum.MCP_SERVER.getCode();
     }
-    
+
+
+    /**
+     * Routes the request based on detected protocol type.
+     *
+     * @param exchange the server web exchange
+     * @param chain    the plugin chain
+     * @param request  the server request
+     * @param uri      the request URI
+     * @return a Mono representing the processing result
+     */
+    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)) {
+            return handleSseRequest(exchange, chain, request, selector, uri);
+        } else {
+            // Default to SSE for backward compatibility
+            LOG.debug("Using default SSE protocol for URI: {}", uri);
+            return handleSseRequest(exchange, chain, request, selector, uri);
+        }
+    }
+
     /**
-     * Extract sessionId from request parameters or headers.
+     * Extracts session ID from request parameters or headers.
+     * Searches in the following order:
+     * <ol>
+     *   <li>Query parameter "sessionId"</li>
+     *   <li>Header "X-Session-Id"</li>
+     *   <li>Header "Mcp-Session-Id"</li>
+     *   <li>Authorization header (Bearer token)</li>
+     * </ol>
      *
-     * @param exchange The ServerWebExchange containing the request
-     * @return The sessionId if found, null otherwise
+     * @param exchange the ServerWebExchange containing the request
+     * @return the sessionId if found, null otherwise
      */
     private String extractSessionId(final ServerWebExchange exchange) {
-        // Try query parameters
-        String sessionId = exchange.getRequest().getQueryParams().getFirst("sessionId");
+        // Try query parameters first
+        String sessionId = exchange.getRequest().getQueryParams().getFirst(SESSION_ID_PARAM);
         if (Objects.nonNull(sessionId)) {
             LOG.debug("Found sessionId in query parameters: {}", sessionId);
             return sessionId;
         }
-        
-        // Try X-Session-Id header
-        sessionId = exchange.getRequest().getHeaders().getFirst("X-Session-Id");
-        if (Objects.nonNull(sessionId)) {
-            LOG.debug("Found sessionId in X-Session-Id header: {}", sessionId);
-            return sessionId;
+
+        // Try session ID headers
+        for (String headerName : SESSION_ID_HEADERS) {
+            sessionId = exchange.getRequest().getHeaders().getFirst(headerName);
+            if (Objects.nonNull(sessionId)) {
+                LOG.debug("Found sessionId in {} header: {}", headerName, sessionId);
+                return sessionId;
+            }
         }
-        
-        // Try Authorization header
-        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
-        if (Objects.nonNull(authHeader) && authHeader.startsWith("Bearer ")) {
-            sessionId = authHeader.substring(7);
+
+        // Try Authorization header as fallback
+        final String authHeader = exchange.getRequest().getHeaders().getFirst(AUTHORIZATION_HEADER);
+        if (Objects.nonNull(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
+            sessionId = authHeader.substring(BEARER_PREFIX.length());
             LOG.debug("Found sessionId in Authorization header: {}", sessionId);
             return sessionId;
         }
-        
+
         LOG.debug("No sessionId found in request for path: {}", exchange.getRequest().getPath().value());
         return null;
     }
-    
+
+    /**
+     * Checks if the URI indicates Streamable HTTP protocol.
+     *
+     * @param uri the URI to check
+     * @return true if Streamable HTTP protocol should be used
+     */
+    private boolean isStreamableHttpProtocol(final String uri) {
+        return uri.contains(STREAMABLE_HTTP_PATH) || uri.endsWith(STREAMABLE_HTTP_PATH);
+    }
+
+    /**
+     * Checks if the URI indicates SSE protocol.
+     *
+     * @param uri the URI to check
+     * @return true if SSE protocol should be used
+     */
+    private boolean isSseProtocol(final String uri) {
+        return uri.contains(SSE_PATH) || uri.endsWith(SSE_PATH) || uri.endsWith(MESSAGE_ENDPOINT);
+    }
+
     /**
-     * Create a direct SSE response by manually setting up the SSE stream.
-     * This bypasses the ServerResponse complexity and writes directly to the
-     * exchange.
+     * Handles Streamable HTTP MCP requests with unified endpoint processing.
+     *
+     * @param exchange the server web exchange
+     * @param chain    the plugin chain
+     * @param request  the server request
+     * @param uri      the request URI
+     * @return a Mono representing the processing result
      */
-    private Mono<Void> handleSseEndpoint(final ServerWebExchange exchange, final ShenyuSseServerTransportProvider transportProvider, final ServerRequest request) {
-        LOG.debug("Handling SSE endpoint for request: {}", request.path());
-        
-        // Set SSE headers
+    private Mono<Void> handleStreamableHttpRequest(final ServerWebExchange exchange,
+                                                   final ShenyuPluginChain chain,
+                                                   final ServerRequest request,
+                                                   final String uri) {
+
+        LOG.debug("Handling Streamable HTTP MCP request for URI: {}", uri);
+
+        final ShenyuStreamableHttpServerTransportProvider transportProvider =
+                shenyuMcpServerManager.getOrCreateStreamableHttpTransport(uri);
+
+        setupSessionContext(exchange, chain);
+
+        return processStreamableHttpEndpoint(exchange, transportProvider, request);
+    }
+
+    /**
+     * Handles SSE MCP requests with endpoint-specific routing.
+     *
+     * @param exchange the server web exchange
+     * @param chain    the plugin chain
+     * @param request  the server request
+     * @param uri      the request URI
+     * @return a Mono representing the processing result
+     */
+    private Mono<Void> handleSseRequest(final ServerWebExchange exchange,
+                                        final ShenyuPluginChain chain,
+                                        final ServerRequest request,
+                                        final SelectorData selector,
+                                        final String uri) {
+
+        LOG.debug("Handling SSE MCP request for URI: {}", uri);
+        ShenyuMcpServer server = McpServerPluginDataHandler.CACHED_SERVER.get().obtainHandle(selector.getId());
+
+        if (Objects.isNull(server)) {
+            return chain.execute(exchange);
+        }
+        // Handle MCP SSE/message requests by delegating to the transport provider
+        // directly
+        String messageEndpoint = server.getMessageEndpoint();
+
+        ShenyuSseServerTransportProvider transportProvider
+                = shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, messageEndpoint);
+
+        if (uri.endsWith(messageEndpoint)) {
+            setupSessionContext(exchange, chain);
+            return handleMessageEndpoint(exchange, transportProvider, request);
+        } else {
+            return handleSseEndpoint(exchange, transportProvider, request);
+        }
+    }
+
+    /**
+     * Sets up session context and exchange correlation.
+     *
+     * @param exchange the server web exchange
+     * @param chain    the plugin chain
+     */
+    private void setupSessionContext(final ServerWebExchange exchange, final ShenyuPluginChain chain) {
+        final String sessionId = extractSessionId(exchange);
+        if (Objects.nonNull(sessionId)) {
+            exchange.getAttributes().put(MCP_SESSION_ID_ATTR, sessionId);
+            exchange.getAttributes().put(Constants.CHAIN, chain);
+            ShenyuMcpExchangeHolder.put(sessionId, exchange);
+            LOG.debug("Set up session context for sessionId: {}", sessionId);
+        }
+    }
+
+    /**
+     * Processes Streamable HTTP unified endpoint requests.
+          * Handles both GET (stream establishment) and POST (message processing) requests
+     * according to the Streamable HTTP protocol specification.
+     *
+     * @param exchange          the server web exchange
+     * @param transportProvider the Streamable HTTP transport provider
+     * @param request           the server request
+     * @return a Mono representing the processing result
+     */
+    private Mono<Void> processStreamableHttpEndpoint(final ServerWebExchange exchange,
+                                                     final ShenyuStreamableHttpServerTransportProvider transportProvider,
+                                                     final ServerRequest request) {
+
+        LOG.debug("Processing Streamable HTTP endpoint for request: {}", request.path());
+
+        final String method = exchange.getRequest().getMethod().name();
+
+        if ("GET".equalsIgnoreCase(method)) {
+            return handleStreamableHttpGetRequest(exchange);
+        } else if ("POST".equalsIgnoreCase(method)) {
+            return handleStreamableHttpPostRequest(exchange, transportProvider, request);
+        } else {
+            return handleUnsupportedMethod(exchange);
+        }
+    }
+
+    /**
+     * Handles Streamable HTTP GET requests (not supported by protocol).
+     *
+     * @param exchange the server web exchange
+     * @return a Mono representing the error response
+     */
+    private Mono<Void> handleStreamableHttpGetRequest(final ServerWebExchange exchange) {
+        LOG.debug("Rejecting Streamable HTTP GET request (protocol does not support GET)");
+
+        setErrorResponse(exchange, HttpStatus.METHOD_NOT_ALLOWED,
+                "POST, OPTIONS",
+                createJsonError(-32601, "Streamable HTTP does not support GET requests. Please use POST requests for all MCP operations."));
+
+        return writeJsonResponse(exchange);
+    }
+
+    /**
+     * Handles Streamable HTTP POST requests for message processing.
+     *
+     * @param exchange          the server web exchange
+     * @param transportProvider the transport provider
+     * @param request           the server request
+     * @return a Mono representing the processing result
+     */
+    private Mono<Void> handleStreamableHttpPostRequest(final ServerWebExchange exchange,
+                                                       final ShenyuStreamableHttpServerTransportProvider transportProvider,
+                                                       final ServerRequest request) {
+
+        LOG.debug("Processing Streamable HTTP POST request for message handling");
+
+        return transportProvider.handleMessageEndpoint(exchange, request)
+                .flatMap(result -> processStreamableHttpResult(exchange, result))
+                .doOnSuccess(aVoid -> LOG.debug("Streamable HTTP message processing completed"))
+                .doOnError(error -> LOG.error("Error in Streamable HTTP message processing: {}", error.getMessage(), error));
+    }
+
+    /**
+     * Processes the result of Streamable HTTP message handling.
+     *
+     * @param exchange the server web exchange
+     * @param result   the message handling result
+     * @return a Mono representing the response writing
+     */
+    private Mono<Void> processStreamableHttpResult(final ServerWebExchange exchange,
+                                                   final MessageHandlingResult result) {
+
+        LOG.debug("Processing Streamable HTTP result - Status: {}, SessionId: {}",
+                result.getStatusCode(), result.getSessionId());
+
+        // Configure response
+        configureStreamableHttpResponse(exchange, result);
+
+        // Write response body
+        final String responseBodyJson = result.getResponseBodyAsJson();
+        final byte[] responseBytes = responseBodyJson.getBytes(StandardCharsets.UTF_8);
+
+        LOG.debug("Writing response body with {} bytes", responseBytes.length);
+
+        return exchange.getResponse()
+                .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseBytes)))
+                .doOnSuccess(aVoid -> LOG.debug("Response transmission completed successfully"))
+                .doOnError(error -> LOG.error("Error writing response: {}", error.getMessage(), error));
+    }
+
+    /**
+     * Configures response headers for Streamable HTTP.
+     *
+     * @param exchange the server web exchange
+     * @param result   the message handling result
+     */
+    private void configureStreamableHttpResponse(final ServerWebExchange exchange,
+                                                 final MessageHandlingResult result) {
+
+        // Set response status
+        exchange.getResponse().setStatusCode(HttpStatus.valueOf(result.getStatusCode()));
+
+        // Add session ID header if available
+        if (Objects.nonNull(result.getSessionId())) {
+            exchange.getResponse().getHeaders().set("Mcp-Session-Id", result.getSessionId());
+        } else {
+            // Fallback to extracting from exchange
+            final String sessionId = extractSessionId(exchange);
+            if (Objects.nonNull(sessionId)) {
+                exchange.getResponse().getHeaders().set("Mcp-Session-Id", sessionId);
+            }
+        }
+
+        // Set standard headers
+        setCorsHeaders(exchange);
+        exchange.getResponse().getHeaders().set("Content-Type", "application/json");
+
+        // Clean up potentially conflicting headers
+        exchange.getResponse().getHeaders().remove("Transfer-Encoding");
+        exchange.getResponse().getHeaders().remove("Content-Length");
+
+        LOG.debug("Configured Streamable HTTP response headers");
+    }
+
+    /**
+     * Handles unsupported HTTP methods.
+     *
+     * @param exchange the server web exchange
+     * @return a Mono representing the error response
+     */
+    private Mono<Void> handleUnsupportedMethod(final ServerWebExchange exchange) {
+        LOG.debug("Unsupported HTTP method: {}", exchange.getRequest().getMethod());
+
+        setErrorResponse(exchange, HttpStatus.BAD_REQUEST,
+                null,
+                createJsonError(-32600, "Unsupported HTTP method"));
+
+        return writeJsonResponse(exchange);
+    }
+
+    /**
+     * Creates SSE response by setting up the SSE stream.
+     *
+     * @param exchange          the server web exchange
+     * @param transportProvider the SSE transport provider
+     * @param request           the server request
+     * @return a Mono representing the SSE stream
+     */
+    private Mono<Void> handleSseEndpoint(final ServerWebExchange exchange,
+                                         final ShenyuSseServerTransportProvider transportProvider,
+                                         final ServerRequest request) {
+
+        LOG.debug("Setting up SSE endpoint for request: {}", request.path());
+
+        // Configure SSE headers
         configureSseHeaders(exchange);
-        
-        // Create SSE flux and write each event immediately to the exchange response
+
+        // Create and write SSE stream
         return exchange.getResponse()
                 .writeWith(transportProvider
                         .createSseFlux(request)
-                        .doOnNext(event -> LOG.debug("SSE Event - Type: {}, Data: {}", event.event(), event.data()))
+                        .doOnNext(event -> {
+                            String eventType = event.event();
+                            LOG.debug("SSE Event - Type: {}", Objects.isNull(eventType) ? "data" : eventType);
+                        })
                         .map(event -> SseEventFormatter.formatEvent(event, exchange))
                         .doOnSubscribe(subscription -> LOG.debug("SSE stream subscribed"))
                         .doOnComplete(() -> LOG.debug("SSE stream completed"))
-                        .doOnError(error -> LOG.error("SSE stream error: {}", error.getMessage())));
+                        .doOnError(error -> LOG.error("SSE stream error: {}", error.getMessage(), error)));
     }
-    
+
+    /**
+     * Configures SSE response headers.
+     *
+     * @param exchange the server web exchange
+     */
     private void configureSseHeaders(final ServerWebExchange exchange) {
-        exchange.getResponse().getHeaders().add("Content-Type", "text/event-stream");
-        exchange.getResponse().getHeaders().add("Cache-Control", "no-cache");
-        exchange.getResponse().getHeaders().add("Connection", "keep-alive");
-        exchange.getResponse().getHeaders().add("Access-Control-Allow-Origin", "*");
-        LOG.debug("Configured SSE headers on exchange response");
-    }
-    
-    private Mono<Void> handleMessageEndpoint(final ServerWebExchange exchange, final ShenyuSseServerTransportProvider transportProvider, final ServerRequest request) {
-        
-        return transportProvider.handleMessageEndpoint(request).flatMap(result -> {
-            LOG.debug("Message handling result - Status: {}, Body: {}", result.getStatusCode(), result.getResponseBody());
-            
-            // Set response status
-            exchange.getResponse().setStatusCode(HttpStatus.valueOf(result.getStatusCode()));
-            // Set appropriate headers
-            exchange.getResponse().getHeaders().add("Content-Type", "application/json");
-            exchange.getResponse().getHeaders().add("Access-Control-Allow-Origin", "*");
-            
-            // Write response body
-            String responseBody = String.format("{\"message\":\"%s\"}", result.getResponseBody());
-            LOG.debug("Sending response body: {}", responseBody);
-            
-            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseBody.getBytes())));
-        }).doOnSuccess(aVoid -> LOG.debug("Message response writing completed")).doOnError(error -> LOG.error("Error writing message response: {}", error.getMessage()));
+        exchange.getResponse().getHeaders().set("Content-Type", "text/event-stream");
+        exchange.getResponse().getHeaders().set("Cache-Control", "no-cache");
+        exchange.getResponse().getHeaders().set("Connection", "keep-alive");
+        setCorsHeaders(exchange);
+        LOG.debug("Configured SSE headers");
+    }
+
+    /**
+     * Handles message endpoint requests for SSE protocol.
+     *
+     * @param exchange          the server web exchange
+     * @param transportProvider the SSE transport provider
+     * @param request           the server request
+     * @return a Mono representing the processing result
+     */
+    private Mono<Void> handleMessageEndpoint(final ServerWebExchange exchange,
+                                             final ShenyuSseServerTransportProvider transportProvider,
+                                             final ServerRequest request) {
+
+        LOG.debug("Processing message endpoint request");
+
+        return transportProvider.handleMessageEndpoint(request)
+                .flatMap(result -> {
+                    LOG.debug("Message handling result - Status: {}, Body length: {} chars",
+                            result.getStatusCode(),
+                            Objects.nonNull(result.getResponseBody()) ? result.getResponseBody().length() : 0);
+
+                    // Configure response
+                    exchange.getResponse().setStatusCode(HttpStatus.valueOf(result.getStatusCode()));
+                    exchange.getResponse().getHeaders().add("Content-Type", "application/json");
+                    setCorsHeaders(exchange);
+
+                    // Create response body
+                    final String responseBody = String.format("{\"message\":\"%s\"}", result.getResponseBody());
+                    LOG.debug("Sending message response with length: {} chars", responseBody.length());
+
+                    return exchange.getResponse()
+                            .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(responseBody.getBytes())));
+                })
+                .doOnSuccess(aVoid -> LOG.debug("Message response completed"))
+                .doOnError(error -> LOG.error("Error in message response: {}", error.getMessage(), error));
     }
+
+    /**
+     * Sets CORS headers for cross-origin requests.
+     *
+     * @param exchange the server web exchange
+     */
+    private void setCorsHeaders(final ServerWebExchange exchange) {
+        exchange.getResponse().getHeaders().set("Access-Control-Allow-Origin", "*");
+        exchange.getResponse().getHeaders().set("Access-Control-Allow-Headers",
+                "Content-Type, Mcp-Session-Id, Authorization, Last-Event-ID");
+        exchange.getResponse().getHeaders().set("Access-Control-Allow-Methods",
+                "GET, POST, OPTIONS");
+    }
+
+    /**
+     * Sets up error response configuration.
+     *
+     * @param exchange    the server web exchange
+     * @param status      the HTTP status
+     * @param allowHeader the Allow header value (nullable)
+     * @param errorBody   the error response body
+     */
+    private void setErrorResponse(final ServerWebExchange exchange,
+                                  final HttpStatus status,
+                                  final String allowHeader,
+                                  final Map<String, Object> errorBody) {
+
+        exchange.getResponse().setStatusCode(status);
+        exchange.getResponse().getHeaders().add("Content-Type", "application/json");
+        setCorsHeaders(exchange);
+
+        if (Objects.nonNull(allowHeader)) {
+            exchange.getResponse().getHeaders().add("Allow", allowHeader);
+        }
+
+        exchange.getAttributes().put("errorBody", errorBody);
+    }
+
+    /**
+     * Writes JSON response to the exchange.
+     *
+     * @param exchange the server web exchange
+     * @return a Mono representing the write operation
+     */
+    private Mono<Void> writeJsonResponse(final ServerWebExchange exchange) {
+        @SuppressWarnings("unchecked") final Map<String, Object> errorBody = (Map<String, Object>) exchange.getAttributes().get("errorBody");
+
+        if (Objects.isNull(errorBody)) {
+            return Mono.empty();
+        }
+
+        try {
+            final String errorResponse = new ObjectMapper()
+                    .writeValueAsString(errorBody);
+            return exchange.getResponse()
+                    .writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(errorResponse.getBytes())));
+        } catch (Exception e) {
+            LOG.error("Error writing JSON response: {}", e.getMessage(), e);
+            return Mono.empty();
+        }
+    }
+
+    /**
+     * Creates a standard JSON-RPC error object.
+     *
+     * @param code    the error code
+     * @param message the error message
+     * @return the error object as a Map
+     */
+    private Map<String, Object> createJsonError(final int code, final String message) {
+        return Map.of(
+                "error", Map.of(
+                        "code", code,
+                        "message", message
+                )
+        );
+    }
+
 }
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
index c5099af0d1..c1d3583047 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java
@@ -18,11 +18,11 @@
 package org.apache.shenyu.plugin.mcp.server.callback;
 
 import com.google.common.collect.Maps;
+import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import io.modelcontextprotocol.server.McpSyncServerExchange;
 import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.dto.MetaData;
-import org.apache.shenyu.common.enums.RpcTypeEnum;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
 import org.apache.shenyu.plugin.api.context.ShenyuContext;
@@ -30,9 +30,11 @@ import org.apache.shenyu.plugin.base.cache.MetaDataCache;
 import org.apache.shenyu.plugin.mcp.server.definition.ShenyuToolDefinition;
 import org.apache.shenyu.plugin.mcp.server.holder.ShenyuMcpExchangeHolder;
 import org.apache.shenyu.plugin.mcp.server.request.BodyWriterExchange;
+import org.apache.shenyu.plugin.mcp.server.request.ParameterFormatter;
 import org.apache.shenyu.plugin.mcp.server.request.RequestConfig;
 import org.apache.shenyu.plugin.mcp.server.request.RequestConfigHelper;
 import org.apache.shenyu.plugin.mcp.server.response.ShenyuMcpResponseDecorator;
+import org.apache.shenyu.plugin.mcp.server.response.NonCommittingMcpResponseDecorator;
 import org.apache.shenyu.plugin.mcp.server.session.McpSessionHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,199 +55,628 @@ import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 
+/**
+ * Tool Callback Implementation for Shenyu Gateway MCP Integration.
+ * 
+ * <p>Handles tool invocations within the MCP framework, enabling AI models to interact
+ * with Shenyu Gateway services through defined tools. Requires pre-established session
+ * context and ServerWebExchange correlation.</p>
+ *
+ * @see ToolCallback
+ * @see ToolDefinition
+ * @since 2.7.0.2
+ */
 public class ShenyuToolCallback implements ToolCallback {
-    
+
     private static final Logger LOG = LoggerFactory.getLogger(ShenyuToolCallback.class);
-    
+
+    /**
+     * Default timeout for tool execution in seconds.
+     */
     private static final int DEFAULT_TIMEOUT_SECONDS = 30;
-    
+
+    /**
+     * MCP tool call attribute marker to prevent infinite loops.
+     */
+    private static final String MCP_TOOL_CALL_ATTR = "MCP_TOOL_CALL";
+
+    /**
+     * MCP session ID attribute key.
+     */
+    private static final String MCP_SESSION_ID_ATTR = "MCP_TOOL_SESSION_ID";
+
+    /**
+     * Streamable HTTP protocol path indicator.
+     */
+    private static final String STREAMABLE_HTTP_PATH = "/streamablehttp";
+
     private final ToolDefinition toolDefinition;
-    
+
+    /**
+     * Constructs a new Shenyu tool callback with the specified tool definition.
+     *
+     * @param toolDefinition the tool definition that this callback will handle
+     * @throws NullPointerException if toolDefinition is null
+     */
     public ShenyuToolCallback(final ToolDefinition toolDefinition) {
         this.toolDefinition = Objects.requireNonNull(toolDefinition, "ToolDefinition cannot be null");
     }
-    
+
     @NonNull
     @Override
     public ToolDefinition getToolDefinition() {
         return this.toolDefinition;
     }
-    
+
     @NonNull
     @Override
     public String call(@NonNull final String input) {
         return call(input, new ToolContext(Maps.newHashMap()));
     }
-    
+
     @NonNull
     @Override
     public String call(@NonNull final String input, final ToolContext toolContext) {
         Objects.requireNonNull(input, "Input cannot be null");
         Objects.requireNonNull(toolContext, "ToolContext cannot be null");
-        
+
+        LOG.debug("Executing tool call for definition '{}' with input length: {} chars",
+                toolDefinition.name(), input.length());
+
         try {
-            // Get MCP session information
-            final McpSyncServerExchange mcpSyncServerExchange = getMcpSyncServerExchange(toolContext);
-            final String sessionId = getSessionId(mcpSyncServerExchange);
-            
-            // Get tool definition and request configuration
-            final ShenyuToolDefinition shenyuToolDefinition = validateAndGetToolDefinition();
-            final String configStr = validateAndGetRequestConfig(shenyuToolDefinition);
-            
-            // Get original exchange object
-            final ServerWebExchange originExchange = ShenyuMcpExchangeHolder.get(sessionId);
-            if (Objects.isNull(originExchange)) {
-                throw new IllegalStateException("No exchange found for sessionId: " + sessionId);
-            }
-            ShenyuPluginChain chain = originExchange.getAttribute(Constants.CHAIN);
-            Assert.notNull(chain, "ShenyuPluginChain cannot be null");
-            
-            final CompletableFuture<String> future = new CompletableFuture<>();
-            
-            // Build new exchange object
-            final ServerWebExchange decoratedExchange = buildExchange(originExchange, future, sessionId, configStr, input);
-            
-            LOG.info("[MCP] Executing plugin chain for session: {}", sessionId);
-            chain.execute(decoratedExchange).doOnSubscribe(s -> LOG.info("[MCP] Subscribed to plugin chain for session: {}", sessionId)).doOnError(e -> {
-                LOG.error("[MCP] Error processing request for session: {}", sessionId, e);
-                future.completeExceptionally(e);
-            }).doOnSuccess(v -> LOG.info("[MCP] Plugin chain completed successfully for session: {}", sessionId)).doOnCancel(() -> {
-                LOG.warn("[MCP] Plugin chain was cancelled for session: {}", sessionId);
-                future.completeExceptionally(new RuntimeException("Request was cancelled"));
-            }).subscribe();
-            
-            try {
-                return future.get(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-            } catch (Exception e) {
-                LOG.error("[MCP] Error waiting for response for session: {}", sessionId, e);
-                throw new RuntimeException("Error waiting for response: " + e.getMessage(), e);
-            }
+            // Extract MCP session context (must be pre-established)
+            final McpSyncServerExchange mcpExchange = extractMcpExchange(toolContext);
+            final String sessionId = extractSessionId(mcpExchange);
+
+            // Validate and extract tool configuration
+            final ShenyuToolDefinition shenyuTool = validateToolDefinition();
+            final String configStr = extractRequestConfig(shenyuTool);
+
+            // Get pre-stored exchange and plugin chain
+            final ServerWebExchange originExchange = getOriginExchange(sessionId);
+            final ShenyuPluginChain chain = getPluginChain(originExchange);
+
+            // Execute the tool call through the plugin chain
+            return executeToolCall(originExchange, chain, sessionId, configStr, input);
+
         } catch (Exception e) {
-            LOG.error("Failed to process request: {}", e.getMessage(), e);
-            throw new RuntimeException("Failed to process request: " + e.getMessage(), e);
-        }
-    }
-    
-    private McpSyncServerExchange getMcpSyncServerExchange(final ToolContext toolContext) {
-        final McpSyncServerExchange exchange = McpSessionHelper.getMcpSyncServerExchange(toolContext);
-        if (Objects.isNull(exchange)) {
-            throw new IllegalStateException("Failed to get MCP sync server exchange");
-        }
-        return exchange;
-    }
-    
-    private String getSessionId(final McpSyncServerExchange exchange) throws NoSuchFieldException, IllegalAccessException {
-        final String sessionId = McpSessionHelper.getSessionId(exchange);
-        if (!StringUtils.hasText(sessionId)) {
-            throw new IllegalStateException("Session ID cannot be empty");
+            LOG.error("Failed to process tool call for '{}': {}", toolDefinition.name(), e.getMessage(), e);
+
+            throw new RuntimeException("Tool execution failed: " + e.getMessage(), e);
         }
-        return sessionId;
     }
-    
-    private ShenyuToolDefinition validateAndGetToolDefinition() {
+
+    /**
+     * Validates and casts the tool definition to Shenyu-specific type.
+     *
+     * @return the Shenyu tool definition
+     * @throws IllegalStateException if tool definition is not of expected type
+     */
+    private ShenyuToolDefinition validateToolDefinition() {
         if (!(this.toolDefinition instanceof ShenyuToolDefinition)) {
-            throw new IllegalStateException("Invalid tool definition type");
+            throw new IllegalStateException("Tool definition must be of type ShenyuToolDefinition, got: "
+                    + this.toolDefinition.getClass().getSimpleName());
         }
         return (ShenyuToolDefinition) this.toolDefinition;
     }
-    
-    private String validateAndGetRequestConfig(final ShenyuToolDefinition definition) {
+
+    /**
+     * Extracts and validates the request configuration from the tool definition.
+     *
+     * @param definition the Shenyu tool definition
+     * @return the request configuration string
+     * @throws IllegalStateException if configuration is missing or invalid
+     */
+    private String extractRequestConfig(final ShenyuToolDefinition definition) {
         final String config = definition.requestConfig();
         if (!StringUtils.hasText(config)) {
             throw new IllegalStateException("Request configuration cannot be empty");
         }
-        LOG.debug("Request configuration: {}", config);
+        LOG.debug("Using request configuration with length: {} chars", config.length());
         return config;
     }
-    
-    private ServerWebExchange buildExchange(final ServerWebExchange originExchange, final CompletableFuture<String> future, final String sessionId, final String configStr, final String input) {
+
+    /**
+     * Extracts the plugin chain from the exchange.
+     *
+     * @param exchange the server web exchange
+     * @return the plugin chain
+     * @throws IllegalStateException if chain is not found
+     */
+    private ShenyuPluginChain getPluginChain(final ServerWebExchange exchange) {
+        final ShenyuPluginChain chain = exchange.getAttribute(Constants.CHAIN);
+        Assert.notNull(chain, "ShenyuPluginChain cannot be null");
+        return chain;
+    }
+
+    /**
+     * Executes the tool call through the Shenyu plugin chain.
+     *
+     * @param originExchange the original server web exchange
+     * @param chain          the plugin chain to execute
+     * @param sessionId      the MCP session identifier
+     * @param configStr      the request configuration
+     * @param input          the tool input parameters
+     * @return the execution result
+     */
+    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);
+
+        LOG.debug("Executing plugin chain for session: {}", sessionId);
+
+        // Check if this is a temporary session that needs cleanup
+        final boolean isTemporarySession = sessionId.startsWith("temp_");
+
+        // Execute the plugin chain asynchronously
+        chain.execute(decoratedExchange)
+                .doOnSubscribe(s -> LOG.debug("Plugin chain subscribed for session: {}", sessionId))
+                .doOnError(e -> {
+                    LOG.error("Plugin chain execution failed for session {}: {}", sessionId, e.getMessage(), e);
+                    responseFuture.completeExceptionally(e);
+                })
+                .doOnSuccess(v -> LOG.debug("Plugin chain completed successfully for session: {}", sessionId))
+                .doOnCancel(() -> {
+                    LOG.warn("Plugin chain execution cancelled for session: {}", sessionId);
+                    responseFuture.completeExceptionally(new RuntimeException("Execution was cancelled"));
+                })
+                .doFinally(signalType -> {
+                    // Clean up temporary sessions after execution
+                    if (isTemporarySession) {
+                        LOG.debug("Cleaning up temporary session: {} (signal: {})", sessionId, signalType);
+                        ShenyuMcpExchangeHolder.remove(sessionId);
+                    }
+                })
+                .subscribe();
+
+        // Wait for the response with timeout
+        try {
+            final String result = responseFuture.get(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            LOG.debug("Tool call completed successfully for session: {}", sessionId);
+            return result;
+        } catch (Exception e) {
+            LOG.error("Timeout or error waiting for response for session {}: {}", sessionId, e.getMessage(), e);
+
+            // Ensure cleanup on error for temporary sessions
+            if (isTemporarySession) {
+                LOG.debug("Emergency cleanup of temporary session on error: {}", sessionId);
+                ShenyuMcpExchangeHolder.remove(sessionId);
+            }
+
+            throw new RuntimeException("Tool execution timeout or error: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Builds a decorated ServerWebExchange for tool execution.
+     * <p>Creates a new exchange with modified request (method, path, headers, body),
+     * response decorator based on protocol type, and updated Shenyu context and metadata.</p>
+     *
+     * @param originExchange the original exchange
+     * @param responseFuture the future for capturing response
+     * @param sessionId      the session identifier
+     * @param configStr      the request configuration
+     * @param input          the tool input parameters
+     * @return the decorated exchange ready for execution
+     */
+    private ServerWebExchange buildDecoratedExchange(final ServerWebExchange originExchange,
+                                                     final CompletableFuture<String> responseFuture,
+                                                     final String sessionId,
+                                                     final String configStr,
+                                                     final String input) {
+
+        // Parse input and configuration
+        final JsonObject inputJson = parseInput(input);
+        final RequestConfig requestConfig = buildRequestConfig(configStr, inputJson);
+
+        // Build decorated request
+        final ServerHttpRequest decoratedRequest = buildDecoratedRequest(
+                originExchange, sessionId, requestConfig);
+
+        // Build response decorator based on protocol
+        final ServerHttpResponseDecorator responseDecorator = createResponseDecorator(
+                originExchange, sessionId, responseFuture, configStr);
+
+        // Create base decorated exchange
+        final ServerWebExchange decoratedExchange = originExchange.mutate()
+                .request(decoratedRequest)
+                .response(responseDecorator)
+                .build();
+        // Handle request body if needed
+        final ServerWebExchange finalExchange = handleRequestBody(decoratedExchange, requestConfig);
+
+        // Configure Shenyu context and metadata
+        configureShenyuContext(finalExchange, sessionId, requestConfig.getPath(), configStr);
+        return finalExchange;
+    }
+
+    /**
+     * Parses the input JSON string into a JsonObject.
+     *
+     * @param input the input JSON string
+     * @return the parsed JsonObject
+     * @throws IllegalArgumentException if input is not valid JSON
+     */
+    private JsonObject parseInput(final String input) {
+        try {
+            final JsonObject inputJson = GsonUtils.getInstance().fromJson(input, JsonObject.class);
+            if (Objects.isNull(inputJson)) {
+                throw new IllegalArgumentException("Invalid input JSON format");
+            }
+            return inputJson;
+        } catch (Exception e) {
+            LOG.error("Failed to parse input JSON: {}", e.getMessage());
+            throw new IllegalArgumentException("Invalid JSON format: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Builds request configuration from configuration string and input parameters.
+     *
+     * @param configStr the configuration string
+     * @param inputJson the input parameters
+     * @return the built request configuration
+     */
+    private RequestConfig buildRequestConfig(final String configStr, final JsonObject inputJson) {
         final RequestConfigHelper configHelper = new RequestConfigHelper(configStr);
-        // Parse input parameters
-        BuildRequestConfigResult result = getBuildRequestConfig(configHelper, input);
-        RequestConfig requestConfig = result.requestConfig();
-        
+        final JsonObject requestTemplate = configHelper.getRequestTemplate();
+        final JsonObject argsPosition = configHelper.getArgsPosition();
+        final String urlTemplate = configHelper.getUrlTemplate();
+        final String method = configHelper.getMethod();
+        final boolean argsToJsonBody = configHelper.isArgsToJsonBody();
+
+        // Build path and query parameters (no formatting needed for URLs)
+        final String path = RequestConfigHelper.buildPath(urlTemplate, argsPosition, inputJson);
+
+        // Build body with parameter formatting (only format body parameters that need it)
+        final JsonObject bodyJson = buildFormattedBodyJson(argsToJsonBody, argsPosition, inputJson);
+
+        return new RequestConfig(method, path, bodyJson, requestTemplate, argsToJsonBody);
+    }
+
+    /**
+     * Build body JSON with parameter formatting for specific types.
+     * Only formats parameters that are mapped to body and need type conversion.
+     *
+     * @param argsToJsonBody whether to convert arguments to JSON body
+     * @param argsPosition   the argument position mapping
+     * @param inputJson      the input JSON object
+     * @return the constructed body JSON object
+     */
+    private JsonObject buildFormattedBodyJson(final boolean argsToJsonBody, final JsonObject argsPosition, final JsonObject inputJson) {
+        JsonObject bodyJson = new JsonObject();
+
+        if (!argsToJsonBody) {
+            return bodyJson;
+        }
+
+        for (String key : argsPosition.keySet()) {
+            String position = argsPosition.get(key).getAsString();
+            if (position.startsWith("body") && inputJson.has(key)) {
+                JsonElement value = inputJson.get(key);
+
+                // Format the value if it's a JSON string that should be parsed
+                JsonElement formattedValue = formatBodyParameterValue(value, key);
+
+                if ("body".equals(position)) {
+                    bodyJson.add(key, formattedValue);
+                } else if (position.startsWith("body.")) {
+                    String[] pathParts = position.substring(5).split("\\.");
+                    setNestedValue(bodyJson, pathParts, formattedValue);
+                }
+            }
+        }
+
+        return bodyJson;
+    }
+
+    /**
+     * Format body parameter value. Only handles JSON string parsing for complex types.
+     *
+     * @param value the parameter value
+     * @param paramName the parameter name for error messages
+     * @return the formatted value
+     */
+    private JsonElement formatBodyParameterValue(final JsonElement value, final String paramName) {
+        // If it's a string, try to parse it as JSON (for array/object types)
+        if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
+            String stringValue = value.getAsString();
+            JsonElement parsed = ParameterFormatter.tryParseJsonString(stringValue);
+
+            // If parsing succeeded (returned different object), log it
+            if (!parsed.equals(value)) {
+                LOG.debug("Parsed JSON string parameter '{}' into {}", paramName,
+                        parsed.isJsonArray() ? "array" : "object");
+            }
+
+            return parsed;
+        }
+
+        // Return as-is for all other cases
+        return value;
+    }
+
+    /**
+     * Set nested value in JSON object.
+     */
+    private void setNestedValue(final JsonObject jsonObject, final String[] pathParts, final JsonElement value) {
+        JsonObject current = jsonObject;
+
+        for (int i = 0; i < pathParts.length - 1; i++) {
+            String part = pathParts[i];
+            if (!current.has(part)) {
+                current.add(part, new JsonObject());
+            }
+            current = current.getAsJsonObject(part);
+        }
+
+        current.add(pathParts[pathParts.length - 1], value);
+    }
+
+    /**
+     * Builds a decorated HTTP request with modified method, path, and headers.
+     *
+     * @param originExchange the original exchange
+     * @param sessionId      the session identifier
+     * @param requestConfig  the request configuration
+     * @return the decorated HTTP request
+     */
+    private ServerHttpRequest buildDecoratedRequest(final ServerWebExchange originExchange,
+                                                    final String sessionId,
+                                                    final RequestConfig requestConfig) {
+
         final ServerHttpRequest.Builder requestBuilder = originExchange
                 .getRequest()
                 .mutate()
                 .method(HttpMethod.valueOf(requestConfig.getMethod()))
                 .header("sessionId", sessionId)
                 .header("Accept", "application/json");
-        
-        // Add custom headers
+
+        // Add custom headers from template
+        addCustomHeaders(requestBuilder, requestConfig);
+
+        // Set content type for body methods
+        configureContentType(requestBuilder, requestConfig.getMethod());
+
+        // Set the target URI
+        setTargetUri(requestBuilder, originExchange, requestConfig.getPath());
+
+        return requestBuilder.build();
+    }
+
+    /**
+     * Adds custom headers from the request template to the request builder.
+     *
+     * @param requestBuilder the request builder
+     * @param requestConfig  the request configuration
+     */
+    private void addCustomHeaders(final ServerHttpRequest.Builder requestBuilder,
+                                  final RequestConfig requestConfig) {
         if (requestConfig.getRequestTemplate().has("headers")) {
             for (final var headerElem : requestConfig.getRequestTemplate().getAsJsonArray("headers")) {
                 final JsonObject headerObj = headerElem.getAsJsonObject();
-                requestBuilder.header(headerObj.get("key").getAsString(), headerObj.get("value").getAsString());
+                requestBuilder.header(
+                        headerObj.get("key").getAsString(),
+                        headerObj.get("value").getAsString()
+                );
             }
         }
-        
-        // Set Content-Type
-        if (RequestConfigHelper.isRequestBodyMethod(requestConfig.getMethod())) {
+    }
+
+    /**
+     * Configures content type based on HTTP method.
+     *
+     * @param requestBuilder the request builder
+     * @param method         the HTTP method
+     */
+    private void configureContentType(final ServerHttpRequest.Builder requestBuilder, final String method) {
+        if (isRequestBodyMethod(method)) {
             requestBuilder.header("Content-Type", "application/json");
         } else {
             requestBuilder.headers(httpHeaders -> httpHeaders.remove("Content-Type"));
         }
-        
-        // Set URI
+    }
+
+    /**
+     * Sets the target URI for the request.
+     *
+     * @param requestBuilder the request builder
+     * @param originExchange the original exchange
+     * @param path           the target path
+     */
+    private void setTargetUri(final ServerHttpRequest.Builder requestBuilder,
+                              final ServerWebExchange originExchange,
+                              final String path) {
         try {
             final URI oldUri = originExchange.getRequest().getURI();
-            final String newUriStr = oldUri.getScheme() + "://" + oldUri.getAuthority() + result.path();
+            final String newUriStr = oldUri.getScheme() + "://" + oldUri.getAuthority() + path;
             requestBuilder.uri(new URI(newUriStr));
         } catch (URISyntaxException e) {
-            throw new RuntimeException("Invalid URI: " + e.getMessage(), e);
-        }
-        
-        // Create response decorator
-        final ServerHttpResponseDecorator responseDecorator = new ShenyuMcpResponseDecorator(originExchange.getResponse(), sessionId, future, configHelper.getResponseTemplate());
-        
-        // Build final exchange object
-        final ServerWebExchange decoratedExchange = originExchange.mutate().request(requestBuilder.build()).response(responseDecorator).build();
-        
-        // Handle request body
-        if (RequestConfigHelper.isRequestBodyMethod(requestConfig.getMethod())
-                && requestConfig.getBodyJson().size() > 0) {
+            throw new RuntimeException("Invalid URI construction: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Creates appropriate response decorator based on protocol type.
+     *
+     * @param originExchange the original exchange
+     * @param sessionId      the session identifier
+     * @param responseFuture the response future
+     * @param configStr      the configuration string (for response template)
+     * @return the appropriate response decorator
+     */
+    private ServerHttpResponseDecorator createResponseDecorator(final ServerWebExchange originExchange,
+                                                                final String sessionId,
+                                                                final CompletableFuture<String> responseFuture,
+                                                                final String configStr) {
+
+        final RequestConfigHelper configHelper = new RequestConfigHelper(configStr);
+        final JsonObject responseTemplate = configHelper.getResponseTemplate();
+
+        if (isStreamableHttpProtocol(originExchange)) {
+            LOG.debug("Using non-committing decorator for Streamable HTTP protocol, session: {}", sessionId);
+            return new NonCommittingMcpResponseDecorator(
+                    originExchange.getResponse(), sessionId, responseFuture, responseTemplate);
+        } else {
+            LOG.debug("Using standard decorator for SSE protocol, session: {}", sessionId);
+            return new ShenyuMcpResponseDecorator(
+                    originExchange.getResponse(), sessionId, responseFuture, responseTemplate);
+        }
+    }
+
+    /**
+     * Handles request body for methods that support it.
+     *
+     * @param decoratedExchange the decorated exchange
+     * @param requestConfig     the request configuration
+     * @return the exchange with body handling applied
+     */
+    private ServerWebExchange handleRequestBody(final ServerWebExchange decoratedExchange,
+                                                final RequestConfig requestConfig) {
+        if (isRequestBodyMethod(requestConfig.getMethod()) && requestConfig.getBodyJson().size() > 0) {
             return new BodyWriterExchange(decoratedExchange, requestConfig.getBodyJson().toString());
         }
-        
-        // Set context and attributes
-        final ShenyuContext shenyuContext = originExchange.getAttribute(Constants.CONTEXT);
+        return decoratedExchange;
+    }
+
+    /**
+     * Configures Shenyu context and metadata for the decorated exchange.
+     *
+     * @param decoratedExchange the decorated exchange
+     * @param sessionId         the session identifier
+     * @param decoratedPath     the decorated request path
+     * @param configStr  configStr.
+     */
+    private void configureShenyuContext(final ServerWebExchange decoratedExchange,
+                                        final String sessionId,
+                                        final String decoratedPath,
+                                        final String configStr) {
+
+        final ShenyuContext shenyuContext = decoratedExchange.getAttribute(Constants.CONTEXT);
         if (Objects.nonNull(shenyuContext)) {
-            String decoratedPath = decoratedExchange.getRequest().getPath().value();
-            MetaData metaData = MetaDataCache.getInstance().obtain(decoratedPath);
+            // Set metadata if available
+            configureMetadata(decoratedExchange, decoratedPath, shenyuContext);
+
+            final RequestConfigHelper configHelper = new RequestConfigHelper(configStr);
+            MetaData metaData = MetaDataCache.getInstance().obtain(configHelper.getUrlTemplate());
             if (Objects.nonNull(metaData) && Boolean.TRUE.equals(metaData.getEnabled())) {
                 decoratedExchange.getAttributes().put(Constants.META_DATA, metaData);
                 shenyuContext.setRpcType(metaData.getRpcType());
-            } else {
-                shenyuContext.setRpcType(RpcTypeEnum.HTTP.getName());
             }
             shenyuContext.setPath(decoratedPath);
             shenyuContext.setRealUrl(decoratedPath);
+
+            LOG.debug("Configured RpcType to HTTP for tool call, session: {}", sessionId);
+
             decoratedExchange.getAttributes().put(Constants.CONTEXT, shenyuContext);
+
+            // Add MCP tool call markers to prevent loops
+            decoratedExchange.getAttributes().put(MCP_TOOL_CALL_ATTR, true);
+            decoratedExchange.getAttributes().put(MCP_SESSION_ID_ATTR, sessionId);
         }
-        
-        return decoratedExchange;
     }
-    
-    private static BuildRequestConfigResult getBuildRequestConfig(final RequestConfigHelper configHelper, final String input) {
-        final JsonObject inputJson = GsonUtils.getInstance().fromJson(input, JsonObject.class);
-        
-        if (Objects.isNull(inputJson)) {
-            throw new IllegalArgumentException("Invalid input JSON format");
+
+    /**
+     * Configures metadata for the request if available.
+     *
+     * @param decoratedExchange the decorated exchange
+     * @param decoratedPath     the request path
+     * @param shenyuContext     the Shenyu context
+     */
+    private void configureMetadata(final ServerWebExchange decoratedExchange,
+                                   final String decoratedPath,
+                                   final ShenyuContext shenyuContext) {
+
+        final MetaData metaData = MetaDataCache.getInstance().obtain(decoratedPath);
+        if (Objects.nonNull(metaData) && Boolean.TRUE.equals(metaData.getEnabled())) {
+            decoratedExchange.getAttributes().put(Constants.META_DATA, metaData);
+            // Set metadata RPC type first (will be overridden to HTTP)
+            shenyuContext.setRpcType(metaData.getRpcType());
+            LOG.debug("Applied metadata for path: {}", decoratedPath);
         }
-        // Parse request configuration
-        final JsonObject requestTemplate = configHelper.getRequestTemplate();
-        final JsonObject argsPosition = configHelper.getArgsPosition();
-        final String urlTemplate = configHelper.getUrlTemplate();
-        final String method = configHelper.getMethod();
-        final boolean argsToJsonBody = configHelper.isArgsToJsonBody();
-        
-        final String path = RequestConfigHelper.buildPath(urlTemplate, argsPosition, inputJson);
-        final JsonObject bodyJson = RequestConfigHelper.buildBodyJson(argsToJsonBody, argsPosition, inputJson);
-        
-        final RequestConfig requestConfig = new RequestConfig(method, path, bodyJson, requestTemplate, argsToJsonBody);
-        return new BuildRequestConfigResult(path, requestConfig);
     }
-    
-    private record BuildRequestConfigResult(String path, RequestConfig requestConfig) {
+
+    /**
+     * Checks if the protocol is Streamable HTTP based on URI path.
+     *
+     * @param exchange the server web exchange
+     * @return true if Streamable HTTP protocol is detected
+     */
+    private boolean isStreamableHttpProtocol(final ServerWebExchange exchange) {
+        final String uri = exchange.getRequest().getURI().getRawPath();
+        final boolean isStreamable = uri.contains(STREAMABLE_HTTP_PATH) || uri.endsWith(STREAMABLE_HTTP_PATH);
+        LOG.debug("Protocol detection - URI: {}, isStreamableHttp: {}", uri, isStreamable);
+        return isStreamable;
+    }
+
+    /**
+     * Checks if the HTTP method supports request body.
+     *
+     * @param method the HTTP method name
+     * @return true if the method supports request body
+     */
+    private boolean isRequestBodyMethod(final String method) {
+        return "POST".equalsIgnoreCase(method)
+                || "PUT".equalsIgnoreCase(method)
+                || "PATCH".equalsIgnoreCase(method);
+    }
+
+    /**
+     * Extracts the MCP sync server exchange from the tool context.
+     *
+     * @param toolContext the tool context containing MCP session information
+     * @return the MCP sync server exchange
+     * @throws IllegalStateException if exchange cannot be retrieved
+     */
+    private McpSyncServerExchange extractMcpExchange(final ToolContext toolContext) {
+        final McpSyncServerExchange exchange = McpSessionHelper.getMcpSyncServerExchange(toolContext);
+        if (Objects.isNull(exchange)) {
+            throw new IllegalStateException("Failed to retrieve MCP sync server exchange from context");
+        }
+        return exchange;
+    }
+
+    /**
+     * Extracts the session ID from the MCP sync server exchange.
+     *
+     * @param mcpExchange the MCP sync server exchange
+     * @return the session ID
+     * @throws IllegalStateException if session ID cannot be extracted
+     */
+    private String extractSessionId(final McpSyncServerExchange mcpExchange) {
+        final String sessionId;
+        try {
+            sessionId = McpSessionHelper.getSessionId(mcpExchange);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        if (StringUtils.hasText(sessionId)) {
+            LOG.debug("Extracted session ID: {}", sessionId);
+            return sessionId;
+        }
+        throw new IllegalStateException("Session ID is empty – it should have been set earlier by handleMessageEndpoint");
+    }
+
+    /**
+     * Gets the origin ServerWebExchange for the given session ID.
+     *
+     * @param sessionId the session ID
+     * @return the origin ServerWebExchange
+     * @throws IllegalStateException if exchange cannot be retrieved
+     */
+    private ServerWebExchange getOriginExchange(final String sessionId) {
+        final ServerWebExchange exchange = ShenyuMcpExchangeHolder.get(sessionId);
+        if (Objects.nonNull(exchange)) {
+            LOG.debug("Found existing exchange for session: {}", sessionId);
+            return exchange;
+        }
+        throw new IllegalStateException("No ServerWebExchange found for session '" + sessionId
+                + "'. It should have been stored by handleMessageEndpoint before the tool was invoked.");
     }
 }
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/handler/McpServerPluginDataHandler.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/handler/McpServerPluginDataHandler.java
index d04959bec8..d225f1bd5a 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/handler/McpServerPluginDataHandler.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/handler/McpServerPluginDataHandler.java
@@ -46,30 +46,32 @@ import java.util.function.Supplier;
  * The type McpServer plugin data handler.
  */
 public class McpServerPluginDataHandler implements PluginDataHandler {
-    
-    public static final Supplier<CommonHandleCache<String, ShenyuMcpServerTool>> CACHED_TOOL = new BeanHolder<>(
-            CommonHandleCache::new);
-    
+
     public static final Supplier<CommonHandleCache<String, ShenyuMcpServer>> CACHED_SERVER = new BeanHolder<>(
             CommonHandleCache::new);
-    
+
+    public static final Supplier<CommonHandleCache<String, ShenyuMcpServerTool>> CACHED_TOOL = new BeanHolder<>(
+            CommonHandleCache::new);
+
+    private static final String DEFAULT_MESSAGE_ENDPOINT = "{\"messageEndpoint\":\"/message\"}";
+
     private static final String SLASH = "/";
-    
+
     private static final String STAR = "/**";
-    
+
     private final ShenyuMcpServerManager shenyuMcpServerManager;
-    
+
     public McpServerPluginDataHandler(
             final ShenyuMcpServerManager shenyuMcpServerManager) {
         this.shenyuMcpServerManager = shenyuMcpServerManager;
     }
-    
+
     @Override
     public void handlerSelector(final SelectorData selectorData) {
         if (Objects.isNull(selectorData) || Objects.isNull(selectorData.getId())) {
             return;
         }
-        
+
         if (CollectionUtils.isEmpty(selectorData.getConditionList())) {
             return;
         }
@@ -80,10 +82,10 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
                 .map(ConditionData::getParamValue)
                 .findFirst()
                 .orElse(null);
-        
+
         String path = StringUtils.removeEnd(uri, SLASH);
         path = StringUtils.removeEnd(path, STAR);
-        ShenyuMcpServer shenyuMcpServer = GsonUtils.getInstance().fromJson(selectorData.getHandle(), ShenyuMcpServer.class);
+        ShenyuMcpServer shenyuMcpServer = GsonUtils.getInstance().fromJson(Objects.isNull(selectorData.getHandle()) ? DEFAULT_MESSAGE_ENDPOINT : selectorData.getHandle(), ShenyuMcpServer.class);
         shenyuMcpServer.setPath(path);
         CACHED_SERVER.get().cachedHandle(
                 selectorData.getId(),
@@ -93,18 +95,18 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
         if (StringUtils.isNotBlank(uri) && !shenyuMcpServerManager.hasMcpServer(uri)) {
             shenyuMcpServerManager.getOrCreateMcpServerTransport(uri, messageEndpoint);
         }
-        
+
         // the update is also need to clean, but there is no way to
         // distinguish between crate and update, so it is always clean
         MetaDataCache.getInstance().clean();
     }
-    
+
     @Override
     public void removeSelector(final SelectorData selectorData) {
         UpstreamCacheManager.getInstance().removeByKey(selectorData.getId());
         MetaDataCache.getInstance().clean();
         CACHED_TOOL.get().removeHandle(CacheKeyUtils.INST.getKey(selectorData.getId(), Constants.DEFAULT_RULE));
-        
+
         // Remove the McpServer for this URI
         // First try to get URI from handle, then from condition list
         String uri = selectorData.getHandle();
@@ -116,14 +118,14 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
                     .findFirst()
                     .orElse(null);
         }
-        
+
         CACHED_SERVER.get().removeHandle(selectorData.getId());
-        
+
         if (StringUtils.isNotBlank(uri) && shenyuMcpServerManager.hasMcpServer(uri)) {
             shenyuMcpServerManager.removeMcpServer(uri);
         }
     }
-    
+
     @Override
     public void handlerRule(final RuleData ruleData) {
         Optional.ofNullable(ruleData.getHandle()).ifPresent(s -> {
@@ -132,9 +134,9 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
             // the update is also need to clean, but there is no way to
             // distinguish between crate and update, so it is always clean
             MetaDataCache.getInstance().clean();
-            
+
             List<McpServerToolParameter> parameters = mcpServerTool.getParameters();
-            
+
             // Create JSON schema from parameters
             String inputSchema = JsonSchemaUtil.createParameterSchema(parameters);
             ShenyuMcpServer server = CACHED_SERVER.get().obtainHandle(ruleData.getSelectorId());
@@ -148,7 +150,7 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
             }
         });
     }
-    
+
     @Override
     public void removeRule(final RuleData ruleData) {
         Optional.ofNullable(ruleData.getHandle()).ifPresent(s -> {
@@ -158,10 +160,10 @@ public class McpServerPluginDataHandler implements PluginDataHandler {
         });
         MetaDataCache.getInstance().clean();
     }
-    
+
     @Override
     public String pluginNamed() {
         return PluginEnum.MCP_SERVER.getName();
     }
-    
+
 }
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/manager/ShenyuMcpServerManager.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/manager/ShenyuMcpServerManager.java
index 07d337eba1..fa2cb92143 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/manager/ShenyuMcpServerManager.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/manager/ShenyuMcpServerManager.java
@@ -23,10 +23,11 @@ import io.modelcontextprotocol.server.McpAsyncServer;
 import io.modelcontextprotocol.server.McpServer;
 import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification;
 import io.modelcontextprotocol.spec.McpSchema;
-import org.apache.commons.lang3.StringUtils;
+import io.modelcontextprotocol.spec.McpServerSession;
 import org.apache.shenyu.plugin.mcp.server.callback.ShenyuToolCallback;
 import org.apache.shenyu.plugin.mcp.server.definition.ShenyuToolDefinition;
 import org.apache.shenyu.plugin.mcp.server.transport.ShenyuSseServerTransportProvider;
+import org.apache.shenyu.plugin.mcp.server.transport.ShenyuStreamableHttpServerTransportProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.ai.mcp.McpToolUtils;
@@ -38,16 +39,30 @@ import org.springframework.web.reactive.function.server.HandlerFunction;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
 
 /**
- * Manager for MCP servers. Maps URIs to McpServer instances.
+ * Enhanced Manager for MCP servers supporting shared server instances across multiple transport protocols.
+ * 
+ * <p>This manager implements a unified architecture where SSE and Streamable HTTP protocols share
+ * the same McpAsyncServer instance per path, with shared tool sets and capabilities.
+ * Provides centralized server and tool management with protocol-specific transport layers.</p>
+ *
+ * @since 2.7.0.2
  */
 @Component
 public class ShenyuMcpServerManager {
 
     private static final Logger LOG = LoggerFactory.getLogger(ShenyuMcpServerManager.class);
 
-    private static final String SLASH = "/";
+    /**
+     * Protocol names for transport identification.
+     */
+    private static final String SSE_PROTOCOL = "SSE";
+
+    private static final String STREAMABLE_HTTP_PROTOCOL = "Streamable HTTP";
 
     /**
      * AntPathMatcher for pattern matching.
@@ -55,63 +70,222 @@ public class ShenyuMcpServerManager {
     private final AntPathMatcher pathMatcher = new AntPathMatcher();
 
     /**
-     * Map to store URI to McpServer mapping.
+     * Shared ObjectMapper instance for JSON processing.
      */
-    private final Map<String, McpAsyncServer> mcpServerMap = new ConcurrentHashMap<>();
+    private final ObjectMapper objectMapper = new ObjectMapper();
 
     /**
-     * Map to store URI to McpServer mapping.
+     * Map to store normalized path to shared McpAsyncServer mapping.
+     * Key: normalized server path, Value: shared McpAsyncServer instance
      */
-    private final Map<String, ShenyuSseServerTransportProvider> mcpServerTransportMap = new ConcurrentHashMap<>();
+    private final Map<String, McpAsyncServer> sharedServerMap = new ConcurrentHashMap<>();
 
+    /**
+     * Map to store route handlers for different endpoints.
+     */
     private final Map<String, HandlerFunction<?>> routeMap = new ConcurrentHashMap<>();
 
     /**
-     * Get or create a McpServer instance for the given URI.
-     * Uses pattern matching to find the best matching server.
+     * Map to store composite transport providers for shared servers.
+     */
+    private final Map<String, CompositeTransportProvider> compositeTransportMap = new ConcurrentHashMap<>();
+
+    /**
+     * Get or create a shared MCP server for the given path, supporting multiple transport protocols.
+     *
+     * @param uri The URI to create or get a server for
+     * @param messageEndPoint The message endpoint path
+     * @return The SSE transport provider for the URI
+     */
+    public ShenyuSseServerTransportProvider getOrCreateMcpServerTransport(final String uri, final String messageEndPoint) {
+        String normalizedPath = processPath(uri);
+        return getOrCreateTransport(normalizedPath, SSE_PROTOCOL, 
+            () -> createSseTransport(normalizedPath, messageEndPoint));
+    }
+
+    /**
+     * Get or create a shared MCP server for Streamable HTTP transport.
+     *
+     * @param uri The URI to create or get a transport provider for
+     * @return The Streamable HTTP transport provider for the URI
+     */
+    public ShenyuStreamableHttpServerTransportProvider getOrCreateStreamableHttpTransport(final String uri) {
+        String normalizedPath = processPath(uri);
+        return getOrCreateTransport(normalizedPath, STREAMABLE_HTTP_PROTOCOL, 
+            () -> createStreamableHttpTransport(normalizedPath, uri));
+    }
+
+    /**
+     * Generic method to get or create transport providers.
      *
-     * @param uri The URI to create or get a McpServer for
-     * @param messageEndpoint The message endpoint to use
-     * @return The McpServer for the URI
+     * @param normalizedPath the normalized path
+     * @param protocol the protocol name
+     * @param transportFactory the factory function to create transport if not exists
+     * @param <T> the transport type
+     * @return the transport provider
      */
-    public ShenyuSseServerTransportProvider getOrCreateMcpServerTransport(final String uri, final String messageEndpoint) {
-        // First try exact match
-        ShenyuSseServerTransportProvider transport = mcpServerTransportMap.get(uri);
-        if (Objects.nonNull(transport)) {
-            return transport;
+    @SuppressWarnings("unchecked")
+    private <T> T getOrCreateTransport(final String normalizedPath, final String protocol, 
+                                      final java.util.function.Supplier<T> transportFactory) {
+        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)) {
+            transport = transportFactory.get();
+            addTransportToSharedServer(normalizedPath, protocol, transport);
         }
+        
+        return transport;
+    }
 
-        // Then try to find existing transport that matches the pattern
-        String basePath = extractBasePath(uri, messageEndpoint);
-        transport = mcpServerTransportMap.get(basePath);
-        if (Objects.nonNull(transport)) {
-            LOG.debug("Found existing transport for base path '{}' for URI '{}'", basePath, uri);
-            return transport;
+    /**
+     * Process URI to get normalized path.
+     *
+     * @param uri the URI to process
+     * @return normalized path
+     */
+    private String processPath(final String uri) {
+        return normalizeServerPath(extractBasePath(uri));
+    }
+
+    /**
+     * Gets or creates a composite transport provider for the given normalized path.
+     *
+     * @param normalizedPath the normalized server path
+     * @return the composite transport provider
+     */
+    private CompositeTransportProvider getOrCreateCompositeTransport(final String normalizedPath) {
+        return compositeTransportMap.computeIfAbsent(normalizedPath, path -> {
+            LOG.debug("Creating composite transport provider for path: {}", path);
+            return new CompositeTransportProvider();
+        });
+    }
+
+    /**
+     * Adds a transport provider to the shared server infrastructure.
+     *
+     * @param normalizedPath    the normalized server path
+     * @param protocol          the protocol name
+     * @param transportProvider the transport provider instance
+     */
+    private void addTransportToSharedServer(final String normalizedPath, final String protocol, final Object transportProvider) {
+        // Get or create shared server
+        getOrCreateSharedServer(normalizedPath);
+
+        // Add transport to composite provider
+        CompositeTransportProvider compositeTransport = compositeTransportMap.get(normalizedPath);
+        if (Objects.nonNull(compositeTransport)) {
+            compositeTransport.addTransport(protocol, transportProvider);
         }
 
-        // Create new transport for the base path
-        return mcpServerTransportMap.putIfAbsent(basePath, createMcpServerTransport(uri, messageEndpoint));
+        LOG.info("Added {} transport to shared server at path: {}", protocol, normalizedPath);
     }
 
     /**
-     * Extract the base path from a URI by removing the /message suffix and any
-     * sub-paths.
+     * Gets or creates a shared McpAsyncServer instance for the given normalized path.
+     *
+     * @param normalizedPath the normalized server path
+     * @return the shared McpAsyncServer instance
+     */
+    private McpAsyncServer getOrCreateSharedServer(final String normalizedPath) {
+        return sharedServerMap.computeIfAbsent(normalizedPath, path -> {
+            LOG.info("Creating shared MCP server for path: {}", path);
+
+            // Get or create composite transport provider
+            CompositeTransportProvider compositeTransport = getOrCreateCompositeTransport(path);
+
+            // Configure server capabilities
+            var capabilities = McpSchema.ServerCapabilities.builder()
+                    .tools(true)
+                    .logging()
+                    .build();
+
+            // Create shared server with composite transport
+            McpAsyncServer server = McpServer
+                    .async(compositeTransport)
+                    .serverInfo("MCP Shenyu Server (Multi-Protocol)", "1.0.0")
+                    .capabilities(capabilities)
+                    .tools(Lists.newArrayList())
+                    .build();
+
+            LOG.info("Created shared MCP server for path: {} with multi-protocol support", path);
+            return server;
+        });
+    }
+
+    /**
+     * Creates SSE transport provider.
+     */
+    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 routes
+        registerRoutes(normalizedPath, messageEndpoint, transportProvider::handleSseConnection, transportProvider::handleMessage);
+
+        LOG.debug("Created SSE transport for path: {}", normalizedPath);
+        return transportProvider;
+    }
+
+    /**
+     * Creates Streamable HTTP transport provider.
+     */
+    private ShenyuStreamableHttpServerTransportProvider createStreamableHttpTransport(final String normalizedPath, final String originalUri) {
+        ShenyuStreamableHttpServerTransportProvider transportProvider = ShenyuStreamableHttpServerTransportProvider.builder()
+                .objectMapper(objectMapper)
+                .endpoint(originalUri)
+                .build();
+
+        // Register routes for original URI
+        registerRoutes(originalUri, null, transportProvider::handleUnifiedEndpoint, null);
+
+        LOG.debug("Created Streamable HTTP transport for original URI: {} (normalized: {})", originalUri, normalizedPath);
+        return transportProvider;
+    }
+
+    /**
+     * Register routes for transport providers.
+     *
+     * @param primaryPath the primary path
+     * @param secondaryPath the secondary path (can be null)
+     * @param primaryHandler the primary handler
+     * @param secondaryHandler the secondary handler (can be null)
+     */
+    private void registerRoutes(final String primaryPath, final String secondaryPath, 
+                               final HandlerFunction<?> primaryHandler, final HandlerFunction<?> secondaryHandler) {
+        routeMap.put(primaryPath, primaryHandler);
+        routeMap.put(primaryPath + "/**", primaryHandler);
+        
+        if (Objects.nonNull(secondaryPath) && Objects.nonNull(secondaryHandler)) {
+            routeMap.put(secondaryPath, secondaryHandler);
+            routeMap.put(secondaryPath + "/**", secondaryHandler);
+        }
+    }
+
+    /**
+     * Extract the base path from a URI by removing the /message suffix and any sub-paths.
      *
      * @param uri The URI to extract base path from
-     * @param messageEndpoint Endpoint for the MCP Server
      * @return The base path
      */
-    private String extractBasePath(final String uri, final String messageEndpoint) {
+    private String extractBasePath(final String uri) {
         String basePath = uri;
 
         // Remove /message suffix if present
-        if (basePath.endsWith(messageEndpoint)) {
-            basePath = basePath.substring(0, basePath.length() - messageEndpoint.length());
+        if (basePath.endsWith("/message")) {
+            basePath = basePath.substring(0, basePath.length() - "/message".length());
         }
 
         // For sub-paths, extract the main MCP server path
-        // This assumes MCP server paths don't contain multiple levels
-        // You may need to adjust this logic based on your specific URL structure
         String[] pathSegments = basePath.split("/");
         if (pathSegments.length > 2) {
             // Keep only the first two segments (empty + server-name)
@@ -128,12 +302,12 @@ public class ShenyuMcpServerManager {
      * @return true if a McpServer exists, false otherwise
      */
     public boolean hasMcpServer(final String uri) {
-        return mcpServerMap.containsKey(uri);
+        String normalizedPath = processPath(uri);
+        return sharedServerMap.containsKey(normalizedPath);
     }
 
     /**
      * Check if a McpServer can route requests for the given URI.
-     * Uses AntPathMatcher to support pattern matching.
      *
      * @param uri The URI to check
      * @return true if the URI is routable, false otherwise
@@ -155,97 +329,301 @@ public class ShenyuMcpServerManager {
         return false;
     }
 
-
     /**
-     * Remove a McpServer for the given URI.
+     * Remove a MCP server and all its associated transports.
      *
-     * @param uri The URI to remove the McpServer for
+     * @param uri The URI to remove the server for
      */
     public void removeMcpServer(final String uri) {
-        LOG.info("Removed McpServer for URI: {}", uri);
-        ShenyuSseServerTransportProvider transportProvider = mcpServerTransportMap.remove(uri);
-        if (Objects.nonNull(transportProvider)) {
-            transportProvider
-                    .closeGracefully()
-                    .doOnSuccess(aVoid -> LOG.info("Successfully closed McpServer for URI: {}", uri))
-                    .doOnError(e -> LOG.error("Error closing McpServer for URI: {}", uri, e))
+        String normalizedPath = processPath(uri);
+        LOG.info("Removing MCP server for URI: {} (normalized: {})", uri, normalizedPath);
+
+        // Close composite transport gracefully
+        CompositeTransportProvider compositeTransport = compositeTransportMap.remove(normalizedPath);
+        if (Objects.nonNull(compositeTransport)) {
+            compositeTransport.closeGracefully()
+                    .doOnSuccess(aVoid -> LOG.info("Successfully closed composite transport for path: {}", normalizedPath))
+                    .doOnError(e -> LOG.error("Error closing composite transport for path: {}", normalizedPath, e))
                     .subscribe();
         }
-        mcpServerMap.remove(uri);
+
+        // Remove server
+        sharedServerMap.remove(normalizedPath);
+
+        // Clean up routes
+        routeMap.entrySet().removeIf(entry -> entry.getKey().startsWith(Objects.requireNonNull(normalizedPath)) || entry.getKey().startsWith(uri));
+
+        LOG.info("Removed MCP server for path: {}", normalizedPath);
     }
 
     /**
-     * Create a new McpServer for the given URI.
+     * Adds a tool to the shared server instance, automatically available across all protocols.
      *
-     * @param uri The URI to create a McpServer for
-     * @param messageEndpoint The message endpoint for the McpServer
-     * @return The McpAsyncServer for the new McpServer
+     * @param serverPath      the server path
+     * @param name            the tool name
+     * @param description     the tool description
+     * @param requestTemplate the request template
+     * @param inputSchema     the input schema
      */
-    private ShenyuSseServerTransportProvider createMcpServerTransport(final String uri, final String messageEndpoint) {
-        String path = StringUtils.removeEnd(uri, SLASH);
-        LOG.info("Creating new McpServer for URI: {}", path);
-        String messagePath = path + messageEndpoint;
-        ShenyuSseServerTransportProvider transportProvider = ShenyuSseServerTransportProvider.builder()
-                .objectMapper(new ObjectMapper()).sseEndpoint(path).messageEndpoint(messagePath).build();
-
-        LOG.debug("Registered RouterFunction for URI: {}", path);
-
-        // Configure server capabilities with resource support
-        var capabilities = McpSchema.ServerCapabilities.builder()
-                // Tool support with list changes notifications
-                .tools(true)
-                // Logging support
-                .logging().build();
-
-        // Create the server with both tool and resource capabilities
-        // Note: McpServer.async() automatically calls
-        // transportProvider.setSessionFactory()
-        McpAsyncServer server = McpServer
-                .async(transportProvider)
-                .serverInfo("MCP Shenyu Server", "1.0.0")
-                .capabilities(capabilities)
-                .tools(Lists.newArrayList())
+    public void addTool(final String serverPath, final String name, final String description,
+                        final String requestTemplate, final String inputSchema) {
+        String normalizedPath = normalizeServerPath(serverPath);
+
+        // Remove existing tool first
+        try {
+            removeTool(serverPath, name);
+        } catch (Exception ignored) {
+            // ignore
+        }
+
+        ToolDefinition shenyuToolDefinition = ShenyuToolDefinition.builder()
+                .name(name)
+                .description(description)
+                .requestConfig(requestTemplate)
+                .inputSchema(inputSchema)
                 .build();
 
-        // Verify that the session factory was properly set
-        LOG.debug("MCP server created successfully for URI: {}, session factory should be available", path);
+        LOG.debug("Adding tool to shared server - name: {}, description: {}, path: {}", name, description, normalizedPath);
 
-        mcpServerMap.put(path, server);
+        ShenyuToolCallback shenyuToolCallback = new ShenyuToolCallback(shenyuToolDefinition);
 
-        // Register both exact paths and wildcard patterns for flexible matching
-        routeMap.put(path, transportProvider::handleSseConnection);
-        routeMap.put(messagePath, transportProvider::handleMessage);
+        // Add tool to shared server (automatically available across all protocols)
+        McpAsyncServer sharedServer = sharedServerMap.get(normalizedPath);
+        if (Objects.nonNull(sharedServer)) {
+            try {
+                for (AsyncToolSpecification asyncToolSpecification : McpToolUtils.toAsyncToolSpecifications(shenyuToolCallback)) {
+                    sharedServer.addTool(asyncToolSpecification).block();
+                }
+
+                Set<String> protocols = getSupportedProtocols(normalizedPath);
+                LOG.info("Added tool '{}' to shared server for path: {} (available across protocols: {})",
+                        name, normalizedPath, protocols);
+            } catch (Exception e) {
+                LOG.error("Failed to add tool '{}' to shared server for path: {}", name, normalizedPath, e);
+            }
+        } else {
+            LOG.warn("No shared server found for path: {}", normalizedPath);
+        }
+    }
 
-        // Add wildcard patterns to support sub-paths
-        routeMap.put(path + "/**", transportProvider::handleSseConnection);
-        routeMap.put(messagePath + "/**", transportProvider::handleMessage);
+    /**
+     * Removes a tool from the shared server instance.
+     *
+     * @param serverPath the server path
+     * @param name       the tool name
+     */
+    public void removeTool(final String serverPath, final String name) {
+        String normalizedPath = normalizeServerPath(serverPath);
+        LOG.debug("Removing tool from shared server - name: {}, path: {}", name, normalizedPath);
+
+        McpAsyncServer sharedServer = sharedServerMap.get(normalizedPath);
+        if (Objects.nonNull(sharedServer)) {
+            try {
+                sharedServer.removeTool(name).block();
+
+                Set<String> protocols = getSupportedProtocols(normalizedPath);
+                LOG.info("Removed tool '{}' from shared server for path: {} (removed from protocols: {})",
+                        name, normalizedPath, protocols);
+            } catch (Exception e) {
+                LOG.error("Failed to remove tool '{}' from shared server for path: {}", name, normalizedPath, e);
+            }
+        } else {
+            LOG.warn("No shared server found for path: {}", normalizedPath);
+        }
+    }
 
-        LOG.info("Created new McpServer for URI: {} with patterns", path);
-        return transportProvider;
+    /**
+     * Get supported protocols for a server path.
+     *
+     * @param serverPath The server path
+     * @return Set of supported protocols
+     */
+    public Set<String> getSupportedProtocols(final String serverPath) {
+        String normalizedPath = normalizeServerPath(serverPath);
+        CompositeTransportProvider compositeTransport = compositeTransportMap.get(normalizedPath);
+        return Objects.nonNull(compositeTransport) ? compositeTransport.getSupportedProtocols() : new HashSet<>();
     }
 
-    public void addTool(final String serverPath, final String name, final String description,
-            final String requestTemplate, final String inputSchema) {
-        // remove first for overwrite
-        try {
-            removeTool(serverPath, name);
-        } catch (Exception ignored) {
-            // ignore
+    /**
+     * Normalize the server path by removing protocol-specific suffixes.
+     *
+     * @param path The original path
+     * @return The normalized path for shared server usage
+     */
+    private String normalizeServerPath(final String path) {
+        if (Objects.isNull(path)) {
+            return null;
         }
 
-        ToolDefinition shenyuToolDefinition = ShenyuToolDefinition.builder().name(name).description(description)
-                .requestConfig(requestTemplate).inputSchema(inputSchema).build();
-        LOG.debug("Adding tool, name: {}, description: {}, requestTemplate: {}, inputSchema: {}", name, description,
-                requestTemplate, inputSchema);
-        ShenyuToolCallback shenyuToolCallback = new ShenyuToolCallback(shenyuToolDefinition);
-        for (AsyncToolSpecification asyncToolSpecification : McpToolUtils
-                .toAsyncToolSpecifications(shenyuToolCallback)) {
-            mcpServerMap.get(serverPath).addTool(asyncToolSpecification).block();
+        String normalizedPath = path;
+
+        // Remove /streamablehttp suffix
+        if (normalizedPath.endsWith("/streamablehttp")) {
+            normalizedPath = normalizedPath.substring(0, normalizedPath.length() - "/streamablehttp".length());
+            LOG.debug("Normalized Streamable HTTP path from '{}' to '{}' for shared server", path, normalizedPath);
         }
+
+        return normalizedPath;
     }
 
-    public void removeTool(final String serverPath, final String name) {
-        LOG.debug("Removing tool, name: {}", name);
-        mcpServerMap.get(serverPath).removeTool(name).block();
+    /**
+     * Composite transport provider that delegates to multiple transport implementations.
+     * Enhanced with protocol-aware session management and improved error handling.
+     * Provides unified access to different transport protocols.
+     */
+    private static class CompositeTransportProvider implements io.modelcontextprotocol.spec.McpServerTransportProvider {
+
+        private final Map<String, Object> transports = new ConcurrentHashMap<>();
+
+        private volatile McpServerSession.Factory sessionFactory;
+
+        // Track active sessions per protocol for isolation
+        private final Map<String, Set<String>> protocolSessions = new ConcurrentHashMap<>();
+
+        public void addTransport(final String protocol, final Object transportProvider) {
+            transports.put(protocol, transportProvider);
+            protocolSessions.put(protocol, Collections.synchronizedSet(new HashSet<>()));
+
+            // Set session factory on the new transport if available
+            if (Objects.nonNull(sessionFactory) && transportProvider instanceof io.modelcontextprotocol.spec.McpServerTransportProvider) {
+                ((io.modelcontextprotocol.spec.McpServerTransportProvider) transportProvider).setSessionFactory(sessionFactory);
+            }
+
+            LOG.debug("Added transport '{}' to composite provider", protocol);
+        }
+
+        /**
+         * Gets the SSE transport provider.
+         *
+         * @return the SSE transport provider, or null if not available
+         */
+        public ShenyuSseServerTransportProvider getSseTransport() {
+            Object transport = transports.get(SSE_PROTOCOL);
+            return transport instanceof ShenyuSseServerTransportProvider 
+                ? (ShenyuSseServerTransportProvider) transport : null;
+        }
+
+        /**
+         * Gets the Streamable HTTP transport provider.
+         *
+         * @return the Streamable HTTP transport provider, or null if not available
+         */
+        public ShenyuStreamableHttpServerTransportProvider getStreamableHttpTransport() {
+            Object transport = transports.get(STREAMABLE_HTTP_PROTOCOL);
+            return transport instanceof ShenyuStreamableHttpServerTransportProvider 
+                ? (ShenyuStreamableHttpServerTransportProvider) transport : null;
+        }
+
+        /**
+         * Gets a transport provider by protocol name.
+         *
+         * @param protocol the protocol name
+         * @return the transport provider, or null if not found
+         */
+        public Object getTransport(final String protocol) {
+            return transports.get(protocol);
+        }
+
+        /**
+         * Checks if a specific protocol is supported.
+         *
+         * @param protocol the protocol name
+         * @return true if the protocol is supported, false otherwise
+         */
+        public boolean hasProtocol(final String protocol) {
+            return transports.containsKey(protocol);
+        }
+
+        /**
+         * Gets all supported protocols.
+         *
+         * @return a set of supported protocol names
+         */
+        public Set<String> getSupportedProtocols() {
+            return new HashSet<>(transports.keySet());
+        }
+
+        @Override
+        public void setSessionFactory(final McpServerSession.Factory sessionFactory) {
+            this.sessionFactory = sessionFactory;
+            // Set session factory on all existing transports atomically
+            synchronized (transports) {
+                for (Object transport : transports.values()) {
+                    if (transport instanceof io.modelcontextprotocol.spec.McpServerTransportProvider) {
+                        try {
+                            ((io.modelcontextprotocol.spec.McpServerTransportProvider) transport).setSessionFactory(sessionFactory);
+                        } catch (Exception e) {
+                            LOG.error("Failed to set session factory on transport: {}", transport.getClass().getSimpleName(), e);
+                        }
+                    }
+                }
+            }
+            LOG.debug("Session factory set on composite transport with {} transports", transports.size());
+        }
+
+        @Override
+        public reactor.core.publisher.Mono<Void> notifyClients(final String method, final Object params) {
+            if (transports.isEmpty()) {
+                LOG.debug("No transports available for client notification");
+                return reactor.core.publisher.Mono.empty();
+            }
+
+            LOG.debug("Broadcasting notification '{}' to {} transports", method, transports.size());
+
+            return reactor.core.publisher.Flux.fromIterable(transports.entrySet())
+                    .flatMap(entry -> {
+                        String protocol = entry.getKey();
+                        Object transport = entry.getValue();
+
+                        if (transport instanceof io.modelcontextprotocol.spec.McpServerTransportProvider) {
+                            return ((io.modelcontextprotocol.spec.McpServerTransportProvider) transport)
+                                    .notifyClients(method, params)
+                                    .doOnSuccess(aVoid -> LOG.debug("Successfully notified {} clients", protocol))
+                                    .doOnError(e -> LOG.warn("Failed to notify {} clients: {}", protocol, e.getMessage()))
+                                    // Continue with other transports even if one fails
+                                    .onErrorComplete();
+                        } else {
+                            LOG.warn("Transport '{}' does not implement McpServerTransportProvider", protocol);
+                            return reactor.core.publisher.Mono.empty();
+                        }
+                    })
+                    .then()
+                    .doOnSuccess(aVoid -> LOG.debug("Client notification broadcast completed"));
+        }
+
+        @Override
+        public reactor.core.publisher.Mono<Void> closeGracefully() {
+            if (transports.isEmpty()) {
+                return reactor.core.publisher.Mono.empty();
+            }
+
+            LOG.info("Initiating graceful shutdown of {} transports", transports.size());
+
+            return reactor.core.publisher.Flux.fromIterable(transports.entrySet())
+                    .flatMap(entry -> {
+                        String protocol = entry.getKey();
+                        Object transport = entry.getValue();
+
+                        if (transport instanceof io.modelcontextprotocol.spec.McpServerTransportProvider) {
+                            return ((io.modelcontextprotocol.spec.McpServerTransportProvider) transport)
+                                    .closeGracefully()
+                                    .doOnSuccess(aVoid -> LOG.info("Successfully closed {} transport", protocol))
+                                    .doOnError(e -> LOG.error("Error closing {} transport: {}", protocol, e.getMessage()))
+                                    // Don't fail entire shutdown if one transport fails
+                                    .onErrorComplete();
+                        } else {
+                            LOG.warn("Transport '{}' does not implement graceful shutdown", protocol);
+                            return reactor.core.publisher.Mono.empty();
+                        }
+                    })
+                    .then()
+                    .doOnSuccess(aVoid -> {
+                        // Clear all tracking data after successful shutdown
+                        transports.clear();
+                        protocolSessions.clear();
+                        LOG.info("Graceful shutdown completed - all transports and sessions cleared");
+                    });
+        }
     }
+
 }
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/BodyWriterExchange.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/BodyWriterExchange.java
index 85f850620d..c4b4e43f46 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/BodyWriterExchange.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/BodyWriterExchange.java
@@ -17,74 +17,98 @@
 
 package org.apache.shenyu.plugin.mcp.server.request;
 
+import org.apache.shenyu.plugin.base.support.BodyInserterContext;
+import org.apache.shenyu.plugin.base.support.CachedBodyOutputMessage;
+import org.apache.shenyu.plugin.base.utils.ResponseUtils;
 import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.core.io.buffer.DataBufferFactory;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.ReactiveHttpOutputMessage;
 import org.springframework.http.server.reactive.ServerHttpRequest;
 import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.ServerWebExchangeDecorator;
 import reactor.core.publisher.Flux;
 
-import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
-import java.util.Map;
 import java.util.Objects;
-import java.util.stream.Collectors;
 
+/**
+ * Body Writer Exchange for MCP tool requests.
+ * 
+ * <p>Decorates ServerWebExchange to properly handle request body writing for different content types.
+ * Supports JSON, form-urlencoded, and multipart form data formats.</p>
+ *
+ * @since 2.7.0.2
+ */
 public class BodyWriterExchange extends ServerWebExchangeDecorator {
 
     private final String body;
 
     private final String contentType;
 
+    private final CachedBodyOutputMessage cachedBodyOutputMessage;
+
     public BodyWriterExchange(final ServerWebExchange delegate, final String body) {
         super(delegate);
         this.body = body;
         HttpHeaders headers = delegate.getRequest().getHeaders();
         this.contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);
+
+        this.cachedBodyOutputMessage = ResponseUtils.newCachedBodyOutputMessage(delegate);
+
+        initializeBody();
     }
 
-    @Override
-    public ServerHttpRequest getRequest() {
-        return new ServerHttpRequestDecorator(super.getRequest()) {
-            @Override
-            public Flux<DataBuffer> getBody() {
-                DataBufferFactory bufferFactory = BodyWriterExchange.this.getResponse().bufferFactory();
-                byte[] bytes = getBodyBytes();
-                DataBuffer buffer = bufferFactory.wrap(bytes);
-                return Flux.just(buffer);
-            }
-        };
+    private void initializeBody() {
+        String bodyContent = getFormattedBody();
+        BodyInserter<String, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromValue(bodyContent);
+
+        bodyInserter.insert(cachedBodyOutputMessage, new BodyInserterContext()).subscribe();
     }
 
-    private byte[] getBodyBytes() {
+    private String getFormattedBody() {
         if (Objects.isNull(contentType)) {
-            return body.getBytes(StandardCharsets.UTF_8);
+            return body;
         }
         if (contentType.contains("application/json")) {
-            return body.getBytes(StandardCharsets.UTF_8);
+            return body;
         } else if (contentType.contains("application/x-www-form-urlencoded")) {
-            // body must be in key1=val1&key2=val2 format
-            return body.getBytes(StandardCharsets.UTF_8);
+            return body;
         } else if (contentType.contains("multipart/form-data")) {
-            // Simple handling here, just return the string. For complex scenarios, use
-            // MultipartBodyBuilder.
-            return body.getBytes(StandardCharsets.UTF_8);
+            return body;
         } else {
-            return body.getBytes(StandardCharsets.UTF_8);
+            return body;
         }
     }
 
-    // Optional: Helper method to convert Map<String, Object> to
-    // x-www-form-urlencoded string
-    public static String toFormUrlEncoded(final Map<String, Object> map) {
-        return map.entrySet().stream()
-                .map(e -> e.getKey() + "=" + urlEncode(e.getValue().toString()))
-                .collect(Collectors.joining("&"));
-    }
+    /**
+     * Returns a decorated ServerHttpRequest with the correct body and headers for downstream processing.
+     *
+     * @return the decorated ServerHttpRequest
+     */
+    @Override
+    public ServerHttpRequest getRequest() {
+        return new ServerHttpRequestDecorator(super.getRequest()) {
+            @Override
+            public Flux<DataBuffer> getBody() {
+                return cachedBodyOutputMessage.getBody();
+            }
+
+            @Override
+            public HttpHeaders getHeaders() {
+                HttpHeaders originalHeaders = super.getHeaders();
+                HttpHeaders newHeaders = new HttpHeaders();
+                newHeaders.putAll(originalHeaders);
 
-    private static String urlEncode(final String value) {
-        return URLEncoder.encode(value, StandardCharsets.UTF_8);
+                if (Objects.nonNull(body)) {
+                    byte[] bodyBytes = getFormattedBody().getBytes(StandardCharsets.UTF_8);
+                    newHeaders.setContentLength(bodyBytes.length);
+                }
+
+                return newHeaders;
+            }
+        };
     }
 }
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/ParameterFormatter.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/ParameterFormatter.java
new file mode 100644
index 0000000000..41dacf82c4
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/ParameterFormatter.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mcp.server.request;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.util.Objects;
+
+/**
+ * Simple parameter formatting utilities.
+ */
+public class ParameterFormatter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ParameterFormatter.class);
+
+    /**
+     * Try to parse a JSON string into JsonElement.
+     * If parsing fails, returns the original string as JsonPrimitive.
+     *
+     * @param jsonString the JSON string to parse
+     * @return the parsed JsonElement or original string
+     */
+    public static JsonElement tryParseJsonString(final String jsonString) {
+        if (!StringUtils.hasText(jsonString)) {
+            return new JsonPrimitive(Objects.isNull(jsonString) ? "" : jsonString);
+        }
+
+        String trimmed = jsonString.trim();
+        if (trimmed.startsWith("[") && trimmed.endsWith("]")
+                || trimmed.startsWith("{") && trimmed.endsWith("}")) {
+            try {
+                return GsonUtils.getInstance().fromJson(jsonString, JsonElement.class);
+            } catch (Exception e) {
+                LOG.debug("Failed to parse JSON string, keeping as string: {}", e.getMessage());
+            }
+        }
+
+        return new JsonPrimitive(jsonString);
+    }
+} 
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
index 5e6e2fe92c..4f076e2d51 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/request/RequestConfigHelper.java
@@ -24,15 +24,9 @@ import org.apache.shenyu.common.utils.GsonUtils;
  * Helper class for parsing and handling requestConfig.
  */
 public class RequestConfigHelper {
-    
-    public static final String METHOD_POST = "POST";
-    
-    public static final String METHOD_PUT = "PUT";
-    
-    public static final String METHOD_PATCH = "PATCH";
-    
+
     private final JsonObject configJson;
-    
+
     /**
      * Constructor.
      *
@@ -41,16 +35,7 @@ public class RequestConfigHelper {
     public RequestConfigHelper(final String requestConfig) {
         this.configJson = GsonUtils.getInstance().fromJson(requestConfig, JsonObject.class);
     }
-    
-    /**
-     * Get the config json object.
-     *
-     * @return the config json object
-     */
-    public JsonObject getConfigJson() {
-        return configJson;
-    }
-    
+
     /**
      * Get the request template json object.
      *
@@ -59,7 +44,7 @@ public class RequestConfigHelper {
     public JsonObject getRequestTemplate() {
         return configJson.getAsJsonObject("requestTemplate");
     }
-    
+
     /**
      * Get the argument position mapping.
      *
@@ -68,7 +53,7 @@ public class RequestConfigHelper {
     public JsonObject getArgsPosition() {
         return configJson.has("argsPosition") ? configJson.getAsJsonObject("argsPosition") : new JsonObject();
     }
-    
+
     /**
      * Get the response template json object.
      *
@@ -77,7 +62,7 @@ public class RequestConfigHelper {
     public JsonObject getResponseTemplate() {
         return configJson.has("responseTemplate") ? configJson.getAsJsonObject("responseTemplate") : null;
     }
-    
+
     /**
      * Get the url template string.
      *
@@ -86,7 +71,7 @@ public class RequestConfigHelper {
     public String getUrlTemplate() {
         return getRequestTemplate().get("url").getAsString();
     }
-    
+
     /**
      * Get the HTTP method string.
      *
@@ -96,17 +81,12 @@ public class RequestConfigHelper {
         JsonObject requestTemplate = getRequestTemplate();
         return requestTemplate.has("method") ? requestTemplate.get("method").getAsString() : "GET";
     }
-    
+
     public boolean isArgsToJsonBody() {
         JsonObject requestTemplate = getRequestTemplate();
         return requestTemplate.has("argsToJsonBody") && requestTemplate.get("argsToJsonBody").getAsBoolean();
     }
-    
-    public boolean isArgsToUrlParam() {
-        JsonObject requestTemplate = getRequestTemplate();
-        return requestTemplate.has("argsToUrlParam") && requestTemplate.get("argsToUrlParam").getAsBoolean();
-    }
-    
+
     /**
      * Build the request path based on the URL template and argument positions.
      *
@@ -118,37 +98,98 @@ public class RequestConfigHelper {
     public static String buildPath(final String urlTemplate, final JsonObject argsPosition,
                                    final JsonObject inputJson) {
         // First, check if the input value is already a complete URL
-        for (String key : argsPosition.keySet()) {
-            if (inputJson.has(key)) {
-                String value = inputJson.get(key).getAsString();
-                // If the input value is already a complete URL, return it directly
-                if (value.startsWith("http://") || value.startsWith("https://")) {
-                    return value;
-                }
-            }
+        if (isCompleteUrl(argsPosition, inputJson)) {
+            return getCompleteUrl(argsPosition, inputJson);
         }
-        
+
         StringBuilder queryBuilder = new StringBuilder();
-        
-        // Check if the URL template already contains query parameters
+
+        // Check whether the URL template already contains the query parameters
         boolean hasExistingQuery = urlTemplate.contains("?");
         String basePath = hasExistingQuery ? urlTemplate.substring(0, urlTemplate.indexOf("?")) : urlTemplate;
         String existingQuery = hasExistingQuery ? urlTemplate.substring(urlTemplate.indexOf("?") + 1) : "";
-        
-        // handle new query parameters
+
+        // Handle new query parameters
+        basePath = processArguments(argsPosition, inputJson, basePath, queryBuilder);
+
+        // Clear the template variables that have not been replaced
+        basePath = basePath.replaceAll("\\{\\{\\.[^}]+}}", "");
+
+        // Build the final URL
+        return buildFinalPath(basePath, queryBuilder, hasExistingQuery, existingQuery);
+    }
+
+    /**
+     * Check if input contains a complete URL.
+     *
+     * @param argsPosition the argument position mapping
+     * @param inputJson the input JSON object
+     * @return true if input contains a complete URL
+     */
+    private static boolean isCompleteUrl(final JsonObject argsPosition, final JsonObject inputJson) {
+        for (String key : argsPosition.keySet()) {
+            if (inputJson.has(key)) {
+                try {
+                    String value = inputJson.get(key).getAsString();
+                    if (value.startsWith("http://") || value.startsWith("https://") || value.contains("?")) {
+                        return true;
+                    }
+                } catch (Exception exception) {
+                    // Ignore exception
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the complete URL from input.
+     *
+     * @param argsPosition the argument position mapping
+     * @param inputJson the input JSON object
+     * @return the complete URL
+     */
+    private static String getCompleteUrl(final JsonObject argsPosition, final JsonObject inputJson) {
+        for (String key : argsPosition.keySet()) {
+            if (inputJson.has(key)) {
+                try {
+                    String value = inputJson.get(key).getAsString();
+                    if (value.startsWith("http://") || value.startsWith("https://") || value.contains("?")) {
+                        return value;
+                    }
+                } catch (Exception exception) {
+                    // Ignore exception
+                }
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Process arguments for path and query parameters.
+     *
+     * @param argsPosition the argument position mapping
+     * @param inputJson the input JSON object
+     * @param basePath the base path to modify
+     * @param queryBuilder the query builder to append to
+     * @return the modified base path
+     */
+    private static String processArguments(final JsonObject argsPosition, final JsonObject inputJson,
+                                       final String basePath, final StringBuilder queryBuilder) {
+        String modifiedBasePath = basePath;
         for (String key : argsPosition.keySet()) {
             String position = argsPosition.get(key).getAsString();
             if ("path".equals(position) && inputJson.has(key)) {
-                // 处理路径参数
+                // Process path parameters
                 String value = inputJson.get(key).getAsString();
                 if (value.contains("?")) {
                     value = value.substring(0, value.indexOf("?"));
                 }
                 value = value.replace("\"", "").trim();
-                basePath = basePath.replace("{{." + key + "}}", value);
+                modifiedBasePath = modifiedBasePath.replace("{{." + key + "}}", value);
             } else if ("query".equals(position) && inputJson.has(key)) {
-                // handle query parameters
-                if (!existingQuery.contains(key + "=")) {
+                // Handle query parameters
+                if (!modifiedBasePath.contains(key + "=")) {
                     if (!queryBuilder.isEmpty()) {
                         queryBuilder.append("&");
                     }
@@ -161,14 +202,23 @@ public class RequestConfigHelper {
                 }
             }
         }
-        
-        // clean up the base path by removing any placeholders that were not replaced
-        basePath = basePath.replaceAll("\\{\\{\\.[^}]+}}", "");
-        
-        // build the final path
+        return modifiedBasePath;
+    }
+
+    /**
+     * Build the final path with query parameters.
+     *
+     * @param basePath the base path
+     * @param queryBuilder the query builder
+     * @param hasExistingQuery whether there are existing query parameters
+     * @param existingQuery the existing query string
+     * @return the final path
+     */
+    private static String buildFinalPath(final String basePath, final StringBuilder queryBuilder,
+                                       final boolean hasExistingQuery, final String existingQuery) {
         StringBuilder finalPath = new StringBuilder(basePath);
-        
-        // add new query parameters if any
+
+        // Add query parameters
         if (!queryBuilder.isEmpty()) {
             if (hasExistingQuery) {
                 finalPath.append("&").append(queryBuilder);
@@ -176,8 +226,8 @@ public class RequestConfigHelper {
                 finalPath.append("?").append(queryBuilder);
             }
         }
-        
-        // add existing query parameters if any
+
+        // Add existing query parameters
         if (hasExistingQuery && !existingQuery.isEmpty()) {
             if (!queryBuilder.isEmpty()) {
                 finalPath.append("&").append(existingQuery);
@@ -185,42 +235,14 @@ public class RequestConfigHelper {
                 finalPath.append("?").append(existingQuery);
             }
         }
-        
+
         String result = finalPath.toString();
-        // remove trailing question mark if it exists
+        // Remove the question mark at the end
         if (result.endsWith("?")) {
             result = result.substring(0, result.length() - 1);
         }
-        
+
         return result;
     }
-    
-    /**
-     * Build the request body JSON object based on the argument positions and input
-     * JSON.
-     *
-     * @param argsToJsonBody whether to convert arguments to JSON body
-     * @param argsPosition the argument position mapping
-     * @param inputJson the input JSON object
-     * @return the constructed body JSON object
-     */
-    public static JsonObject buildBodyJson(final boolean argsToJsonBody, final JsonObject argsPosition,
-                                           final JsonObject inputJson) {
-        JsonObject bodyJson = new JsonObject();
-        if (argsToJsonBody) {
-            for (String key : argsPosition.keySet()) {
-                String position = argsPosition.get(key).getAsString();
-                if ("body".equals(position) && inputJson.has(key)) {
-                    bodyJson.add(key, inputJson.get(key));
-                }
-            }
-        }
-        return bodyJson;
-    }
-    
-    public static boolean isRequestBodyMethod(final String method) {
-        return METHOD_POST.equalsIgnoreCase(method)
-                || METHOD_PUT.equalsIgnoreCase(method)
-                || METHOD_PATCH.equalsIgnoreCase(method);
-    }
+
 }
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/response/NonCommittingMcpResponseDecorator.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/response/NonCommittingMcpResponseDecorator.java
new file mode 100644
index 0000000000..5e76a08fd9
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/response/NonCommittingMcpResponseDecorator.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mcp.server.response;
+
+import com.google.gson.JsonObject;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.reactivestreams.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CompletableFuture;
+import java.util.Objects;
+
+/**
+ * Non-committing MCP response decorator for Streamable HTTP protocol.
+ * Intercepts response data without writing to the original response, avoiding premature
+ * response commitment issues. Used for capturing response data for processing and transformation
+ * before final output.
+ *
+ * @see org.apache.shenyu.plugin.mcp.server.response.ShenyuMcpResponseDecorator
+ * @since 2.7.0.2
+ */
+public class NonCommittingMcpResponseDecorator extends ServerHttpResponseDecorator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NonCommittingMcpResponseDecorator.class);
+
+    /**
+     * Template placeholder pattern for variable substitution.
+     */
+    private static final String PLACEHOLDER_PREFIX = "${";
+
+    private static final String PLACEHOLDER_SUFFIX = "}";
+
+    private final String sessionId;
+
+    private final CompletableFuture<String> responseFuture;
+
+    private final JsonObject responseTemplate;
+
+    /**
+     * Constructs a new non-committing MCP response decorator.
+     *
+     * @param delegate         the underlying HTTP response delegate
+     * @param sessionId        the MCP session identifier for correlation
+     * @param responseFuture   the future to complete with processed response data
+     * @param responseTemplate the JSON template for response transformation (nullable)
+     */
+    public NonCommittingMcpResponseDecorator(final ServerHttpResponse delegate,
+                                             final String sessionId,
+                                             final CompletableFuture<String> responseFuture,
+                                             final JsonObject responseTemplate) {
+        super(delegate);
+        this.sessionId = sessionId;
+        this.responseFuture = responseFuture;
+        this.responseTemplate = responseTemplate;
+        LOG.debug("Created non-committing MCP response decorator for session: {}", sessionId);
+    }
+
+    @Override
+    public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
+        LOG.debug("Processing writeWith for session: {}", sessionId);
+
+        return Flux.from(body)
+                .collectList()
+                .doOnNext(this::processResponseData)
+                .then()
+                .doOnSuccess(aVoid -> LOG.debug("Successfully completed writeWith for session: {}", sessionId))
+                .doOnError(error -> handleProcessingError("writeWith", error));
+    }
+
+    @Override
+    public Mono<Void> writeAndFlushWith(final Publisher<? extends Publisher<? extends DataBuffer>> body) {
+        LOG.debug("Processing writeAndFlushWith for session: {}", sessionId);
+
+        return Flux.from(body)
+                .flatMap(Flux::from)
+                .collectList()
+                .doOnNext(this::processResponseData)
+                .then()
+                .doOnSuccess(aVoid -> LOG.debug("Successfully completed writeAndFlushWith for session: {}", sessionId))
+                .doOnError(error -> handleProcessingError("writeAndFlushWith", error));
+    }
+
+    /**
+     * Processes the collected response data buffers and completes the response future.
+     *
+     * <p>Aggregates all data buffers into a single response string, applies response template
+     * transformations if configured, and completes the response future with the processed result.
+     *
+     * @param dataBuffers the collected response data buffers
+     */
+    private void processResponseData(final java.util.List<? extends DataBuffer> dataBuffers) {
+        try {
+            final String responseBody = aggregateDataBuffers(dataBuffers);
+            LOG.debug("Captured response data for session {}, length: {} chars", sessionId, responseBody.length());
+
+            final String processedResponse = processResponse(responseBody);
+            LOG.debug("Processed response for session {}, final length: {} chars", sessionId, processedResponse.length());
+
+            // Complete the future with processed response without writing to original response
+            responseFuture.complete(processedResponse);
+
+        } catch (Exception e) {
+            LOG.error("Error processing response data for session {}: {}", sessionId, e.getMessage(), e);
+            responseFuture.completeExceptionally(e);
+        }
+    }
+
+    /**
+     * Aggregates multiple data buffers into a single response string.
+     *
+     * @param dataBuffers the list of data buffers to aggregate
+     * @return the aggregated response string
+     */
+    private String aggregateDataBuffers(final java.util.List<? extends DataBuffer> dataBuffers) {
+        final StringBuilder responseBuilder = new StringBuilder();
+
+        for (DataBuffer buffer : dataBuffers) {
+            final byte[] bytes = new byte[buffer.readableByteCount()];
+            buffer.read(bytes);
+            responseBuilder.append(new String(bytes, StandardCharsets.UTF_8));
+        }
+
+        return responseBuilder.toString();
+    }
+
+    /**
+     * Handles processing errors by logging and completing the future exceptionally.
+     *
+     * @param operation the operation that failed
+     * @param error     the error that occurred
+     */
+    private void handleProcessingError(final String operation, final Throwable error) {
+        LOG.error("Error in {} operation for session {}: {}", operation, sessionId, error.getMessage(), error);
+        responseFuture.completeExceptionally(error);
+    }
+
+    /**
+     * Processes response data by applying response template transformations.
+     * If no response template is configured, returns the original response body unchanged.
+     * If a template is provided, attempts to parse the response as JSON and apply template
+     * transformations including placeholder substitution.
+     *
+     * @param responseBody the original response body to process
+     * @return the processed response body
+     */
+    private String processResponse(final String responseBody) {
+        try {
+            if (Objects.isNull(responseTemplate) || responseTemplate.size() == 0) {
+                return responseBody;
+            }
+
+            // Attempt to parse response as JSON for template processing
+            final JsonObject responseJson = parseResponseAsJson(responseBody);
+            if (Objects.isNull(responseJson)) {
+                LOG.debug("Response is not valid JSON for session: {}, returning unchanged", sessionId);
+                return responseBody;
+            }
+
+            // Apply response template transformations
+            return applyResponseTemplate(responseJson);
+
+        } catch (Exception e) {
+            LOG.error("Error applying response template for session {}: {}", sessionId, e.getMessage());
+            return responseBody;
+        }
+    }
+
+    /**
+     * Attempts to parse the response body as JSON.
+     *
+     * @param responseBody the response body to parse
+     * @return the parsed JSON object, or null if parsing fails
+     */
+    private JsonObject parseResponseAsJson(final String responseBody) {
+        try {
+            return GsonUtils.getInstance().fromJson(responseBody, JsonObject.class);
+        } catch (Exception e) {
+            LOG.debug("Failed to parse response as JSON for session: {}", sessionId);
+            return null;
+        }
+    }
+
+    /**
+     * Applies response template transformations to the parsed JSON response.
+     *
+     * @param responseJson the parsed JSON response data
+     * @return the transformed response as JSON string
+     */
+    private String applyResponseTemplate(final JsonObject responseJson) {
+        final JsonObject processedResponse = new JsonObject();
+
+        if (responseTemplate.has("content")) {
+            final JsonObject contentTemplate = responseTemplate.getAsJsonObject("content");
+            final JsonObject content = buildContentFromTemplate(contentTemplate, responseJson);
+            processedResponse.add("content", content);
+        } else {
+            // If no specific template structure, return original response
+            return GsonUtils.getInstance().toJson(responseJson);
+        }
+
+        return GsonUtils.getInstance().toJson(processedResponse);
+    }
+
+    /**
+     * Builds content object from template and response data.
+     *
+     * @param contentTemplate the content template configuration
+     * @param responseData    the response data for placeholder substitution
+     * @return the built content JSON object
+     */
+    private JsonObject buildContentFromTemplate(final JsonObject contentTemplate, final JsonObject responseData) {
+        final JsonObject content = new JsonObject();
+
+        if (contentTemplate.has("type")) {
+            content.addProperty("type", contentTemplate.get("type").getAsString());
+        }
+
+        if (contentTemplate.has("text")) {
+            final String textTemplate = contentTemplate.get("text").getAsString();
+            final String processedText = applyPlaceholderSubstitution(textTemplate, responseData);
+            content.addProperty("text", processedText);
+        }
+
+        return content;
+    }
+
+    /**
+     * Applies placeholder substitution to a text template using response data.
+     * Supports simple placeholder format: ${fieldName}
+     *
+     * @param template     the text template containing placeholders
+     * @param responseData the JSON object containing substitution values
+     * @return the text with placeholders replaced by actual values
+     */
+    private String applyPlaceholderSubstitution(final String template, final JsonObject responseData) {
+        if (Objects.isNull(responseData)) {
+            return template;
+        }
+
+        String result = template;
+
+        for (String key : responseData.keySet()) {
+            final String placeholder = PLACEHOLDER_PREFIX + key + PLACEHOLDER_SUFFIX;
+            if (result.contains(placeholder)) {
+                final String value = responseData.get(key).isJsonPrimitive()
+                        ? responseData.get(key).getAsString()
+                        : responseData.get(key).toString();
+                result = result.replace(placeholder, value);
+            }
+        }
+
+        return result;
+    }
+} 
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/MessageHandlingResult.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/MessageHandlingResult.java
new file mode 100644
index 0000000000..be971c7a43
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/MessageHandlingResult.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mcp.server.transport;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Result object for message handling operations.
+ * This class encapsulates the result of processing a Streamable HTTP message,
+ * including the HTTP status code, response body, and session correlation information.
+ */
+public class MessageHandlingResult {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MessageHandlingResult.class);
+
+    private final int statusCode;
+
+    private final Object responseBody;
+
+    private final String sessionId;
+
+    /**
+     * Creates a new message handling result.
+     *
+     * @param statusCode   the HTTP status code for the response
+     * @param responseBody the response body object
+     * @param sessionId    the session identifier for correlation (nullable)
+     */
+    public MessageHandlingResult(final int statusCode, final Object responseBody, final String sessionId) {
+        this.statusCode = statusCode;
+        this.responseBody = responseBody;
+        this.sessionId = sessionId;
+    }
+
+    /**
+     * Gets the HTTP status code for this result.
+     *
+     * @return the status code
+     */
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    /**
+     * Gets the response body object.
+     *
+     * @return the response body
+     */
+    public Object getResponseBody() {
+        return responseBody;
+    }
+
+    /**
+     * Gets the session identifier associated with this result.
+     *
+     * @return the session ID, or null if not available
+     */
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    /**
+     * Converts the response body to a JSON string for HTTP response transmission.
+     * If the response body is already a string, returns it as-is. Otherwise,
+     * attempts to serialize it to JSON using ObjectMapper. On serialization failure,
+     * returns a standard JSON-RPC error response.
+     *
+     * @return the response body as JSON string
+     */
+    public String getResponseBodyAsJson() {
+        if (responseBody instanceof String) {
+            return (String) responseBody;
+        }
+
+        try {
+            return new ObjectMapper().writeValueAsString(responseBody);
+        } catch (Exception e) {
+            LOGGER.error("Failed to serialize response body to JSON: {}", e.getMessage());
+            return "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32603,\"message\":\"Internal error\"}}";
+        }
+    }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/ShenyuStreamableHttpServerTransportProvider.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/ShenyuStreamableHttpServerTransportProvider.java
new file mode 100644
index 0000000000..fd31765b66
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/ShenyuStreamableHttpServerTransportProvider.java
@@ -0,0 +1,996 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mcp.server.transport;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.spec.McpError;
+import io.modelcontextprotocol.spec.McpSchema;
+import io.modelcontextprotocol.spec.McpServerSession;
+import io.modelcontextprotocol.spec.McpServerTransport;
+import io.modelcontextprotocol.spec.McpServerTransportProvider;
+import io.modelcontextprotocol.util.Assert;
+import org.apache.shenyu.plugin.mcp.server.holder.ShenyuMcpExchangeHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.Exceptions;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Streamable HTTP Server Transport Provider for MCP (Model Context Protocol).
+ * Implements the Streamable HTTP MCP transport protocol with unified endpoint for all MCP operations.
+ * Provides advanced session management with automatic recovery and reconnection capabilities.
+ *
+ * @see McpServerTransportProvider
+ * @see McpServerSession
+ * @see org.apache.shenyu.plugin.mcp.server.holder.ShenyuMcpExchangeHolder
+ * @since 2.7.0.2
+ */
+public class ShenyuStreamableHttpServerTransportProvider implements McpServerTransportProvider {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ShenyuStreamableHttpServerTransportProvider.class);
+
+    /**
+     * Header name for MCP session identification.
+     */
+    private static final String SESSION_ID_HEADER = "Mcp-Session-Id";
+
+    /**
+     * JSON-RPC 2.0 version identifier.
+     */
+    private static final String JSONRPC_VERSION = "2.0";
+
+    /**
+     * MCP initialize method name.
+     */
+    private static final String INITIALIZE_METHOD = "initialize";
+
+    /**
+     * Default MCP protocol version.
+     */
+    private static final String DEFAULT_PROTOCOL_VERSION = "2025-03-26";
+
+    /**
+     * Server information constants.
+     */
+    private static final String SERVER_NAME = "ShenyuMcpServer";
+
+    private static final String SERVER_VERSION = "1.0.0";
+
+    private final ObjectMapper objectMapper;
+
+    private McpServerSession.Factory sessionFactory;
+
+    /**
+     * Map of active client sessions, keyed by session ID.
+     * This enables session reuse for subsequent requests with the same session ID.
+     */
+    private final ConcurrentHashMap<String, McpServerSession> sessions = new ConcurrentHashMap<>();
+
+    /**
+     * Map of session transports, keyed by session ID.
+     * This enables access to transport-specific functionality and response correlation.
+     */
+    private final ConcurrentHashMap<String, StreamableHttpSessionTransport> sessionTransports = new ConcurrentHashMap<>();
+
+    /**
+     * Flag indicating if the transport provider is shutting down.
+     */
+    private volatile boolean isClosing;
+
+    /**
+     * Constructs a new Streamable HTTP server transport provider instance.
+     *
+     * @param objectMapper The ObjectMapper to use for JSON serialization/deserialization
+     * @param endpoint     The endpoint path for the Streamable HTTP MCP transport
+     * @throws IllegalArgumentException if objectMapper or endpoint is null
+     */
+    public ShenyuStreamableHttpServerTransportProvider(final ObjectMapper objectMapper, final String endpoint) {
+        Assert.notNull(objectMapper, "ObjectMapper must not be null");
+        Assert.notNull(endpoint, "Endpoint must not be null");
+        this.objectMapper = objectMapper;
+        LOGGER.debug("Created Streamable HTTP transport provider for endpoint: {}", endpoint);
+    }
+
+    /**
+     * Creates a new builder instance for constructing transport providers.
+     *
+     * @return a new Builder instance
+     */
+    public static StreamableHttpProviderBuilder builder() {
+        return new StreamableHttpProviderBuilder();
+    }
+
+    @Override
+    public void setSessionFactory(final McpServerSession.Factory sessionFactory) {
+        this.sessionFactory = sessionFactory;
+        LOGGER.debug("Session factory configured for Streamable HTTP transport");
+    }
+
+    @Override
+    public Mono<Void> notifyClients(final String method, final Object params) {
+        if (sessions.isEmpty()) {
+            LOGGER.debug("No active sessions available for client notification");
+            return Mono.empty();
+        }
+        LOGGER.debug("Broadcasting notification '{}' to {} active sessions", method, sessions.size());
+        return Flux.fromIterable(sessions.values())
+                .flatMap(session -> session.sendNotification(method, params)
+                        .doOnError(e -> LOGGER.warn("Failed to send notification to session {}: {}",
+                                session.getId(), e.getMessage()))
+                        .onErrorComplete())
+                .then()
+                .doOnSuccess(aVoid -> LOGGER.debug("Client notification broadcast completed"));
+    }
+
+    @Override
+    public Mono<Void> closeGracefully() {
+        isClosing = true;
+        if (sessions.isEmpty()) {
+            LOGGER.debug("No active sessions to close during graceful shutdown");
+            return Mono.empty();
+        }
+        LOGGER.debug("Initiating graceful shutdown of {} active sessions", sessions.size());
+        return Flux.fromIterable(sessions.values())
+                .flatMap(McpServerSession::closeGracefully)
+                .doOnComplete(() -> {
+                    sessions.clear();
+                    sessionTransports.clear();
+                    LOGGER.debug("Graceful shutdown completed - all sessions and transports cleared");
+                })
+                .then();
+    }
+
+    /**
+     * Unified endpoint for Streamable HTTP protocol. Handles GET (stream) and POST (message).
+     *
+     * @param request the server request
+     * @return a Mono containing the server response
+     */
+    public Mono<ServerResponse> handleUnifiedEndpoint(final ServerRequest request) {
+        if (isClosing) {
+            return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE).bodyValue("Server is shutting down");
+        }
+        if (Objects.isNull(sessionFactory)) {
+            LOGGER.error("SessionFactory is null - MCP server not properly initialized");
+            return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).bodyValue("MCP server not properly initialized");
+        }
+        if ("OPTIONS".equalsIgnoreCase(request.methodName())) {
+            // Handle CORS preflight requests
+            return ServerResponse.ok()
+                    .header("Access-Control-Allow-Origin", "*")
+                    .header("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id, Authorization")
+                    .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
+                    .header("Access-Control-Max-Age", "3600")
+                    .build();
+        } else if ("GET".equalsIgnoreCase(request.methodName())) {
+            // Streamable HTTP protocol does not support GET requests, return 405 error
+            return ServerResponse.status(HttpStatus.METHOD_NOT_ALLOWED)
+                    .header("Access-Control-Allow-Origin", "*")
+                    .header("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id, Authorization")
+                    .header("Access-Control-Allow-Methods", "POST, OPTIONS")
+                    .header("Allow", "POST, OPTIONS")
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .bodyValue(new java.util.HashMap<String, Object>() {{
+                            put("error", new java.util.HashMap<String, Object>() {{
+                                    put("code", -32601);
+                                    put("message", "Streamable HTTP does not support GET requests. Please use POST requests for all MCP operations.");
+                                }});
+                        }});
+        } else if ("POST".equalsIgnoreCase(request.methodName())) {
+            // Extract ServerWebExchange from ServerRequest
+            final ServerWebExchange exchange = request.exchange();
+            return handleMessageEndpoint(exchange, request).flatMap(result -> {
+                ServerResponse.BodyBuilder builder = ServerResponse.status(HttpStatus.valueOf(result.getStatusCode()))
+                        .header("Access-Control-Allow-Origin", "*")
+                        .header("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id, Authorization")
+                        .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
+                if (Objects.nonNull(result.getSessionId())) {
+                    builder.header(SESSION_ID_HEADER, result.getSessionId());
+                }
+                builder.contentType(MediaType.APPLICATION_JSON);
+                return builder.bodyValue(result.getResponseBodyAsJson());
+            });
+        }
+        return ServerResponse.badRequest().bodyValue(new McpError("Unsupported HTTP method"));
+    }
+
+    /**
+     * Handles POST requests for message processing in Streamable HTTP protocol.
+     * This method processes all MCP operations including initialize, tools/list, tools/call,
+     * and other custom operations. Enhanced to support requests without sessionId (creates
+     * temporary sessions) and invalid sessionId (creates new session and re-stores it).
+     * The method distinguishes between initialize requests (which create new sessions)
+     * and regular requests (which require existing session correlation or temporary session creation).
+     *
+     * @param exchange the server web exchange
+     * @param request  the server request containing the message
+     * @return a Mono containing the message handling result
+     */
+    public Mono<MessageHandlingResult> handleMessageEndpoint(final ServerWebExchange exchange, final ServerRequest request) {
+        LOGGER.debug("Processing Streamable HTTP message for path: {}", request.path());
+        return request.bodyToMono(String.class)
+                .flatMap(body -> {
+                    LOGGER.debug("Received request body with length: {} chars", body.length());
+                    try {
+                        // Deserialize JSON-RPC request
+                        final McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(objectMapper, body);
+                        LOGGER.debug("Parsed JSON-RPC message of type: {}", message.getClass().getSimpleName());
+                        // Handle initialize requests specially
+                        if (isInitializeRequest(message)) {
+                            return handleInitializeRequest(exchange, message);
+                        }
+                        // Handle regular requests with session management enhancement
+                        return handleRegularRequestWithEnhancement(exchange, message, request);
+                    } catch (IOException e) {
+                        LOGGER.warn("Failed to parse JSON-RPC message: {}", e.getMessage());
+                        final Object errorResponse = createJsonRpcError(null, -32700, "Parse error: Invalid JSON-RPC message");
+                        return Mono.just(new MessageHandlingResult(400, errorResponse, null));
+                    } catch (Exception e) {
+                        LOGGER.error("Unexpected error handling message: {}", e.getMessage(), e);
+                        final Object errorResponse = createJsonRpcError(null, -32603, "Internal error: " + e.getMessage());
+                        return Mono.just(new MessageHandlingResult(500, errorResponse, null));
+                    }
+                });
+    }
+
+    /**
+     * Checks if the given message is an initialize request.
+     *
+     * @param message the JSON-RPC message to check
+     * @return true if this is an initialize request
+     */
+    private boolean isInitializeRequest(final McpSchema.JSONRPCMessage message) {
+        if (message instanceof io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) {
+            final String method = ((io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) message).method();
+            return INITIALIZE_METHOD.equals(method);
+        }
+        return false;
+    }
+
+    /**
+     * Handles initialize requests by creating new sessions and transport connections.
+     *
+     * @param exchange the server web exchange
+     * @param message  the initialize request message
+     * @return a Mono containing the initialization result
+     */
+    private Mono<MessageHandlingResult> handleInitializeRequest(final ServerWebExchange exchange,
+                                                                final McpSchema.JSONRPCMessage message) {
+        try {
+            // Create new session and transport
+            final StreamableHttpSessionTransport transport = new StreamableHttpSessionTransport();
+            final McpServerSession session = sessionFactory.create(transport);
+            final String newSessionId = session.getId();
+            LOGGER.debug("Created new MCP session: {}", newSessionId);
+            // Store session and transport for reuse
+            sessions.put(newSessionId, session);
+            sessionTransports.put(newSessionId, transport);
+            // Configure exchange attributes
+            configureExchangeForSession(exchange, newSessionId);
+            // Extract and validate protocol version
+            final Object messageId = extractMessageId(message);
+            final String clientProtocolVersion = extractProtocolVersionFromInitialize(message);
+            if (!isSupportedProtocolVersion(clientProtocolVersion)) {
+                LOGGER.warn("Unsupported protocol version requested: {}", clientProtocolVersion);
+                cleanupInvalidSession(newSessionId);
+                final Object errorResponse = createJsonRpcError(messageId, -32600,
+                        "Unsupported protocol version. Supported versions: ['" + DEFAULT_PROTOCOL_VERSION + "']");
+                return Mono.just(new MessageHandlingResult(400, errorResponse, null));
+            }
+            // Create initialize response
+            final Object initializeResponse = createInitializeResponse(messageId, clientProtocolVersion, newSessionId);
+            LOGGER.debug("Initialize request processed successfully for session: {}", newSessionId);
+            return Mono.just(new MessageHandlingResult(200, initializeResponse, newSessionId));
+        } catch (Exception e) {
+            LOGGER.error("Error handling initialize request: {}", e.getMessage(), e);
+            final Object errorResponse = createJsonRpcError(extractMessageId(message), -32603,
+                    "Internal error during initialization: " + e.getMessage());
+            return Mono.just(new MessageHandlingResult(500, errorResponse, null));
+        }
+    }
+
+    /**
+     * Handles regular (non-initialize) requests with comprehensive session management.
+     * Implements session management strategy for missing session ID (creates temporary session),
+     * invalid session ID (creates new session), and valid session ID (processes normally).
+     * Provides automatic session recovery and ServerWebExchange correlation for tool callbacks.
+     *
+     * @param exchange the server web exchange
+     * @param message  the JSON-RPC message
+     * @param request  the server request
+     * @return a Mono containing the processing result
+     */
+    private Mono<MessageHandlingResult> handleRegularRequestWithEnhancement(final ServerWebExchange exchange,
+                                                                            final McpSchema.JSONRPCMessage message,
+                                                                            final ServerRequest request) {
+        final String requestedSessionId = extractSessionId(request);
+        final Object messageId = extractMessageId(message);
+        if (Objects.isNull(requestedSessionId)) {
+            LOGGER.info("No sessionId provided, creating temporary session for request");
+            return createTemporarySessionAndProcess(exchange, message, messageId);
+        }
+        final McpServerSession existingSession = sessions.get(requestedSessionId);
+        if (Objects.isNull(existingSession)) {
+            LOGGER.info("SessionId {} not found, creating new session and re-storing", requestedSessionId);
+            return createSessionAndRestoreId(exchange, message, requestedSessionId, messageId);
+        }
+        LOGGER.debug("Processing request for existing session: {}", requestedSessionId);
+        final ServerWebExchange existingExchange = ShenyuMcpExchangeHolder.get(requestedSessionId);
+        LOGGER.debug("Checking exchange mapping for session {}: existing={}", requestedSessionId,
+                Objects.nonNull(existingExchange) ? "present (ID: " + System.identityHashCode(existingExchange) + ")" : "null");
+        if (Objects.isNull(ShenyuMcpExchangeHolder.get(requestedSessionId))) {
+            LOGGER.info("Exchange mapping lost for session {}, re-binding new exchange (ID: {})",
+                    requestedSessionId, System.identityHashCode(exchange));
+            configureExchangeForSession(exchange, requestedSessionId);
+            LOGGER.info("Re-bound ServerWebExchange to session {} after reconnect", requestedSessionId);
+        } else {
+            LOGGER.debug("Exchange mapping already exists for session {}, using existing exchange", requestedSessionId);
+        }
+        return processWithExistingSession(existingSession, requestedSessionId, message, messageId)
+                .map(result -> {
+                    if (!requestedSessionId.equals(result.getSessionId())) {
+                        LOGGER.info("Returning actual session ID {} instead of requested ID {}", result.getSessionId(), requestedSessionId);
+                        return new MessageHandlingResult(result.getStatusCode(), result.getResponseBody(), result.getSessionId());
+                    }
+                    return result;
+                });
+    }
+
+    /**
+     * Creates a temporary session for stateless requests.
+     * This method handles requests that arrive without a session ID by creating a temporary
+     * session that is automatically cleaned up after the request completes. The session is
+     * fully initialized using async methods to avoid blocking the Netty event loop.
+     * Process Flow:
+     * - Create session with auto-generated ID from MCP framework
+     * - Bind ServerWebExchange to session ID before initialization
+     * - Perform async session initialization (initialize + notification)
+     * - Process the business request
+     * - Clean up session and exchange binding after completion
+     *
+     * @param exchange  the server web exchange
+     * @param message   the JSON-RPC message
+     * @param messageId the message ID for correlation
+     * @return a Mono containing the processing result
+     */
+    private Mono<MessageHandlingResult> createTemporarySessionAndProcess(final ServerWebExchange exchange,
+                                                                         final McpSchema.JSONRPCMessage message,
+                                                                         final Object messageId) {
+        try {
+            final StreamableHttpSessionTransport tempTransport = new StreamableHttpSessionTransport();
+            final McpServerSession tempSession = sessionFactory.create(tempTransport);
+            final String actualSessionId = tempSession.getId();
+            LOGGER.info("Created temporary session: {}", actualSessionId);
+            sessions.put(actualSessionId, tempSession);
+            sessionTransports.put(actualSessionId, tempTransport);
+            configureExchangeForSession(exchange, actualSessionId);
+            LOGGER.debug("Bound exchange to temporary session: {} before handshake", actualSessionId);
+            initializeSessionDirectly(tempSession, actualSessionId);
+            tempTransport.resetCapturedMessage();
+            return processWithExistingSession(tempSession, actualSessionId, message, messageId)
+                    .doFinally(signalType -> {
+                        LOGGER.debug("Cleaning up temporary session: {} (signal: {})", actualSessionId, signalType);
+                        removeSession(actualSessionId);
+                        ShenyuMcpExchangeHolder.remove(actualSessionId);
+                    })
+                    .map(result -> new MessageHandlingResult(result.getStatusCode(), result.getResponseBody(), null));
+        } catch (Exception e) {
+            LOGGER.error("Error creating temporary session: {}", e.getMessage(), e);
+            final Object errorResponse = createJsonRpcError(messageId, -32603,
+                    "Internal error creating temporary session: " + e.getMessage());
+            return Mono.just(new MessageHandlingResult(500, errorResponse, null));
+        }
+    }
+
+    /**
+     * Creates a new session for session restoration scenarios.
+     * This method handles scenarios where a client provides a session ID that no longer
+     * exists on the server (e.g., server restart, session timeout, network disconnection).
+     * A new session is created using the MCP framework, which generates its own session ID.
+     * The client receives the new session ID for subsequent requests.
+     * Important: The MCP framework generates its own session IDs, so the
+     * client's requested session ID may differ from the actual session ID returned.
+     * The response includes the actual session ID that should be used for future requests.
+     *
+     * @param exchange           the server web exchange
+     * @param message            the JSON-RPC message
+     * @param requestedSessionId the sessionId that the client requested
+     * @param messageId          the message ID for correlation
+     * @return a Mono containing the processing result
+     */
+    private Mono<MessageHandlingResult> createSessionAndRestoreId(final ServerWebExchange exchange,
+                                                                  final McpSchema.JSONRPCMessage message,
+                                                                  final String requestedSessionId,
+                                                                  final Object messageId) {
+        try {
+            final StreamableHttpSessionTransport newTransport = new StreamableHttpSessionTransport(requestedSessionId);
+            final McpServerSession newSession = sessionFactory.create(newTransport);
+            final String actualSessionId = newSession.getId();
+            LOGGER.info("Created new session - requested ID: {}, actual ID: {}", requestedSessionId, actualSessionId);
+            sessions.put(actualSessionId, newSession);
+            sessionTransports.put(actualSessionId, newTransport);
+            configureExchangeForSession(exchange, actualSessionId);
+            LOGGER.debug("Bound exchange to restored session: {} before handshake", actualSessionId);
+            initializeSessionDirectly(newSession, actualSessionId);
+            newTransport.resetCapturedMessage();
+            return processWithExistingSession(newSession, actualSessionId, message, messageId)
+                    .map(result -> {
+                        if (!actualSessionId.equals(requestedSessionId)) {
+                            LOGGER.info("Returning actual session ID {} instead of requested ID {}", actualSessionId, requestedSessionId);
+                            return new MessageHandlingResult(result.getStatusCode(), result.getResponseBody(), actualSessionId);
+                        }
+                        return result;
+                    });
+        } catch (Exception e) {
+            LOGGER.error("Error creating session with restored ID {}: {}", requestedSessionId, e.getMessage(), e);
+            final Object errorResponse = createJsonRpcError(messageId, -32603,
+                    "Internal error restoring session: " + e.getMessage());
+            return Mono.just(new MessageHandlingResult(500, errorResponse, requestedSessionId));
+        }
+    }
+
+    /**
+     * Processes a message with an existing session.
+     * This method contains the core message processing logic that is shared
+     * between regular requests, temporary sessions, and restored sessions.
+     *
+     * @param session   the MCP server session
+     * @param sessionId the session identifier
+     * @param message   the JSON-RPC message
+     * @param messageId the message ID for correlation
+     * @return a Mono containing the processing result
+     */
+    private Mono<MessageHandlingResult> processWithExistingSession(final McpServerSession session,
+                                                                   final String sessionId,
+                                                                   final McpSchema.JSONRPCMessage message,
+                                                                   final Object messageId) {
+        // Verify exchange is available before processing
+        final ServerWebExchange verifyExchange = ShenyuMcpExchangeHolder.get(sessionId);
+        if (Objects.isNull(verifyExchange)) {
+            LOGGER.error("CRITICAL: No exchange found in ShenyuMcpExchangeHolder for session {} when processing business request. This will cause ToolCallback to fail.", sessionId);
+        } else {
+            LOGGER.debug("Exchange verification passed for session {} (exchange ID: {})",
+                    sessionId, System.identityHashCode(verifyExchange));
+        }
+        final StreamableHttpSessionTransport transport = getSessionTransport(sessionId);
+        // Let MCP framework handle the message - framework will send response through transport
+        return session.handle(message)
+                .cast(Object.class)
+                .doOnSuccess(result -> LOGGER.debug("Successfully processed message for session: {}", sessionId))
+                .then(waitForTransportResponse(transport, sessionId, messageId))
+                .onErrorResume(error -> {
+                    LOGGER.error("Error processing message for session {}: {}", sessionId, error.getMessage(), error);
+                    final Object errorResponse = createJsonRpcError(messageId, -32603,
+                            "Internal error: " + error.getMessage());
+                    return Mono.just(new MessageHandlingResult(500, errorResponse, sessionId));
+                });
+    }
+
+    /**
+     * Configures exchange attributes for the given session.
+     *
+     * @param exchange  the server web exchange
+     * @param sessionId the session identifier
+     */
+    private void configureExchangeForSession(final ServerWebExchange exchange, final String sessionId) {
+        if (Objects.isNull(exchange)) {
+            LOGGER.error("Attempted to configure null exchange for session: {}", sessionId);
+            return;
+        }
+        exchange.getAttributes().put("MCP_SESSION_ID", sessionId);
+        ShenyuMcpExchangeHolder.put(sessionId, exchange);
+        // Verify exchange was stored correctly
+        final ServerWebExchange storedExchange = ShenyuMcpExchangeHolder.get(sessionId);
+        if (Objects.isNull(storedExchange)) {
+            LOGGER.error("Failed to store exchange in ShenyuMcpExchangeHolder for session: {}", sessionId);
+        } else {
+            LOGGER.info("Successfully configured and stored exchange for session: {} (exchange ID: {})",
+                    sessionId, System.identityHashCode(exchange));
+        }
+    }
+
+    /**
+     * Cleans up an invalid session that failed initialization.
+     *
+     * @param sessionId the session ID to clean up
+     */
+    private void cleanupInvalidSession(final String sessionId) {
+        sessions.remove(sessionId);
+        sessionTransports.remove(sessionId);
+        LOGGER.debug("Cleaned up invalid session: {}", sessionId);
+    }
+
+    /**
+     * Extracts the protocol version from an initialize request.
+     *
+     * @param message the initialize request message
+     * @return the requested protocol version or default version
+     */
+    private String extractProtocolVersionFromInitialize(final McpSchema.JSONRPCMessage message) {
+        if (message instanceof io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) {
+            final Object params = ((io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) message).params();
+            if (params instanceof Map) {
+                final Map<?, ?> paramsMap = (Map<?, ?>) params;
+                final Object protocolVersion = paramsMap.get("protocolVersion");
+                return Objects.nonNull(protocolVersion) ? protocolVersion.toString() : DEFAULT_PROTOCOL_VERSION;
+            }
+        }
+        return DEFAULT_PROTOCOL_VERSION;
+    }
+
+    /**
+     * Checks if the specified protocol version is supported.
+     *
+     * @param version the protocol version to check
+     * @return true if the version is supported
+     */
+    private boolean isSupportedProtocolVersion(final String version) {
+        return DEFAULT_PROTOCOL_VERSION.equals(version);
+    }
+
+    /**
+     * Creates a standard initialize response for successful initialization.
+     *
+     * @param messageId       the original message ID for correlation
+     * @param protocolVersion the negotiated protocol version
+     * @param sessionId       the created session ID
+     * @return the initialize response object
+     */
+    private Object createInitializeResponse(final Object messageId, final String protocolVersion, final String sessionId) {
+        final Map<String, Object> capabilities = new java.util.HashMap<>();
+        final Map<String, Object> toolsCapability = new java.util.HashMap<>();
+        toolsCapability.put("listChanged", true);
+        capabilities.put("tools", toolsCapability);
+        final Map<String, Object> serverInfo = new java.util.HashMap<>();
+        serverInfo.put("name", SERVER_NAME);
+        serverInfo.put("version", SERVER_VERSION);
+        final Map<String, Object> result = new java.util.HashMap<>();
+        result.put("protocolVersion", protocolVersion);
+        result.put("capabilities", capabilities);
+        result.put("serverInfo", serverInfo);
+        result.put("instructions", "Use available tools to interact with Shenyu gateway services");
+        result.put("sessionId", sessionId);
+        return createJsonRpcResponse(messageId, result);
+    }
+
+    /**
+     * Extracts the message ID from a JSON-RPC message for response correlation.
+     *
+     * @param message the JSON-RPC message
+     * @return the message ID, or null if not available
+     */
+    private Object extractMessageId(final McpSchema.JSONRPCMessage message) {
+        if (message instanceof io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) {
+            return ((io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest) message).id();
+        }
+        return null;
+    }
+
+    /**
+     * Creates a standard JSON-RPC 2.0 success response.
+     *
+     * @param id     the message ID for correlation
+     * @param result the result object
+     * @return the response object
+     */
+    private Object createJsonRpcResponse(final Object id, final Object result) {
+        final Map<String, Object> response = new java.util.HashMap<>();
+        response.put("jsonrpc", JSONRPC_VERSION);
+        if (Objects.nonNull(id)) {
+            response.put("id", id);
+        }
+        response.put("result", Objects.nonNull(result) ? result : new java.util.HashMap<>());
+        return response;
+    }
+
+    /**
+     * Creates a standard JSON-RPC 2.0 error response.
+     *
+     * @param id      the message ID for correlation
+     * @param code    the error code
+     * @param message the error message
+     * @return the error response object
+     */
+    private Object createJsonRpcError(final Object id, final int code, final String message) {
+        final Map<String, Object> error = new java.util.HashMap<>();
+        error.put("code", code);
+        error.put("message", message);
+        final Map<String, Object> response = new java.util.HashMap<>();
+        response.put("jsonrpc", JSONRPC_VERSION);
+        if (Objects.nonNull(id)) {
+            response.put("id", id);
+        }
+        response.put("error", error);
+        return response;
+    }
+
+    /**
+     * Extracts the session ID from the request headers or query parameters.
+     *
+     * @param request the server request
+     * @return the session ID, or null if not found
+     */
+    private String extractSessionId(final ServerRequest request) {
+        String sessionId = request.queryParam("sessionId").orElse(null);
+        if (Objects.nonNull(sessionId)) {
+            return sessionId;
+        }
+        return request.headers().firstHeader(SESSION_ID_HEADER);
+    }
+
+    /**
+     * Removes a session from the active sessions map.
+     * This method should be called when a session is closed or becomes invalid
+     * to prevent memory leaks and ensure proper cleanup.
+     *
+     * @param sessionId the session ID to remove
+     */
+    public void removeSession(final String sessionId) {
+        final McpServerSession removedSession = sessions.remove(sessionId);
+        final StreamableHttpSessionTransport removedTransport = sessionTransports.remove(sessionId);
+        if (Objects.nonNull(removedSession) || Objects.nonNull(removedTransport)) {
+            LOGGER.debug("Removed session and transport: {}", sessionId);
+        }
+    }
+
+    /**
+     * Gets the session transport for a given session ID.
+     *
+     * @param sessionId the session identifier
+     * @return the session transport, or null if not found
+     */
+    private StreamableHttpSessionTransport getSessionTransport(final String sessionId) {
+        return sessionTransports.get(sessionId);
+    }
+
+    /**
+     * Waits for and retrieves the transport response for correlation with HTTP response.
+     * Checks if the transport has captured a response from the MCP framework and returns it as a MessageHandlingResult.
+     * If no response is available, returns a default success response to prevent hanging.
+     *
+     * @param transport the session transport to check for responses
+     * @param sessionId the session identifier for logging
+     * @param messageId the original message ID for correlation
+     * @return a Mono containing the message handling result
+     */
+    private Mono<MessageHandlingResult> waitForTransportResponse(final StreamableHttpSessionTransport transport,
+                                                                 final String sessionId,
+                                                                 final Object messageId) {
+        return Mono.fromCallable(() -> {
+            if (Objects.nonNull(transport) && transport.isResponseReady() && Objects.nonNull(transport.getLastSentMessage())) {
+                final McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessage();
+                LOGGER.debug("Retrieved captured response from transport for session: {}", sessionId);
+                return new MessageHandlingResult(200, sentMessage, sessionId);
+            } else {
+                LOGGER.debug("No response captured from transport, returning default success for session: {}", sessionId);
+                final Object successResponse = createJsonRpcResponse(messageId, new java.util.HashMap<>());
+                return new MessageHandlingResult(200, successResponse, sessionId);
+            }
+        });
+    }
+
+    /**
+     * Performs backend session initialization using async MCP protocol handshake.
+     * Simulates complete initialize request-response cycle to ensure session's internal
+     * state machine transitions correctly. Uses async operations to avoid blocking Netty threads.
+     *
+     * @param session   the MCP server session
+     * @param sessionId the session identifier
+     */
+    private void initializeSessionDirectly(final McpServerSession session, final String sessionId) {
+        try {
+            // Core strategy: Simulate complete initialize request-response cycle
+            // This ensures the session's internal state machine transitions correctly
+            LOGGER.debug("Starting backend initialization for session: {}", sessionId);
+            // Create a proper initialize request
+            final String initRequestJson = createInitializeRequest();
+            final McpSchema.JSONRPCMessage initRequest = McpSchema.deserializeJsonRpcMessage(objectMapper, initRequestJson);
+            LOGGER.debug("Created initialize request for session: {}", sessionId);
+            // Use subscribe instead of block to avoid blocking in Netty thread
+            session.handle(initRequest)
+                    .doOnSuccess(v -> {
+                        LOGGER.debug("Initialize request processed successfully for session: {}", sessionId);
+                        // Complete the handshake by sending a notification to finalize session state
+                        try {
+                            final StreamableHttpSessionTransport transport = sessionTransports.get(sessionId);
+                            if (Objects.nonNull(transport) && transport.isResponseReady()) {
+                                LOGGER.debug("Initialize response captured, session {} should be ready", sessionId);
+                                // Complete the handshake by sending a notification to finalize session state
+                                completeInitializationHandshakeAsync(session, sessionId);
+                            } else {
+                                LOGGER.warn("No initialize response captured for session: {}", sessionId);
+                            }
+                        } catch (Exception e) {
+                            LOGGER.error("Error checking initialize response for session {}: {}", sessionId, e.getMessage());
+                        }
+                    })
+                    .doOnError(e -> {
+                        LOGGER.error("Failed to process initialize request for session {}: {}", sessionId, e.getMessage(), e);
+                    })
+                    // Use subscribe instead of block to avoid blocking
+                    .subscribe();
+            LOGGER.debug("Backend initialization completed for session: {}", sessionId);
+            // Verify initialization success
+            if (verifySessionInitialized(session, sessionId)) {
+                LOGGER.info("Session {} successfully initialized and ready for business requests", sessionId);
+            } else {
+                LOGGER.warn("Session {} initialization may be incomplete - proceeding anyway", sessionId);
+            }
+        } catch (Exception e) {
+            LOGGER.error("Unexpected error during session initialization for {}: {}", sessionId, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Creates a proper initialize request JSON for backend session initialization.
+     *
+     * @return the initialize request JSON string
+     */
+    private String createInitializeRequest() {
+        final Map<String, Object> params = new java.util.HashMap<>();
+        params.put("protocolVersion", DEFAULT_PROTOCOL_VERSION);
+        params.put("capabilities", new java.util.HashMap<String, Object>() {{
+                put("roots", new java.util.ArrayList<>());
+            }});
+        params.put("clientInfo", new java.util.HashMap<String, Object>() {{
+                put("name", "ShenyuMcpClient");
+                put("version", "1.0.0");
+            }});
+        final Map<String, Object> request = new java.util.HashMap<>();
+        request.put("jsonrpc", JSONRPC_VERSION);
+        request.put("id", "__backend_init");
+        request.put("method", INITIALIZE_METHOD);
+        request.put("params", params);
+        try {
+            return objectMapper.writeValueAsString(request);
+        } catch (Exception e) {
+            LOGGER.error("Failed to create initialize request JSON: {}", e.getMessage());
+            return "{\"jsonrpc\":\"2.0\",\"id\":\"__backend_init\",\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"" + DEFAULT_PROTOCOL_VERSION + "\"}}";
+        }
+    }
+
+    /**
+     * Verifies that a session has been properly initialized.
+     *
+     * @param session   the MCP server session to verify
+     * @param sessionId the session identifier for logging
+     * @return true if the session appears to be initialized
+     */
+    private boolean verifySessionInitialized(final McpServerSession session, final String sessionId) {
+        try {
+            // Strategy 1: Check for 'initialized' field
+            try {
+                final java.lang.reflect.Field initializedField = session.getClass().getDeclaredField("initialized");
+                initializedField.setAccessible(true);
+                final Object value = initializedField.get(session);
+                final boolean isInitialized = Boolean.TRUE.equals(value);
+                LOGGER.debug("Session {} initialized field value: {}", sessionId, isInitialized);
+                return isInitialized;
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                LOGGER.debug("No 'initialized' field found or accessible in session: {}", e.getMessage());
+            }
+            // Strategy 2: Check for 'state' field
+            try {
+                final java.lang.reflect.Field stateField = session.getClass().getDeclaredField("state");
+                stateField.setAccessible(true);
+                final Object stateValue = stateField.get(session);
+                if (Objects.nonNull(stateValue)) {
+                    final String stateStr = stateValue.toString().toLowerCase();
+                    final boolean isInitialized = stateStr.contains("init") && !stateStr.contains("uninit");
+                    LOGGER.debug("Session {} state value: {} (initialized: {})", sessionId, stateValue, isInitialized);
+                    return isInitialized;
+                }
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                LOGGER.debug("No 'state' field found or accessible in session: {}", e.getMessage());
+            }
+            // Strategy 3: Check transport response
+            final StreamableHttpSessionTransport transport = sessionTransports.get(sessionId);
+            if (Objects.nonNull(transport)) {
+                final boolean hasResponse = transport.isResponseReady();
+                LOGGER.debug("Session {} transport has response ready: {}", sessionId, hasResponse);
+                // If we have a response, the session likely processed the initialize request
+                return hasResponse;
+            }
+            LOGGER.debug("Unable to verify initialization state for session: {}", sessionId);
+            // Unable to verify, assume not initialized
+            return false;
+        } catch (Exception e) {
+            LOGGER.error("Error verifying session initialization for {}: {}", sessionId, e.getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * Completes the handshake by sending a notification to finalize session state asynchronously.
+     * Uses multiple strategies to ensure session state is properly set, including sending
+     * initialized notifications and using reflection as fallback.
+     *
+     * @param session   the MCP server session
+     * @param sessionId the session identifier
+     */
+    private void completeInitializationHandshakeAsync(final McpServerSession session, final String sessionId) {
+        try {
+            // Strategy 1: Send a "initialized" notification to complete the handshake
+            try {
+                final Map<String, Object> notification = new java.util.HashMap<>();
+                notification.put("jsonrpc", JSONRPC_VERSION);
+                notification.put("method", "notifications/initialized");
+                notification.put("params", new java.util.HashMap<>());
+                final String notificationJson = objectMapper.writeValueAsString(notification);
+                final McpSchema.JSONRPCMessage notificationMessage = McpSchema.deserializeJsonRpcMessage(objectMapper, notificationJson);
+                session.handle(notificationMessage)
+                        .doOnSuccess(v -> {
+                            LOGGER.debug("Initialized notification sent successfully for session: {}", sessionId);
+                        })
+                        .doOnError(e -> {
+                            LOGGER.debug("Initialized notification failed for session {}: {}", sessionId, e.getMessage());
+                        })
+                        .onErrorComplete()
+                        .subscribe();
+            } catch (Exception e) {
+                LOGGER.debug("Strategy 1 failed: {}", e.getMessage());
+            }
+            // Strategy 2: Force state transition using reflection as a fallback
+            try {
+                final java.lang.reflect.Field initializedField = session.getClass().getDeclaredField("initialized");
+                initializedField.setAccessible(true);
+                initializedField.set(session, true);
+                LOGGER.debug("Successfully set session {} to initialized state via reflection", sessionId);
+            } catch (Exception e) {
+                LOGGER.debug("Strategy 2 failed: {}", e.getMessage());
+            }
+            // Strategy 3: Try to set state field
+            try {
+                final java.lang.reflect.Field stateField = session.getClass().getDeclaredField("state");
+                stateField.setAccessible(true);
+                final Object stateValue = stateField.get(session);
+                if (Objects.nonNull(stateValue) && stateValue.getClass().isEnum()) {
+                    // Try to find INITIALIZED enum value
+                    for (Object enumConstant : stateValue.getClass().getEnumConstants()) {
+                        if (enumConstant.toString().toLowerCase().contains("init") && !enumConstant.toString().toLowerCase().contains("uninit")) {
+                            stateField.set(session, enumConstant);
+                            LOGGER.debug("Successfully set session {} state to {} via reflection", sessionId, enumConstant);
+                            break;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                LOGGER.debug("Strategy 3 failed: {}", e.getMessage());
+            }
+        } catch (Exception e) {
+            LOGGER.error("Error completing handshake for session {}: {}", sessionId, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Session transport implementation for Streamable HTTP with proper lifecycle management.
+     * This transport handles the communication between the MCP framework and the Streamable HTTP
+     * protocol. It captures responses from the MCP session and makes them available for
+     * HTTP response correlation.
+     */
+    private class StreamableHttpSessionTransport implements McpServerTransport {
+
+        private final String sessionId;
+
+        private volatile boolean closed;
+
+        private volatile McpSchema.JSONRPCMessage lastSentMessage;
+
+        private volatile boolean responseReady;
+
+        /**
+         * Creates a new session transport with auto-generated session ID.
+         */
+        StreamableHttpSessionTransport() {
+            this.sessionId = java.util.UUID.randomUUID().toString();
+            LOGGER.debug("Created StreamableHttpSessionTransport with auto-generated sessionId: {}", this.sessionId);
+        }
+
+        /**
+         * Creates a new session transport with the specified session ID.
+         *
+         * @param sessionId the session identifier, or null to auto-generate
+         */
+        StreamableHttpSessionTransport(final String sessionId) {
+            this.sessionId = Objects.nonNull(sessionId) ? sessionId : java.util.UUID.randomUUID().toString();
+            LOGGER.debug("Created StreamableHttpSessionTransport with sessionId: {}", this.sessionId);
+        }
+
+        /**
+         * Gets the last message sent through this transport.
+         * This is used for Streamable HTTP response correlation, allowing the
+         * transport provider to capture and return responses sent by the MCP framework.
+         *
+         * @return the last sent message, or null if no message has been sent
+         */
+        public McpSchema.JSONRPCMessage getLastSentMessage() {
+            return lastSentMessage;
+        }
+
+        /**
+         * Checks if a response is ready for retrieval.
+         *
+         * @return true if response is available
+         */
+        public boolean isResponseReady() {
+            return responseReady;
+        }
+
+        @Override
+        public Mono<Void> sendMessage(final McpSchema.JSONRPCMessage message) {
+            if (!closed) {
+                this.lastSentMessage = message;
+                this.responseReady = true;
+                LOGGER.debug("Captured response message for session: {}", sessionId);
+            }
+            return Mono.empty();
+        }
+
+        @Override
+        public <T> T unmarshalFrom(final Object data, final TypeReference<T> typeRef) {
+            try {
+                return new ObjectMapper().convertValue(data, typeRef);
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
+        @Override
+        public Mono<Void> closeGracefully() {
+            return Mono.fromRunnable(() -> {
+                if (!closed) {
+                    closed = true;
+                    removeSession(sessionId);
+                    LOGGER.debug("Session transport closed gracefully: {}", sessionId);
+                }
+            });
+        }
+
+        @Override
+        public void close() {
+            if (!closed) {
+                closed = true;
+                removeSession(sessionId);
+                LOGGER.debug("Session transport closed immediately: {}", sessionId);
+            }
+        }
+
+        /**
+         * Clears the captured message and resets the response flag so that a subsequent
+         * business request is not confused with the internal initialize handshake that
+         * is executed during short-reconnect or temporary session creation.
+         */
+        public void resetCapturedMessage() {
+            this.lastSentMessage = null;
+            this.responseReady = false;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/StreamableHttpProviderBuilder.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/StreamableHttpProviderBuilder.java
new file mode 100644
index 0000000000..ff59ace50c
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/StreamableHttpProviderBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mcp.server.transport;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.util.Assert;
+
+/**
+ * Builder class for constructing instances of ShenyuStreamableHttpServerTransportProvider.
+ * This builder provides a fluent interface for configuring transport provider instances
+ * with proper validation and default values.
+ * Usage Example:
+ * ShenyuStreamableHttpServerTransportProvider provider =
+ * ShenyuStreamableHttpServerTransportProvider.builder()
+ * .objectMapper(new ObjectMapper())
+ * .endpoint("/mcp")
+ * .build();
+ */
+public class StreamableHttpProviderBuilder {
+
+    /**
+     * Default unified endpoint path for Streamable HTTP protocol.
+     */
+    private static final String DEFAULT_ENDPOINT = "/mcp";
+
+    private ObjectMapper objectMapper;
+
+    private String endpoint = DEFAULT_ENDPOINT;
+
+    /**
+     * Sets the ObjectMapper for JSON serialization/deserialization.
+     *
+     * @param objectMapper the ObjectMapper instance (required)
+     * @return this builder for method chaining
+     * @throws IllegalArgumentException if objectMapper is null
+     */
+    public StreamableHttpProviderBuilder objectMapper(final ObjectMapper objectMapper) {
+        Assert.notNull(objectMapper, "ObjectMapper must not be null");
+        this.objectMapper = objectMapper;
+        return this;
+    }
+
+    /**
+     * Sets the endpoint path for the transport provider.
+     *
+     * @param endpoint the endpoint path (defaults to "/mcp" if not specified)
+     * @return this builder for method chaining
+     * @throws IllegalArgumentException if endpoint is null
+     */
+    public StreamableHttpProviderBuilder endpoint(final String endpoint) {
+        Assert.notNull(endpoint, "Endpoint must not be null");
+        this.endpoint = endpoint;
+        return this;
+    }
+
+    /**
+     * Builds a new ShenyuStreamableHttpServerTransportProvider instance.
+     *
+     * @return the configured transport provider
+     * @throws IllegalStateException if required configuration is missing
+     */
+    public ShenyuStreamableHttpServerTransportProvider build() {
+        Assert.notNull(objectMapper, "ObjectMapper must be configured");
+        return new ShenyuStreamableHttpServerTransportProvider(objectMapper, endpoint);
+    }
+}
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
index 4c62efdd39..fee135e937 100644
--- a/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
+++ b/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/utils/JsonSchemaUtil.java
@@ -46,7 +46,7 @@ public final class JsonSchemaUtil {
      * @return the JSON schema as a string
      */
     public static String createParameterSchema(final List<McpServerToolParameter> parameters,
-            final SchemaOption... schemaOptions) {
+                                               final SchemaOption... schemaOptions) {
         if (CollectionUtils.isEmpty(parameters)) {
             return emptySchema(schemaOptions);
         }
@@ -58,7 +58,7 @@ public final class JsonSchemaUtil {
         ObjectNode properties = schema.putObject("properties");
         for (McpServerToolParameter parameter : parameters) {
             ObjectNode property = properties.putObject(parameter.getName());
-            property.put("type", "string");
+            property.put("type", parameter.getType());
             property.put("description", parameter.getDescription());
         }