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-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 4a58f3e9 Feat mcp streamable http (#536)
4a58f3e9 is described below

commit 4a58f3e9dc0b582c060baf5032d88776c803773c
Author: 有若旭 <[email protected]>
AuthorDate: Mon Jul 21 11:08:31 2025 +0800

    Feat mcp streamable http (#536)
    
    * [feat] mcp server plugin, Add streamable http support & Add json edit for 
Mcp plugin.
    
    * [feat] mcp server plugin, fix es lint for mcp plugin.
---
 src/locales/en-US.json                        | 103 ++-
 src/locales/zh-CN.json                        | 103 ++-
 src/routes/Plugin/McpServer/JsonEditModal.js  | 709 ++++++++++++++++++++
 src/routes/Plugin/McpServer/McpConfigModal.js | 465 +++++++++++++
 src/routes/Plugin/McpServer/ToolsModal.js     | 919 ++++++++++++++++++++------
 src/routes/Plugin/McpServer/index.js          | 144 +++-
 6 files changed, 2226 insertions(+), 217 deletions(-)

diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 538211ed..77895823 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -521,5 +521,106 @@
   "SHENYU.NAMESPACE.INPUTDESC":"description",
   "SHENYU.NAMESPACE.INPUTNAMESPACEID":"namespaceId",
   "SHENYU.NAMESPACE.ALERTNAMESPACEID":"Automatically generated namespaceId",
-  "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"Plugin"
+  "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"Plugin",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.WEIGHT": "Weight",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.WARMUP": "Warmup",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.GRAY": "Gray",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.STARTUP": "Startup",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.PROTOCOL": "Protocol",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.STATUS": "Status",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.URL": "URL",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.TIMESTAMP": "Timestamp",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.TOTAL": "Total",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.HEALTHY": "Healthy",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.UNHEALTHY": "Unhealthy",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.LISTEN": "Listen",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.HANDLER": "Handler",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SELECTED": "Selected",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SELECTED.TOTAL": "Selected Total",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DETECTED": "Detected",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DETECTED.TOTAL": "Detected Total",
+  "SHENYU.DISCOVERY.CONFIG.DISCOVERYTYPE": "Discovery Type",
+  "SHENYU.DISCOVERY.CONFIG.SERVERLIST": "Server List",
+  "SHENYU.DISCOVERY.CONFIG.PROPS": "Properties",
+  "SHENYU.DISCOVERY.CONFIG.LISTENERNODE": "Listener Node",
+  "SHENYU.DISCOVERY.CONFIG.HANDLER": "Handler",
+  "SHENYU.DISCOVERY.CONFIG.PROPS.PLACEHOLDER": "Please enter properties (JSON 
format)",
+  "SHENYU.DISCOVERY.CONFIG.SERVERLIST.PLACEHOLDER": "Please enter server list",
+  "SHENYU.DISCOVERY.CONFIG.LISTENERNODE.PLACEHOLDER": "Please enter listener 
node",
+  "SHENYU.DISCOVERY.CONFIG.HANDLER.PLACEHOLDER": "Please enter handler (JSON 
format)",
+  "SHENYU.DISCOVERY.CONFIG.DISCOVERYTYPE.PLACEHOLDER": "Please select 
discovery type",
+  "SHENYU.MCP.JSON.EDIT.TITLE": "JSON Edit Handle Configuration",
+  "SHENYU.MCP.JSON.EDIT.DESCRIPTION": "You can directly edit the JSON 
configuration of the Handle field. Support copy and paste the entire JSON 
text.",
+  "SHENYU.MCP.JSON.EDIT.FORMAT": "Format",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS": "Compress",
+  "SHENYU.MCP.JSON.EDIT.COPY": "Copy All",
+  "SHENYU.MCP.JSON.EDIT.TAB.TEXT": "Text Edit",
+  "SHENYU.MCP.JSON.EDIT.TAB.PREVIEW": "Visual Preview",
+  "SHENYU.MCP.JSON.EDIT.ERROR.PREFIX": "Format Error: ",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.TITLE": "Handle Field Structure Description:",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.PARAMETERS": "Tool parameter list, including 
name, type, description and other fields",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.REQUESTCONFIG": "Request configuration 
information, usually in JSON string format",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.DESCRIPTION": "Detailed description of the 
tool",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.TITLE": "Operation Instructions:",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.KEYBOARD": "Support Ctrl+A to select all, 
Ctrl+C to copy, Ctrl+V to paste Handle configuration",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.SWITCH": "You can switch between text 
editing and visual preview",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.VALIDATE": "JSON format will be 
automatically validated before saving",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.ONLY.HANDLE": "Only the Handle field will be 
updated, other tool information remains unchanged",
+  "SHENYU.MCP.JSON.EDIT.PLACEHOLDER": "Please enter Handle configuration in 
JSON format",
+  "SHENYU.MCP.JSON.EDIT.EMPTY.ERROR": "Handle configuration cannot be empty",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.ERROR": "JSON Format Error: ",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.SUCCESS": "JSON formatted successfully",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.FAILED": "JSON format is incorrect and cannot 
be formatted",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS.SUCCESS": "JSON compressed successfully",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS.FAILED": "JSON format is incorrect and cannot 
be compressed",
+  "SHENYU.MCP.JSON.EDIT.COPY.SUCCESS": "Copied to clipboard",
+  "SHENYU.MCP.JSON.EDIT.COPY.FAILED": "Copy failed",
+  "SHENYU.MCP.JSON.EDIT.UPDATE.SUCCESS": "Tool data updated successfully",
+  "SHENYU.MCP.EDIT.JSON": "EditJSON",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME": "Tool Name",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME.PLACEHOLDER": "Please enter tool name",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME.ERROR": "Tool name cannot be empty",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.FIELD": "Handle Configuration",
+  "SHENYU.MCP.JSON.EDIT.MODE.SEPARATE": "Separate Edit",
+  "SHENYU.MCP.JSON.EDIT.MODE.UNIFIED": "Unified Edit",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.TITLE": "Unified JSON Edit",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.DESCRIPTION": "You can directly edit the 
complete JSON data including tool name and Handle configuration.",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.TITLE": "Complete Data Structure 
Description:",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.NAME": "Tool's name identifier",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.HANDLE": "Tool's complete Handle 
configuration, including parameters, requestConfig, etc.",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.PLACEHOLDER": "Please enter complete JSON 
configuration containing name and handle fields",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.UNIFIED.UPDATE": "Update both tool name and 
Handle fields simultaneously",
+  "SHENYU.MCP.TOOLS.ADD.MODE.FORM": "Form Edit",
+  "SHENYU.MCP.TOOLS.ADD.MODE.JSON": "JSON Edit",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TITLE": "JSON Add Tool",
+  "SHENYU.MCP.TOOLS.ADD.JSON.DESCRIPTION": "You can directly edit the complete 
tool JSON configuration containing all necessary fields.",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.TITLE": "Complete Tool Data Structure 
Description:",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.NAME": "Tool's name identifier",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.DESCRIPTION": "Tool's detailed 
description",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.ENABLED": "Whether to enable this tool",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.HANDLE": "Tool's complete Handle 
configuration, including parameters, requestConfig, etc.",
+  "SHENYU.MCP.TOOLS.ADD.JSON.PLACEHOLDER": "Please enter complete tool JSON 
configuration containing name, description, enabled, parameters, requestConfig 
and other fields",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE": "Generate Template",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE.SUCCESS": "Tool template generated 
successfully",
+  "SHENYU.MCP.TOOLS.ADD.JSON.OPERATION.TEMPLATE": "Support one-click template 
generation with complete tool structure examples",
+  "SHENYU.MCP.CONFIG.SSE": "SSE Config",
+  "SHENYU.MCP.CONFIG.STREAMABLE": "Streamable Config",
+  "SHENYU.MCP.CONFIG.SSE.TITLE": "SSE Protocol MCP Service Configuration",
+  "SHENYU.MCP.CONFIG.STREAMABLE.TITLE": "Streamable HTTP Protocol MCP Service 
Configuration",
+  "SHENYU.MCP.CONFIG.DESCRIPTION": "The following configuration can be used 
for MCP client integration with ShenYu gateway",
+  "SHENYU.MCP.CONFIG.SERVICE.NAME": "Service Name",
+  "SHENYU.MCP.CONFIG.SERVICE.DESCRIPTION": "Service Description",
+  "SHENYU.MCP.CONFIG.SERVICE.URL": "Service URL",
+  "SHENYU.MCP.CONFIG.SERVICE.HEADERS": "Request Headers",
+  "SHENYU.MCP.CONFIG.SERVICE.TRANSPORT": "Transport Protocol",
+  "SHENYU.MCP.CONFIG.COPY.SUCCESS": "Configuration copied to clipboard",
+  "SHENYU.MCP.CONFIG.COPY.FAILED": "Failed to copy configuration",
+  "SHENYU.MCP.CONFIG.EXPLANATION.TITLE": "Configuration Description:",
+  "SHENYU.MCP.CONFIG.EXPLANATION.URL": "MCP service access address, generated 
based on selector rules",
+  "SHENYU.MCP.CONFIG.EXPLANATION.NAME": "Service display name",
+  "SHENYU.MCP.CONFIG.EXPLANATION.DESCRIPTION": "Service detailed description",
+  "SHENYU.MCP.CONFIG.EXPLANATION.HEADERS": "HTTP headers required for 
requests. X-Client-ID and Authorization should be filled according to actual 
situation",
+  "SHENYU.MCP.CONFIG.EXPLANATION.TRANSPORT": "Transport protocol type",
+  "SHENYU.MCP.CONFIG.JSON.TITLE": "JSON Configuration (Copy Ready):",
+  "SHENYU.MCP.CONFIG.COPY.JSON": "Copy JSON"
 }
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index 997ab6db..fcf4e881 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -527,5 +527,106 @@
   "SHENYU.NAMESPACE.INPUTDESC":"请输入命名空间描述",
   "SHENYU.NAMESPACE.INPUTNAMESPACEID":"请输入namespaceId",
   "SHENYU.NAMESPACE.ALERTNAMESPACEID":"系统自动生成namespaceId",
-  "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"插件管理"
+  "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACEPLUGIN":"插件管理",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.WEIGHT": "权重",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.WARMUP": "热身",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.GRAY": "灰度",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.STARTUP": "启动",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.PROTOCOL": "协议",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.STATUS": "状态",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.URL": "URL",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.TIMESTAMP": "时间戳",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.TOTAL": "总数",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.HEALTHY": "健康",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.UNHEALTHY": "不健康",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.LISTEN": "监听",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.HANDLER": "处理器",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SELECTED": "被选中",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.SELECTED.TOTAL": "被选中总数",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DETECTED": "检测到",
+  "SHENYU.DISCOVERY.SELECTOR.UPSTREAM.DETECTED.TOTAL": "检测到总数",
+  "SHENYU.DISCOVERY.CONFIG.DISCOVERYTYPE": "发现类型",
+  "SHENYU.DISCOVERY.CONFIG.SERVERLIST": "服务器列表",
+  "SHENYU.DISCOVERY.CONFIG.PROPS": "属性",
+  "SHENYU.DISCOVERY.CONFIG.LISTENERNODE": "监听节点",
+  "SHENYU.DISCOVERY.CONFIG.HANDLER": "处理器",
+  "SHENYU.DISCOVERY.CONFIG.PROPS.PLACEHOLDER": "请输入属性(JSON 格式)",
+  "SHENYU.DISCOVERY.CONFIG.SERVERLIST.PLACEHOLDER": "请输入服务器列表",
+  "SHENYU.DISCOVERY.CONFIG.LISTENERNODE.PLACEHOLDER": "请输入监听节点",
+  "SHENYU.DISCOVERY.CONFIG.HANDLER.PLACEHOLDER": "请输入处理器(JSON 格式)",
+  "SHENYU.DISCOVERY.CONFIG.DISCOVERYTYPE.PLACEHOLDER": "请选择发现类型",
+  "SHENYU.MCP.JSON.EDIT.TITLE": "JSON编辑Handle配置",
+  "SHENYU.MCP.JSON.EDIT.DESCRIPTION": "您可以直接编辑Handle字段的JSON配置。支持复制粘贴整个JSON文本。",
+  "SHENYU.MCP.JSON.EDIT.FORMAT": "格式化",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS": "压缩",
+  "SHENYU.MCP.JSON.EDIT.COPY": "复制全部",
+  "SHENYU.MCP.JSON.EDIT.TAB.TEXT": "文本编辑",
+  "SHENYU.MCP.JSON.EDIT.TAB.PREVIEW": "可视化预览",
+  "SHENYU.MCP.JSON.EDIT.ERROR.PREFIX": "格式错误: ",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.TITLE": "Handle字段结构说明:",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.PARAMETERS": 
"工具参数列表,包含name、type、description等字段",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.REQUESTCONFIG": "请求配置信息,通常是JSON字符串格式",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.DESCRIPTION": "工具的详细描述信息",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.TITLE": "操作说明:",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.KEYBOARD": 
"支持Ctrl+A全选,Ctrl+C复制,Ctrl+V粘贴Handle配置",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.SWITCH": "可以在文本编辑和可视化预览之间切换查看",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.VALIDATE": "保存前会自动验证JSON格式的正确性",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.ONLY.HANDLE": "只会更新Handle字段,其他工具信息保持不变",
+  "SHENYU.MCP.JSON.EDIT.PLACEHOLDER": "请输入JSON格式的Handle配置",
+  "SHENYU.MCP.JSON.EDIT.EMPTY.ERROR": "Handle配置不能为空",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.ERROR": "JSON格式错误: ",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.SUCCESS": "JSON格式化成功",
+  "SHENYU.MCP.JSON.EDIT.FORMAT.FAILED": "JSON格式不正确,无法格式化",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS.SUCCESS": "JSON压缩成功",
+  "SHENYU.MCP.JSON.EDIT.COMPRESS.FAILED": "JSON格式不正确,无法压缩",
+  "SHENYU.MCP.JSON.EDIT.COPY.SUCCESS": "已复制到剪贴板",
+  "SHENYU.MCP.JSON.EDIT.COPY.FAILED": "复制失败",
+  "SHENYU.MCP.JSON.EDIT.UPDATE.SUCCESS": "工具数据更新成功",
+  "SHENYU.MCP.EDIT.JSON": "EditJSON",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME": "工具名称",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME.PLACEHOLDER": "请输入工具名称",
+  "SHENYU.MCP.JSON.EDIT.TOOL.NAME.ERROR": "工具名称不能为空",
+  "SHENYU.MCP.JSON.EDIT.HANDLE.FIELD": "Handle配置",
+  "SHENYU.MCP.JSON.EDIT.MODE.SEPARATE": "分别编辑",
+  "SHENYU.MCP.JSON.EDIT.MODE.UNIFIED": "统一编辑",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.TITLE": "统一JSON编辑",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.DESCRIPTION": 
"您可以直接编辑包含工具名称和Handle配置的完整JSON数据。",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.TITLE": "完整数据结构说明:",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.NAME": "工具的名称标识",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.HANDLE": 
"工具的完整Handle配置,包含parameters、requestConfig等",
+  "SHENYU.MCP.JSON.EDIT.UNIFIED.PLACEHOLDER": "请输入包含name和handle字段的完整JSON配置",
+  "SHENYU.MCP.JSON.EDIT.OPERATION.UNIFIED.UPDATE": "同时更新工具名称和Handle字段",
+  "SHENYU.MCP.TOOLS.ADD.MODE.FORM": "表单编辑",
+  "SHENYU.MCP.TOOLS.ADD.MODE.JSON": "JSON编辑",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TITLE": "JSON添加工具",
+  "SHENYU.MCP.TOOLS.ADD.JSON.DESCRIPTION": "您可以直接编辑完整的工具JSON配置,包含所有必要字段。",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.TITLE": "完整工具数据结构说明:",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.NAME": "工具的名称标识",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.DESCRIPTION": "工具的详细描述",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.ENABLED": "是否启用该工具",
+  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.HANDLE": 
"工具的完整Handle配置,包含parameters、requestConfig等",
+  "SHENYU.MCP.TOOLS.ADD.JSON.PLACEHOLDER": 
"请输入包含name、description、enabled、parameters、requestConfig等字段的完整工具JSON配置",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE": "生成模板",
+  "SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE.SUCCESS": "已生成工具模板",
+  "SHENYU.MCP.TOOLS.ADD.JSON.OPERATION.TEMPLATE": "支持一键生成模板,包含完整的工具结构示例",
+  "SHENYU.MCP.CONFIG.SSE": "SSE配置",
+  "SHENYU.MCP.CONFIG.STREAMABLE": "Streamable配置",
+  "SHENYU.MCP.CONFIG.SSE.TITLE": "SSE协议MCP服务配置",
+  "SHENYU.MCP.CONFIG.STREAMABLE.TITLE": "Streamable HTTP协议MCP服务配置",
+  "SHENYU.MCP.CONFIG.DESCRIPTION": "以下配置可用于MCP客户端与ShenYu网关的集成",
+  "SHENYU.MCP.CONFIG.SERVICE.NAME": "服务名称",
+  "SHENYU.MCP.CONFIG.SERVICE.DESCRIPTION": "服务描述",
+  "SHENYU.MCP.CONFIG.SERVICE.URL": "服务URL",
+  "SHENYU.MCP.CONFIG.SERVICE.HEADERS": "请求头",
+  "SHENYU.MCP.CONFIG.SERVICE.TRANSPORT": "传输协议",
+  "SHENYU.MCP.CONFIG.COPY.SUCCESS": "配置已复制到剪贴板",
+  "SHENYU.MCP.CONFIG.COPY.FAILED": "复制配置失败",
+  "SHENYU.MCP.CONFIG.EXPLANATION.TITLE": "配置说明:",
+  "SHENYU.MCP.CONFIG.EXPLANATION.URL": "MCP服务访问地址,根据选择器规则生成",
+  "SHENYU.MCP.CONFIG.EXPLANATION.NAME": "服务显示名称",
+  "SHENYU.MCP.CONFIG.EXPLANATION.DESCRIPTION": "服务详细描述",
+  "SHENYU.MCP.CONFIG.EXPLANATION.HEADERS": 
"请求时需要携带的HTTP头信息。X-Client-ID和Authorization需要根据实际情况填写",
+  "SHENYU.MCP.CONFIG.EXPLANATION.TRANSPORT": "传输协议类型",
+  "SHENYU.MCP.CONFIG.JSON.TITLE": "JSON配置(可直接复制):",
+  "SHENYU.MCP.CONFIG.COPY.JSON": "复制JSON"
 }
diff --git a/src/routes/Plugin/McpServer/JsonEditModal.js 
b/src/routes/Plugin/McpServer/JsonEditModal.js
new file mode 100644
index 00000000..86ef1673
--- /dev/null
+++ b/src/routes/Plugin/McpServer/JsonEditModal.js
@@ -0,0 +1,709 @@
+/*
+ * 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.
+ */
+
+import React, { Component } from "react";
+import { Button, Modal, message, Input, Row, Col, Tabs, Radio } from "antd";
+import ReactJson from "react-json-view";
+import { getIntlContent } from "../../../utils/IntlUtils";
+
+const { TextArea } = Input;
+const { TabPane } = Tabs;
+
+class JsonEditModal extends Component {
+  constructor(props) {
+    super(props);
+
+    // 解析handle字段,提升到最外层
+    let flattenedData = {};
+    let flattenedText = "{}";
+    let toolName = "";
+    let unifiedText = "{}";
+
+    if (props.data) {
+      toolName = props.data.name || "";
+
+      // 将handle内容提升到最外层
+      if (props.data.handle) {
+        try {
+          const handleObj = JSON.parse(props.data.handle);
+          flattenedData = {
+            name: toolName,
+            parameters: handleObj.parameters || [],
+            requestConfig: handleObj.requestConfig || "{}",
+            description: handleObj.description || "",
+          };
+          flattenedText = JSON.stringify(flattenedData, null, 2);
+        } catch (e) {
+          // Failed to parse handle JSON
+          flattenedData = {
+            name: toolName,
+            parameters: [],
+            requestConfig: "{}",
+            description: "",
+          };
+          flattenedText = JSON.stringify(flattenedData, null, 2);
+        }
+      } else {
+        flattenedData = {
+          name: toolName,
+          parameters: [],
+          requestConfig: "{}",
+          description: "",
+        };
+        flattenedText = JSON.stringify(flattenedData, null, 2);
+      }
+    }
+
+    // 统一编辑模式使用相同的扁平化数据
+    unifiedText = flattenedText;
+
+    this.state = {
+      originalData: props.data || {},
+      toolName,
+      flattenedText,
+      unifiedText,
+      editMode: "separate", // "separate" 或 "unified"
+      activeTab: "1",
+      parseError: null,
+      unifiedParseError: null,
+      toolNameError: null,
+    };
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.data !== this.props.data && this.props.data) {
+      let flattenedData = {};
+      let flattenedText = "{}";
+      let toolName = "";
+      let unifiedText = "{}";
+
+      toolName = this.props.data.name || "";
+
+      // 将handle内容提升到最外层
+      if (this.props.data.handle) {
+        try {
+          const handleObj = JSON.parse(this.props.data.handle);
+          flattenedData = {
+            name: toolName,
+            parameters: handleObj.parameters || [],
+            requestConfig: handleObj.requestConfig || "{}",
+            description: handleObj.description || "",
+          };
+          flattenedText = JSON.stringify(flattenedData, null, 2);
+        } catch (e) {
+          flattenedData = {
+            name: toolName,
+            parameters: [],
+            requestConfig: "{}",
+            description: "",
+          };
+          flattenedText = JSON.stringify(flattenedData, null, 2);
+        }
+      } else {
+        flattenedData = {
+          name: toolName,
+          parameters: [],
+          requestConfig: "{}",
+          description: "",
+        };
+        flattenedText = JSON.stringify(flattenedData, null, 2);
+      }
+
+      // 统一编辑模式使用相同的扁平化数据
+      unifiedText = flattenedText;
+
+      this.setState({
+        originalData: { ...this.props.data },
+        toolName,
+        flattenedText,
+        unifiedText,
+        parseError: null,
+        unifiedParseError: null,
+        toolNameError: null,
+      });
+    }
+  }
+
+  handleToolNameChange = (e) => {
+    const toolName = e.target.value;
+    this.setState({
+      toolName,
+      toolNameError: null,
+    });
+  };
+
+  handleTextChange = (e) => {
+    const flattenedText = e.target.value;
+    this.setState({ flattenedText });
+
+    // 实时验证JSON格式
+    try {
+      JSON.parse(flattenedText);
+      this.setState({ parseError: null });
+    } catch (error) {
+      this.setState({ parseError: error.message });
+    }
+  };
+
+  handleUnifiedTextChange = (e) => {
+    const unifiedText = e.target.value;
+    this.setState({ unifiedText });
+
+    // 实时验证JSON格式
+    try {
+      const parsed = JSON.parse(unifiedText);
+      this.setState({ unifiedParseError: null });
+
+      // 同步更新分别编辑模式的数据
+      if (parsed.name !== undefined) {
+        this.setState({ toolName: parsed.name });
+      }
+
+      // 更新扁平化文本
+      this.setState({
+        flattenedText: JSON.stringify(parsed, null, 2),
+        parseError: null,
+      });
+    } catch (error) {
+      this.setState({ unifiedParseError: error.message });
+    }
+  };
+
+  handleEditModeChange = (e) => {
+    const editMode = e.target.value;
+    this.setState({ editMode });
+
+    // 切换到统一编辑模式时,同步数据
+    if (editMode === "unified") {
+      try {
+        const flattenedJson = JSON.parse(this.state.flattenedText);
+        // 确保toolName被包含在JSON中
+        flattenedJson.name = this.state.toolName;
+        const unifiedText = JSON.stringify(flattenedJson, null, 2);
+        this.setState({
+          unifiedText,
+          unifiedParseError: null,
+        });
+      } catch (error) {
+        // 如果解析失败,使用基础模板
+        const basicJson = {
+          name: this.state.toolName,
+          parameters: [],
+          requestConfig: "{}",
+          description: "",
+        };
+        const unifiedText = JSON.stringify(basicJson, null, 2);
+        this.setState({
+          unifiedText,
+          unifiedParseError: null,
+        });
+      }
+    }
+  };
+
+  handleFormatJson = () => {
+    const { editMode } = this.state;
+
+    if (editMode === "separate") {
+      try {
+        const parsed = JSON.parse(this.state.flattenedText);
+        const formatted = JSON.stringify(parsed, null, 2);
+        this.setState({
+          flattenedText: formatted,
+          parseError: null,
+        });
+        message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.SUCCESS"));
+      } catch (error) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.FAILED"));
+      }
+    } else {
+      try {
+        const parsed = JSON.parse(this.state.unifiedText);
+        const formatted = JSON.stringify(parsed, null, 2);
+        this.setState({
+          unifiedText: formatted,
+          unifiedParseError: null,
+        });
+        message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.SUCCESS"));
+      } catch (error) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.FAILED"));
+      }
+    }
+  };
+
+  handleCompressJson = () => {
+    const { editMode } = this.state;
+
+    if (editMode === "separate") {
+      try {
+        const parsed = JSON.parse(this.state.flattenedText);
+        const compressed = JSON.stringify(parsed);
+        this.setState({
+          flattenedText: compressed,
+          parseError: null,
+        });
+        message.success(
+          getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.SUCCESS"),
+        );
+      } catch (error) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.FAILED"));
+      }
+    } else {
+      try {
+        const parsed = JSON.parse(this.state.unifiedText);
+        const compressed = JSON.stringify(parsed);
+        this.setState({
+          unifiedText: compressed,
+          unifiedParseError: null,
+        });
+        message.success(
+          getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.SUCCESS"),
+        );
+      } catch (error) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.FAILED"));
+      }
+    }
+  };
+
+  handleCopyToClipboard = () => {
+    const { editMode } = this.state;
+    const textToCopy =
+      editMode === "separate"
+        ? this.state.flattenedText
+        : this.state.unifiedText;
+
+    navigator.clipboard
+      .writeText(textToCopy)
+      .then(() => {
+        message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.COPY.SUCCESS"));
+      })
+      .catch(() => {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.COPY.FAILED"));
+      });
+  };
+
+  handleOk = () => {
+    const { editMode, toolName, flattenedText, unifiedText, originalData } =
+      this.state;
+    const { onOk } = this.props;
+
+    let finalJsonData = {};
+
+    if (editMode === "separate") {
+      // 分别编辑模式验证
+      if (!toolName.trim()) {
+        this.setState({
+          toolNameError: 
getIntlContent("SHENYU.MCP.JSON.EDIT.TOOL.NAME.ERROR"),
+        });
+        return;
+      }
+
+      if (!flattenedText.trim()) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.EMPTY.ERROR"));
+        return;
+      }
+
+      try {
+        const parsed = JSON.parse(flattenedText);
+        // 确保name字段是从toolName输入框获取的
+        finalJsonData = {
+          ...parsed,
+          name: toolName,
+        };
+      } catch (error) {
+        message.error(
+          
`${getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.ERROR")}${error.message}`,
+        );
+        return;
+      }
+    } else {
+      // 统一编辑模式验证
+      if (!unifiedText.trim()) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.EMPTY.ERROR"));
+        return;
+      }
+
+      try {
+        finalJsonData = JSON.parse(unifiedText);
+
+        if (!finalJsonData.name || !finalJsonData.name.trim()) {
+          
message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.TOOL.NAME.ERROR"));
+          return;
+        }
+      } catch (error) {
+        message.error(
+          
`${getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.ERROR")}${error.message}`,
+        );
+        return;
+      }
+    }
+
+    // 验证必要字段
+    if (!finalJsonData.parameters) {
+      finalJsonData.parameters = [];
+    }
+    if (!finalJsonData.requestConfig) {
+      finalJsonData.requestConfig = "{}";
+    }
+    if (!finalJsonData.description) {
+      finalJsonData.description = "";
+    }
+
+    // 将扁平化数据转换回原始格式
+    const updatedData = {
+      ...originalData,
+      name: finalJsonData.name,
+      handle: JSON.stringify({
+        parameters: finalJsonData.parameters,
+        requestConfig: finalJsonData.requestConfig,
+        description: finalJsonData.description,
+      }),
+    };
+
+    onOk(updatedData);
+  };
+
+  handleCancel = () => {
+    const { onCancel } = this.props;
+    onCancel();
+  };
+
+  render() {
+    const { visible } = this.props;
+    const {
+      toolName,
+      flattenedText,
+      unifiedText,
+      editMode,
+      parseError,
+      unifiedParseError,
+      toolNameError,
+      activeTab,
+    } = this.state;
+
+    // 用于预览的JSON对象
+    let previewJson = {};
+    let unifiedPreviewJson = {};
+
+    try {
+      previewJson = JSON.parse(flattenedText);
+    } catch (e) {
+      previewJson = {
+        error: getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX") + e.message,
+      };
+    }
+
+    try {
+      unifiedPreviewJson = JSON.parse(unifiedText);
+    } catch (e) {
+      unifiedPreviewJson = {
+        error: getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX") + e.message,
+      };
+    }
+
+    const modalTitle =
+      editMode === "separate"
+        ? getIntlContent("SHENYU.MCP.JSON.EDIT.TITLE")
+        : getIntlContent("SHENYU.MCP.JSON.EDIT.UNIFIED.TITLE");
+
+    return (
+      <Modal
+        title={modalTitle}
+        visible={visible}
+        onOk={this.handleOk}
+        onCancel={this.handleCancel}
+        width={900}
+        okText={getIntlContent("SHENYU.COMMON.SURE")}
+        cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
+        bodyStyle={{ padding: "20px" }}
+      >
+        <div style={{ marginBottom: "16px" }}>
+          <div style={{ marginBottom: "12px" }}>
+            <Radio.Group value={editMode} onChange={this.handleEditModeChange}>
+              <Radio.Button value="separate">
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.MODE.SEPARATE")}
+              </Radio.Button>
+              <Radio.Button value="unified">
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.MODE.UNIFIED")}
+              </Radio.Button>
+            </Radio.Group>
+          </div>
+
+          <p style={{ color: "#666", fontSize: "12px", marginBottom: "8px" }}>
+            {editMode === "separate"
+              ? getIntlContent("SHENYU.MCP.JSON.EDIT.DESCRIPTION")
+              : getIntlContent("SHENYU.MCP.JSON.EDIT.UNIFIED.DESCRIPTION")}
+          </p>
+
+          <Row gutter={8}>
+            <Col>
+              <Button size="small" onClick={this.handleFormatJson}>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT")}
+              </Button>
+            </Col>
+            <Col>
+              <Button size="small" onClick={this.handleCompressJson}>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS")}
+              </Button>
+            </Col>
+            <Col>
+              <Button size="small" onClick={this.handleCopyToClipboard}>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.COPY")}
+              </Button>
+            </Col>
+          </Row>
+        </div>
+
+        {editMode === "separate" ? (
+          <Tabs
+            activeKey={activeTab}
+            onChange={(key) => this.setState({ activeTab: key })}
+          >
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.TEXT")}
+              key="1"
+            >
+              <div style={{ marginBottom: "16px" }}>
+                <div style={{ marginBottom: "8px", fontWeight: "bold" }}>
+                  {getIntlContent("SHENYU.MCP.JSON.EDIT.TOOL.NAME")}
+                </div>
+                <Input
+                  value={toolName}
+                  onChange={this.handleToolNameChange}
+                  placeholder={getIntlContent(
+                    "SHENYU.MCP.JSON.EDIT.TOOL.NAME.PLACEHOLDER",
+                  )}
+                  style={{ marginBottom: "8px" }}
+                />
+                {toolNameError && (
+                  <div
+                    style={{
+                      color: "#ff4d4f",
+                      fontSize: "12px",
+                      marginBottom: "8px",
+                    }}
+                  >
+                    {toolNameError}
+                  </div>
+                )}
+              </div>
+
+              <div style={{ marginBottom: "8px", fontWeight: "bold" }}>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.FIELD")}
+              </div>
+              <div style={{ position: "relative" }}>
+                <TextArea
+                  value={flattenedText}
+                  onChange={this.handleTextChange}
+                  placeholder={getIntlContent(
+                    "SHENYU.MCP.JSON.EDIT.UNIFIED.PLACEHOLDER",
+                  )}
+                  autoSize={{ minRows: 12, maxRows: 20 }}
+                  style={{
+                    fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
+                    fontSize: "13px",
+                    lineHeight: "1.4",
+                  }}
+                />
+                {parseError && (
+                  <div
+                    style={{
+                      position: "absolute",
+                      bottom: "8px",
+                      right: "8px",
+                      background: "#ff4d4f",
+                      color: "white",
+                      padding: "4px 8px",
+                      borderRadius: "4px",
+                      fontSize: "12px",
+                      maxWidth: "300px",
+                      wordBreak: "break-word",
+                    }}
+                  >
+                    {getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX")}
+                    {parseError}
+                  </div>
+                )}
+              </div>
+            </TabPane>
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.PREVIEW")}
+              key="2"
+            >
+              <div style={{ marginBottom: "16px" }}>
+                <div style={{ marginBottom: "8px", fontWeight: "bold" }}>
+                  {getIntlContent("SHENYU.MCP.JSON.EDIT.TOOL.NAME")}
+                </div>
+                <div
+                  style={{
+                    padding: "8px 12px",
+                    backgroundColor: "#f5f5f5",
+                    borderRadius: "4px",
+                    fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
+                    fontSize: "13px",
+                  }}
+                >
+                  {toolName ||
+                    getIntlContent(
+                      "SHENYU.MCP.JSON.EDIT.TOOL.NAME.PLACEHOLDER",
+                    )}
+                </div>
+              </div>
+
+              <div style={{ marginBottom: "8px", fontWeight: "bold" }}>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.FIELD")}
+              </div>
+              <div
+                style={{
+                  border: "1px solid #d9d9d9",
+                  borderRadius: "4px",
+                  maxHeight: "400px",
+                  overflow: "auto",
+                }}
+              >
+                <ReactJson
+                  src={previewJson}
+                  theme="monokai"
+                  displayDataTypes={false}
+                  displayObjectSize={false}
+                  name={false}
+                  enableClipboard={false}
+                  style={{
+                    padding: "16px",
+                    fontSize: "13px",
+                  }}
+                />
+              </div>
+            </TabPane>
+          </Tabs>
+        ) : (
+          <Tabs
+            activeKey={activeTab}
+            onChange={(key) => this.setState({ activeTab: key })}
+          >
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.TEXT")}
+              key="1"
+            >
+              <div style={{ position: "relative" }}>
+                <TextArea
+                  value={unifiedText}
+                  onChange={this.handleUnifiedTextChange}
+                  placeholder={getIntlContent(
+                    "SHENYU.MCP.JSON.EDIT.UNIFIED.PLACEHOLDER",
+                  )}
+                  autoSize={{ minRows: 15, maxRows: 25 }}
+                  style={{
+                    fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
+                    fontSize: "13px",
+                    lineHeight: "1.4",
+                  }}
+                />
+                {unifiedParseError && (
+                  <div
+                    style={{
+                      position: "absolute",
+                      bottom: "8px",
+                      right: "8px",
+                      background: "#ff4d4f",
+                      color: "white",
+                      padding: "4px 8px",
+                      borderRadius: "4px",
+                      fontSize: "12px",
+                      maxWidth: "300px",
+                      wordBreak: "break-word",
+                    }}
+                  >
+                    {getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX")}
+                    {unifiedParseError}
+                  </div>
+                )}
+              </div>
+            </TabPane>
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.PREVIEW")}
+              key="2"
+            >
+              <div
+                style={{
+                  border: "1px solid #d9d9d9",
+                  borderRadius: "4px",
+                  maxHeight: "400px",
+                  overflow: "auto",
+                }}
+              >
+                <ReactJson
+                  src={unifiedPreviewJson}
+                  theme="monokai"
+                  displayDataTypes={false}
+                  displayObjectSize={false}
+                  name={false}
+                  enableClipboard={false}
+                  style={{
+                    padding: "16px",
+                    fontSize: "13px",
+                  }}
+                />
+              </div>
+            </TabPane>
+          </Tabs>
+        )}
+
+        <div style={{ marginTop: "16px", color: "#666", fontSize: "12px" }}>
+          <p>
+            <strong>
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.TITLE")}
+            </strong>
+          </p>
+          <ul>
+            <li>
+              <code>name</code>:{" "}
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.UNIFIED.STRUCTURE.NAME")}
+            </li>
+            <li>
+              <code>parameters</code>:{" "}
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.PARAMETERS")}
+            </li>
+            <li>
+              <code>requestConfig</code>:{" "}
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.REQUESTCONFIG")}
+            </li>
+            <li>
+              <code>description</code>:{" "}
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.DESCRIPTION")}
+            </li>
+          </ul>
+          <p>
+            <strong>
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.TITLE")}
+            </strong>
+          </p>
+          <ul>
+            
<li>{getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.KEYBOARD")}</li>
+            <li>{getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.SWITCH")}</li>
+            
<li>{getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.VALIDATE")}</li>
+            <li>
+              {getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.UNIFIED.UPDATE")}
+            </li>
+          </ul>
+        </div>
+      </Modal>
+    );
+  }
+}
+
+export default JsonEditModal;
diff --git a/src/routes/Plugin/McpServer/McpConfigModal.js 
b/src/routes/Plugin/McpServer/McpConfigModal.js
new file mode 100644
index 00000000..a0866519
--- /dev/null
+++ b/src/routes/Plugin/McpServer/McpConfigModal.js
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ */
+
+import React, { Component } from "react";
+import { Modal, Button, message, Typography, Divider, Input } from "antd";
+import ReactJson from "react-json-view";
+import { getIntlContent } from "../../../utils/IntlUtils";
+
+const { Title, Text } = Typography;
+const { TextArea } = Input;
+
+class McpConfigModal extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      sseConfig: {},
+      streamableConfig: {},
+      customGatewayHost: "", // 用户自定义的网关地址
+    };
+  }
+
+  componentDidMount() {
+    this.generateConfigs();
+  }
+
+  componentDidUpdate(prevProps) {
+    if (prevProps.selectorList !== this.props.selectorList) {
+      this.generateConfigs();
+    }
+  }
+
+  // 生成配置信息
+  generateConfigs = () => {
+    const { selectorList } = this.props;
+
+    if (!selectorList || selectorList.length === 0) {
+      this.setState({
+        sseConfig: this.getDefaultConfig("sse"),
+        streamableConfig: this.getDefaultConfig("streamableHttp"),
+      });
+      return;
+    }
+
+    // 获取网关基础信息
+    const gatewayHost = this.getGatewayHost();
+    const defaultHeaders = this.getDefaultHeaders();
+
+    // 为每种协议类型生成配置
+    const mcpServers = {};
+
+    selectorList.forEach((selector, index) => {
+      const selectorName = selector.name || `selector-${index}`;
+      const selectorDescription = this.getHandleDescription(selector.handle);
+      const selectorUrl = this.getSelectorUrl(selector, gatewayHost);
+
+      // SSE 配置
+      const sseKey = `shenyu-mcp-sse-${selectorName}`;
+      mcpServers[sseKey] = {
+        url: `${selectorUrl}/sse`,
+        name: `${selectorName}服务sse`,
+        description: `${selectorName}服务测试sse - ${selectorDescription}`,
+        headers: defaultHeaders,
+        transport: "sse",
+      };
+
+      // Streamable HTTP 配置
+      const streamableKey = `shenyu-mcp-${selectorName}`;
+      mcpServers[streamableKey] = {
+        url: `${selectorUrl}/streamablehttp`,
+        name: `${selectorName}服务`,
+        description: `${selectorName}服务测试 - ${selectorDescription}`,
+        headers: defaultHeaders,
+        transport: "streamableHttp",
+      };
+    });
+
+    this.setState({
+      sseConfig: { mcpServers: this.filterByTransport(mcpServers, "sse") },
+      streamableConfig: {
+        mcpServers: this.filterByTransport(mcpServers, "streamableHttp"),
+      },
+    });
+  };
+
+  // 获取网关主机地址
+  getGatewayHost = () => {
+    // 优先使用用户自定义的网关地址
+    if (this.state.customGatewayHost && this.state.customGatewayHost.trim()) {
+      let customHost = this.state.customGatewayHost.trim();
+      // 确保包含协议
+      if (
+        !customHost.startsWith("http://";) &&
+        !customHost.startsWith("https://";)
+      ) {
+        customHost = `http://${customHost}`;
+      }
+      return customHost;
+    }
+
+    // 否则使用当前浏览器地址
+    const protocol = window.location.protocol;
+    const hostname = window.location.hostname;
+    const port = "9195"; // ShenYu默认端口,可以考虑从配置读取
+
+    // 可以在这里添加从配置文件或环境变量读取的逻辑
+    // const configHost = process.env.REACT_APP_SHENYU_HOST;
+    // const configPort = process.env.REACT_APP_SHENYU_PORT;
+
+    return `${protocol}//${hostname}:${port}`;
+  };
+
+  // 获取默认请求头
+  getDefaultHeaders = () => {
+    return {
+      "X-Client-ID": "cursor-client",
+      Authorization:
+        "Bearer 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.E8C6cQxv5N9Qy7JHHZzY3osVbP40TgXnyFEG-Dc2uo0",
+    };
+  };
+
+  // 从selector的handle字段获取描述信息
+  getHandleDescription = (handle) => {
+    if (!handle) return "默认MCP服务";
+
+    try {
+      const handleObj = JSON.parse(handle);
+      return handleObj.description || "默认MCP服务";
+    } catch (e) {
+      return "默认MCP服务";
+    }
+  };
+
+  // 根据传输协议过滤配置
+  filterByTransport = (mcpServers, transport) => {
+    const filtered = {};
+    Object.keys(mcpServers).forEach((key) => {
+      if (mcpServers[key].transport === transport) {
+        filtered[key] = mcpServers[key];
+      }
+    });
+    return filtered;
+  };
+
+  // 获取默认配置
+  getDefaultConfig = (transport) => {
+    const gatewayHost = this.getGatewayHost();
+    const defaultHeaders = this.getDefaultHeaders();
+
+    const key = transport === "sse" ? "shenyu-mcp-sse" : "shenyu-mcp";
+    // 使用更合理的默认路径,与selector路径生成逻辑保持一致
+    const basePath = "/http"; // MCP插件的默认基础路径
+    const urlPath =
+      transport === "sse" ? `${basePath}/sse` : `${basePath}/streamablehttp`;
+    const nameSuffix = transport === "sse" ? "服务sse" : "服务";
+    const descSuffix = transport === "sse" ? "服务测试sse" : "服务测试";
+
+    return {
+      mcpServers: {
+        [key]: {
+          url: `${gatewayHost}${urlPath}`,
+          name: `shenyuMcp${nameSuffix}`,
+          description: `shenyuMcp${descSuffix}`,
+          headers: defaultHeaders,
+          transport,
+        },
+      },
+    };
+  };
+
+  // 复制配置到剪贴板
+  handleCopyConfig = (config) => {
+    const configText = JSON.stringify(config, null, 2);
+    navigator.clipboard
+      .writeText(configText)
+      .then(() => {
+        message.success(getIntlContent("SHENYU.MCP.CONFIG.COPY.SUCCESS"));
+      })
+      .catch(() => {
+        message.error(getIntlContent("SHENYU.MCP.CONFIG.COPY.FAILED"));
+      });
+  };
+
+  // 复制JSON文本
+  copyJsonText = (text) => {
+    navigator.clipboard
+      .writeText(text)
+      .then(() => {
+        message.success(getIntlContent("SHENYU.MCP.CONFIG.COPY.SUCCESS"));
+      })
+      .catch(() => {
+        message.error(getIntlContent("SHENYU.MCP.CONFIG.COPY.FAILED"));
+      });
+  };
+
+  // 根据selector规则生成URL
+  getSelectorUrl = (selector, gatewayHost) => {
+    // 默认路径使用/http,这是MCP插件的通用前缀
+    let basePath = "/http";
+
+    // 尝试从selector的条件中获取更具体的路径信息
+    if (selector.selectorConditions && selector.selectorConditions.length > 0) 
{
+      for (let i = 0; i < selector.selectorConditions.length; i += 1) {
+        const condition = selector.selectorConditions[i];
+        // 查找URI相关的条件
+        if (condition.paramType === "uri" && condition.paramValue) {
+          let conditionPath = condition.paramValue.trim();
+
+          // 移除通配符和多余的字符
+          conditionPath = conditionPath
+            .replace(/\/\*\*$/, "") // 移除 /**
+            .replace(/\/\*$/, "") // 移除 /*
+            .replace(/\*$/, ""); // 移除结尾的 *
+
+          // 如果路径不为空且不是根路径,使用它作为基础路径
+          if (
+            conditionPath &&
+            conditionPath !== "/" &&
+            conditionPath !== "/**"
+          ) {
+            // 确保路径以/开头
+            if (!conditionPath.startsWith("/")) {
+              conditionPath = `/${conditionPath}`;
+            }
+            basePath = conditionPath;
+            break; // 找到第一个有效的URI条件就使用
+          }
+        }
+      }
+    }
+
+    return `${gatewayHost}${basePath}`;
+  };
+
+  // 获取从selector提取的路径信息,用于界面显示
+  getSelectorPathInfo = (selector) => {
+    if (
+      !selector ||
+      !selector.selectorConditions ||
+      selector.selectorConditions.length === 0
+    ) {
+      return null;
+    }
+
+    for (let i = 0; i < selector.selectorConditions.length; i += 1) {
+      const condition = selector.selectorConditions[i];
+      if (condition.paramType === "uri" && condition.paramValue) {
+        let conditionPath = condition.paramValue.trim();
+
+        // 移除通配符和多余的字符
+        conditionPath = conditionPath
+          .replace(/\/\*\*$/, "") // 移除 /**
+          .replace(/\/\*$/, "") // 移除 /*
+          .replace(/\*$/, ""); // 移除结尾的 *
+
+        // 如果路径不为空且不是根路径,返回它
+        if (conditionPath && conditionPath !== "/" && conditionPath !== "/**") 
{
+          // 确保路径以/开头
+          if (!conditionPath.startsWith("/")) {
+            conditionPath = `/${conditionPath}`;
+          }
+          return conditionPath;
+        }
+      }
+    }
+
+    return "/http (默认)";
+  };
+
+  render() {
+    const { visible, configType, onCancel, selectorList } = this.props;
+    const { sseConfig, streamableConfig } = this.state;
+
+    const isSSE = configType === "sse";
+    const config = isSSE ? sseConfig : streamableConfig;
+    const title = isSSE
+      ? getIntlContent("SHENYU.MCP.CONFIG.SSE.TITLE")
+      : getIntlContent("SHENYU.MCP.CONFIG.STREAMABLE.TITLE");
+
+    // 获取当前selector信息
+    const currentSelector =
+      selectorList && selectorList.length === 1 ? selectorList[0] : null;
+    const selectorInfo = currentSelector
+      ? ` - ${currentSelector.name || "Unnamed Selector"}`
+      : "";
+
+    return (
+      <Modal
+        title={`${title}${selectorInfo}`}
+        visible={visible}
+        onCancel={onCancel}
+        width={800}
+        footer={[
+          <Button
+            key="copy"
+            type="primary"
+            onClick={() => this.handleCopyConfig(config)}
+          >
+            {getIntlContent("SHENYU.MCP.JSON.EDIT.COPY")}
+          </Button>,
+          <Button key="close" onClick={onCancel}>
+            {getIntlContent("SHENYU.COMMON.CALCEL")}
+          </Button>,
+        ]}
+      >
+        <div style={{ marginBottom: "16px" }}>
+          <Text type="secondary">
+            {getIntlContent("SHENYU.MCP.CONFIG.DESCRIPTION")}
+          </Text>
+        </div>
+
+        <div style={{ marginBottom: "16px" }}>
+          <div style={{ marginBottom: "8px" }}>
+            <Text strong>网关地址配置:</Text>
+          </div>
+          <Input
+            placeholder="输入自定义网关地址,如: http://localhost:9195 (留空使用默认)"
+            value={this.state.customGatewayHost}
+            onChange={(e) => {
+              this.setState({ customGatewayHost: e.target.value }, () => {
+                // 当网关地址改变时重新生成配置
+                this.generateConfigs();
+              });
+            }}
+            style={{ marginBottom: "8px" }}
+          />
+          <div style={{ fontSize: "12px", color: "#666" }}>
+            当前使用: <strong>{this.getGatewayHost()}</strong>
+          </div>
+        </div>
+
+        {currentSelector && (
+          <div style={{ marginBottom: "16px" }}>
+            <Text type="secondary">
+              <>
+                <strong>Selector:</strong> {currentSelector.name}
+                {currentSelector.enabled ? " (启用)" : " (禁用)"}
+                <br />
+                <strong>基础路径:</strong>{" "}
+                {this.getSelectorPathInfo(currentSelector)}
+                <br />
+                <small style={{ color: "#888" }}>
+                  最终URL = 网关地址 + 基础路径 + 协议后缀(/sse 或
+                  /streamablehttp)
+                </small>
+              </>
+            </Text>
+          </div>
+        )}
+
+        <Divider />
+
+        <div style={{ marginBottom: "16px" }}>
+          <Title level={5}>
+            {getIntlContent("SHENYU.MCP.CONFIG.SERVICE.TRANSPORT")}:{" "}
+            {isSSE ? "SSE" : "Streamable HTTP"}
+          </Title>
+        </div>
+
+        <div
+          style={{
+            border: "1px solid #d9d9d9",
+            borderRadius: "4px",
+            maxHeight: "500px",
+            overflow: "auto",
+            backgroundColor: "#fafafa",
+          }}
+        >
+          <ReactJson
+            src={config}
+            theme="monokai"
+            displayDataTypes={false}
+            displayObjectSize={false}
+            name={false}
+            enableClipboard={true}
+            collapsed={1}
+            style={{
+              padding: "16px",
+              fontSize: "13px",
+            }}
+          />
+        </div>
+
+        {/* 可复制的JSON文本展示 */}
+        <div style={{ marginTop: "16px" }}>
+          <div
+            style={{
+              display: "flex",
+              justifyContent: "space-between",
+              alignItems: "center",
+              marginBottom: "8px",
+            }}
+          >
+            <Title level={5}>
+              {getIntlContent("SHENYU.MCP.CONFIG.JSON.TITLE")}
+            </Title>
+            <Button
+              size="small"
+              type="primary"
+              onClick={() => this.copyJsonText(JSON.stringify(config, null, 
2))}
+            >
+              {getIntlContent("SHENYU.MCP.CONFIG.COPY.JSON")}
+            </Button>
+          </div>
+          <TextArea
+            value={JSON.stringify(config, null, 2)}
+            readOnly
+            autoSize={{ minRows: 6, maxRows: 12 }}
+            style={{
+              fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
+              fontSize: "12px",
+              backgroundColor: "#f8f8f8",
+            }}
+          />
+        </div>
+
+        {Object.keys(config.mcpServers || {}).length > 0 && (
+          <div style={{ marginTop: "16px" }}>
+            <Title level={5}>
+              {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.TITLE")}
+            </Title>
+            <ul style={{ fontSize: "12px", color: "#666" }}>
+              <li>
+                <strong>url</strong>:{" "}
+                {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.URL")}
+              </li>
+              <li>
+                <strong>name</strong>:{" "}
+                {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.NAME")}
+              </li>
+              <li>
+                <strong>description</strong>:{" "}
+                {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.DESCRIPTION")}
+              </li>
+              <li>
+                <strong>headers</strong>:{" "}
+                {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.HEADERS")}
+              </li>
+              <li>
+                <strong>transport</strong>:{" "}
+                {getIntlContent("SHENYU.MCP.CONFIG.EXPLANATION.TRANSPORT")} (
+                {isSSE ? "sse" : "streamableHttp"})
+              </li>
+            </ul>
+          </div>
+        )}
+      </Modal>
+    );
+  }
+}
+
+export default McpConfigModal;
diff --git a/src/routes/Plugin/McpServer/ToolsModal.js 
b/src/routes/Plugin/McpServer/ToolsModal.js
index 73d87ddf..ee967530 100644
--- a/src/routes/Plugin/McpServer/ToolsModal.js
+++ b/src/routes/Plugin/McpServer/ToolsModal.js
@@ -26,16 +26,18 @@ import {
   Row,
   Select,
   Switch,
+  Radio,
+  Tabs,
 } from "antd";
 import { connect } from "dva";
 import TextArea from "antd/lib/input/TextArea";
 import ReactJson from "react-json-view";
 import styles from "../index.less";
 import { getIntlContent } from "../../../utils/IntlUtils";
-import RuleCopy from "../Common/RuleCopy";
 
 const FormItem = Form.Item;
 const { Option } = Select;
+const { TabPane } = Tabs;
 
 @connect(({ global }) => ({
   currentNamespaceId: global.currentNamespaceId,
@@ -43,45 +45,222 @@ const { Option } = Select;
 class AddModal extends Component {
   constructor(props) {
     super(props);
+
+    // 初始化表单参数
+    const formData = this.initParameters(props);
+
+    // 初始化JSON模式数据
+    const jsonData = this.initJsonMode(props);
+
     this.state = {
-      visible: false,
-      questJson: {},
+      // 表单模式状态
+      parameters: formData.parameters,
+      questJson: formData.questJson,
+      // JSON模式状态
+      jsonText: jsonData.jsonText,
+      jsonError: jsonData.jsonError,
+      // 通用状态
+      editMode: "form", // "form" 或 "json"
+      activeTab: "1",
     };
-
-    this.initParameters(props);
   }
 
   initParameters = (props) => {
-    let parameters = [];
+    const { handle } = props;
     let questJson = {};
-    try {
-      const handle = props.handle ? JSON.parse(props.handle) : {};
-      parameters = handle.parameters || [
+    let parameters = [];
+
+    if (handle) {
+      try {
+        const handleObj = JSON.parse(handle);
+        parameters = handleObj.parameters || [];
+        questJson = handleObj.requestConfig
+          ? JSON.parse(handleObj.requestConfig)
+          : {};
+      } catch (e) {
+        // Failed to parse handle JSON
+        parameters = [
+          {
+            type: "String",
+            name: "",
+            description: "",
+          },
+        ];
+      }
+    } else {
+      parameters = [
         {
           type: "String",
           name: "",
           description: "",
         },
       ];
-      questJson = JSON.parse(handle.requestConfig);
-    } catch (e) {
-      console.error("Failed to parse handle JSON:", e);
-      parameters = [
+    }
+    return { parameters, questJson };
+  };
+
+  initJsonMode = (props) => {
+    const { name, description, enabled, handle } = props;
+
+    // 将handle内容提升到最外层,创建扁平化的JSON结构
+    let flattenedJson = {
+      name: name || "",
+      description: description || "",
+      enabled: enabled !== undefined ? enabled : true,
+      parameters: [],
+      requestConfig: "{}",
+    };
+
+    if (handle) {
+      try {
+        const handleObj = JSON.parse(handle);
+        flattenedJson = {
+          name: name || "",
+          description: description || "",
+          enabled: enabled !== undefined ? enabled : true,
+          parameters: handleObj.parameters || [],
+          requestConfig: handleObj.requestConfig || "{}",
+        };
+      } catch (e) {
+        // Failed to parse handle JSON
+      }
+    }
+
+    return {
+      jsonText: JSON.stringify(flattenedJson, null, 2),
+      jsonError: null,
+    };
+  };
+
+  generateTemplate = () => {
+    const template = {
+      name: "getOrderById",
+      description: "Get order details by ID",
+      enabled: true,
+      parameters: [
         {
-          type: "String",
-          name: "",
-          description: "",
+          name: "orderId",
+          type: "string",
+          description: "Order ID",
+          required: true,
         },
-      ];
+      ],
+      requestConfig: JSON.stringify({
+        requestTemplate: {
+          url: "/api/orders/{orderId}",
+          method: "GET",
+          headers: [
+            {
+              name: "Authorization",
+              value: "Bearer {token}",
+            },
+          ],
+          timeout: 30000,
+        },
+        argsPosition: {
+          orderId: "url.path",
+        },
+      }),
+    };
+
+    this.setState({
+      jsonText: JSON.stringify(template, null, 2),
+      jsonError: null,
+    });
+
+    message.success(
+      getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE.SUCCESS"),
+    );
+  };
+
+  handleEditModeChange = (e) => {
+    const editMode = e.target.value;
+    this.setState({ editMode });
+
+    // 切换到JSON模式时,同步表单数据到JSON
+    if (editMode === "json") {
+      const { form } = this.props;
+      const { parameters, questJson } = this.state;
+
+      // 获取表单数据
+      form.validateFields((err, values) => {
+        if (!err) {
+          const toolJson = {
+            name: values.name || "",
+            description: values.description || "",
+            enabled: values.enabled !== undefined ? values.enabled : true,
+            handle: {
+              parameters,
+              requestConfig: questJson,
+              description: values.description || "",
+            },
+          };
+
+          this.setState({
+            jsonText: JSON.stringify(toolJson, null, 2),
+            jsonError: null,
+          });
+        }
+      });
+    }
+  };
+
+  handleJsonTextChange = (e) => {
+    const jsonText = e.target.value;
+    this.setState({ jsonText });
+
+    // 实时验证JSON格式
+    try {
+      JSON.parse(jsonText);
+      this.setState({ jsonError: null });
+    } catch (error) {
+      this.setState({ jsonError: error.message });
     }
-    this.state.parameters = parameters;
-    this.state.questJson = questJson;
+  };
+
+  handleFormatJson = () => {
+    try {
+      const parsed = JSON.parse(this.state.jsonText);
+      const formatted = JSON.stringify(parsed, null, 2);
+      this.setState({
+        jsonText: formatted,
+        jsonError: null,
+      });
+      message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.SUCCESS"));
+    } catch (error) {
+      message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.FAILED"));
+    }
+  };
+
+  handleCompressJson = () => {
+    try {
+      const parsed = JSON.parse(this.state.jsonText);
+      const compressed = JSON.stringify(parsed);
+      this.setState({
+        jsonText: compressed,
+        jsonError: null,
+      });
+      message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.SUCCESS"));
+    } catch (error) {
+      message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS.FAILED"));
+    }
+  };
+
+  handleCopyToClipboard = () => {
+    navigator.clipboard
+      .writeText(this.state.jsonText)
+      .then(() => {
+        message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.COPY.SUCCESS"));
+      })
+      .catch(() => {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.COPY.FAILED"));
+      });
   };
 
   updateJson = (obj) => {
-    this.setState({ questJson: obj.updated_src });
-    this.props.form.setFieldsValue({
-      requestConfig: JSON.stringify(obj.updated_src),
+    this.setState({
+      jsonText: JSON.stringify(obj, null, 2),
+      jsonError: null,
     });
   };
 
@@ -109,11 +288,31 @@ class AddModal extends Component {
 
   handleSubmit = (e) => {
     e.preventDefault();
+    const { editMode } = this.state;
+
+    if (editMode === "form") {
+      // 表单模式提交
+      this.handleFormSubmit();
+    } else {
+      // JSON模式提交
+      this.handleJsonSubmit();
+    }
+  };
+
+  handleFormSubmit = () => {
     const { form, handleOk } = this.props;
     const { parameters } = this.state;
 
     form.validateFieldsAndScroll((err, values) => {
-      const { name, description, enabled } = values;
+      const {
+        name,
+        description,
+        enabled,
+        sort,
+        loged,
+        matchMode,
+        matchRestful,
+      } = values;
       if (!err) {
         const submit = this.checkParams();
         if (submit) {
@@ -130,10 +329,10 @@ class AddModal extends Component {
             description,
             handle,
             enabled,
-            sort: 1,
-            loged: true,
-            matchMode: "0",
-            matchRestful: false,
+            sort: parseInt(sort, 10),
+            loged,
+            matchMode,
+            matchRestful,
             ruleConditions: [
               {
                 paramType: "uri",
@@ -148,6 +347,66 @@ class AddModal extends Component {
     });
   };
 
+  handleJsonSubmit = () => {
+    const { jsonText } = this.state;
+    const { handleOk } = this.props;
+
+    if (!jsonText.trim()) {
+      message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.EMPTY.ERROR"));
+      return;
+    }
+
+    try {
+      const parsedJson = JSON.parse(jsonText);
+
+      // 验证必要字段
+      if (!parsedJson.name || !parsedJson.name.trim()) {
+        message.error(getIntlContent("SHENYU.MCP.JSON.EDIT.TOOL.NAME.ERROR"));
+        return;
+      }
+
+      // 确保字段存在
+      const finalData = {
+        name: parsedJson.name,
+        description: parsedJson.description || "",
+        enabled: parsedJson.enabled !== undefined ? parsedJson.enabled : true,
+        parameters: parsedJson.parameters || [],
+        requestConfig: parsedJson.requestConfig || "{}",
+      };
+
+      // 将扁平化数据转换回原始格式,并添加必需的规则级别字段
+      const transformedData = {
+        name: finalData.name,
+        description: finalData.description,
+        enabled: finalData.enabled,
+        handle: JSON.stringify({
+          parameters: finalData.parameters,
+          requestConfig: finalData.requestConfig,
+          description: finalData.description,
+        }),
+        // 添加必需的规则级别字段默认值
+        sort: 1,
+        loged: true,
+        matchMode: "0",
+        matchRestful: false,
+        ruleConditions: [
+          {
+            paramType: "uri",
+            operator: "pathPattern",
+            paramName: "/",
+            paramValue: "/**",
+          },
+        ],
+      };
+
+      handleOk(transformedData);
+    } catch (error) {
+      message.error(
+        
`${getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT.ERROR")}${error.message}`,
+      );
+    }
+  };
+
   handleAdd = () => {
     let { parameters } = this.state;
     parameters.push({
@@ -169,24 +428,6 @@ class AddModal extends Component {
     this.setState({ parameters });
   };
 
-  handleCopyData = (copyData) => {
-    if (!copyData) {
-      this.setState({ visible: false });
-      return;
-    }
-    const { form } = this.props;
-    const { name, matchMode, loged, enabled, sort } = copyData;
-    const formData = {
-      name,
-      matchMode: matchMode.toString(),
-      loged,
-      enabled,
-      sort,
-    };
-    form.setFieldsValue(formData);
-    this.setState({ visible: false });
-  };
-
   render() {
     let {
       onCancel,
@@ -195,15 +436,20 @@ class AddModal extends Component {
       description = "",
       enabled = true,
       handle = "{}",
+      sort = 1,
+      loged = true,
+      matchMode = "0",
+      matchRestful = false,
     } = this.props;
-    const { parameters, visible, questJson } = this.state;
+    const { parameters, questJson, editMode, jsonText, jsonError, activeTab } =
+      this.state;
 
     // Parse handle JSON to get requestConfig and description
     let parsedHandle = {};
     try {
       parsedHandle = JSON.parse(handle);
     } catch (e) {
-      console.error("Failed to parse handle JSON:", e);
+      // Failed to parse handle JSON
     }
 
     const { description: handleDescription = "" } = parsedHandle;
@@ -219,190 +465,435 @@ class AddModal extends Component {
         sm: { span: 20 },
       },
     };
+
+    // 用于预览的JSON对象
+    let previewJson = {};
+    try {
+      previewJson = JSON.parse(jsonText);
+    } catch (e) {
+      previewJson = {
+        error: getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX") + e.message,
+      };
+    }
+
+    const modalTitle =
+      editMode === "form"
+        ? getIntlContent("SHENYU.TOOL.NAME")
+        : getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.TITLE");
+
     return (
       <Modal
         width={1000}
         centered
-        title={getIntlContent("SHENYU.TOOL.NAME")}
+        title={modalTitle}
         visible
         okText={getIntlContent("SHENYU.COMMON.SURE")}
         cancelText={getIntlContent("SHENYU.COMMON.CALCEL")}
         onOk={this.handleSubmit}
         onCancel={onCancel}
       >
-        <Form onSubmit={this.handleSubmit} className="login-form">
-          <FormItem
-            label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
-            {...formItemLayout}
-          >
-            {getFieldDecorator("name", {
-              rules: [
-                {
-                  required: true,
-                  message: getIntlContent("SHENYU.COMMON.INPUTNAME"),
-                },
-              ],
-              initialValue: name,
-            })(
-              <Input
-                allowClear
-                placeholder={getIntlContent(
-                  "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME",
-                )}
-                addonAfter={
-                  <Button
-                    size="small"
-                    type="link"
-                    onClick={() => {
-                      this.setState({ visible: true });
-                    }}
-                  >
-                    {getIntlContent("SHENYU.PLUGIN.SEARCH.RULE.COPY")}
-                  </Button>
-                }
-              />,
-            )}
-          </FormItem>
-          <RuleCopy
-            visible={visible}
-            onOk={this.handleCopyData}
-            onCancel={() => {
-              this.setState({ visible: false });
-            }}
-          />
-          <FormItem
-            label={getIntlContent(
-              "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.DESCRIPTION",
-            )}
-            {...formItemLayout}
-          >
-            {getFieldDecorator("description", {
-              rules: [
-                {
-                  required: true,
-                  message: getIntlContent("SHENYU.COMMON.INPUTDESCRIPTION"),
-                },
-              ],
-              initialValue: finalDescription,
-            })(
-              <TextArea
-                allowClear
-                placeholder={getIntlContent(
-                  "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.DESCRIPTION",
-                )}
-              />,
-            )}
-          </FormItem>
-          <div className={styles.condition}>
+        <div style={{ marginBottom: "16px" }}>
+          <div style={{ marginBottom: "12px" }}>
+            <Radio.Group value={editMode} onChange={this.handleEditModeChange}>
+              <Radio.Button value="form">
+                {getIntlContent("SHENYU.MCP.TOOLS.ADD.MODE.FORM")}
+              </Radio.Button>
+              <Radio.Button value="json">
+                {getIntlContent("SHENYU.MCP.TOOLS.ADD.MODE.JSON")}
+              </Radio.Button>
+            </Radio.Group>
+          </div>
+
+          <p style={{ color: "#666", fontSize: "12px", marginBottom: "8px" }}>
+            {editMode === "form"
+              ? getIntlContent("SHENYU.MCP.JSON.EDIT.DESCRIPTION")
+              : getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.DESCRIPTION")}
+          </p>
+
+          {editMode === "json" && (
+            <Row gutter={8}>
+              <Col>
+                <Button size="small" onClick={this.generateTemplate}>
+                  {getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.TEMPLATE")}
+                </Button>
+              </Col>
+              <Col>
+                <Button size="small" onClick={this.handleFormatJson}>
+                  {getIntlContent("SHENYU.MCP.JSON.EDIT.FORMAT")}
+                </Button>
+              </Col>
+              <Col>
+                <Button size="small" onClick={this.handleCompressJson}>
+                  {getIntlContent("SHENYU.MCP.JSON.EDIT.COMPRESS")}
+                </Button>
+              </Col>
+              <Col>
+                <Button size="small" onClick={this.handleCopyToClipboard}>
+                  {getIntlContent("SHENYU.MCP.JSON.EDIT.COPY")}
+                </Button>
+              </Col>
+            </Row>
+          )}
+        </div>
+
+        {editMode === "form" ? (
+          // 表单模式
+          <Form onSubmit={this.handleSubmit} className="login-form">
             <FormItem
-              label={getIntlContent("SHENYU.COMMON.PARAMETER")}
+              label={getIntlContent("SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME")}
               {...formItemLayout}
             >
-              {parameters.map((item, index) => {
-                return (
-                  <Row key={index} gutter={8}>
-                    <Col span={4}>
-                      <Input
-                        allowClear
-                        value={item.name}
-                        placeholder={getIntlContent(
-                          "SHENYU.COMMON.PARAMETER.NAME",
-                        )}
-                        onChange={(e) => {
-                          const newValue = e.target.value;
-                          const newParameters = [...parameters];
-                          newParameters[index].name = newValue;
-                          this.setState({ parameters: newParameters });
-                        }}
-                      />
-                    </Col>
-                    <Col span={5}>
-                      <Select
-                        value={item.type}
-                        placeholder={getIntlContent(
-                          "SHENYU.COMMON.PARAMETER.TYPE",
-                        )}
-                        onChange={(value) => {
-                          const newParameters = [...parameters];
-                          newParameters[index].type = value;
-                          this.setState({ parameters: newParameters });
-                        }}
-                      >
-                        <Option value="String">String</Option>
-                        <Option value="Integer">Integer</Option>
-                        <Option value="Long">Long</Option>
-                        <Option value="Double">Double</Option>
-                        <Option value="Float">Float</Option>
-                        <Option value="Boolean">Boolean</Option>
-                      </Select>
-                    </Col>
-                    <Col span={11}>
-                      <Input
-                        allowClear
-                        value={item.description}
-                        placeholder={getIntlContent(
-                          "SHENYU.COMMON.PARAMETER.DESCRIPTION",
-                        )}
-                        onChange={(e) => {
-                          const newValue = e.target.value;
-                          const newParameters = [...parameters];
-                          newParameters[index].description = newValue;
-                          this.setState({ parameters: newParameters });
-                        }}
-                      />
-                    </Col>
-                    <Col span={4}>
-                      <Button
-                        type="danger"
-                        onClick={() => {
-                          this.handleDelete(index);
-                        }}
-                      >
-                        {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
-                      </Button>
-                    </Col>
-                  </Row>
-                );
-              })}
+              {getFieldDecorator("name", {
+                rules: [
+                  {
+                    required: true,
+                    message: getIntlContent("SHENYU.COMMON.INPUTNAME"),
+                  },
+                ],
+                initialValue: name,
+              })(
+                <Input
+                  allowClear
+                  placeholder={getIntlContent(
+                    "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.NAME",
+                  )}
+                />,
+              )}
             </FormItem>
-            <FormItem label={" "} colon={false} {...formItemLayout}>
-              <Button
-                className={styles.addButton}
-                onClick={this.handleAdd}
-                type="primary"
+            <FormItem
+              label={getIntlContent(
+                "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.DESCRIPTION",
+              )}
+              {...formItemLayout}
+            >
+              {getFieldDecorator("description", {
+                rules: [
+                  {
+                    required: true,
+                    message: getIntlContent("SHENYU.COMMON.INPUTDESCRIPTION"),
+                  },
+                ],
+                initialValue: finalDescription,
+              })(
+                <TextArea
+                  allowClear
+                  placeholder={getIntlContent(
+                    "SHENYU.PLUGIN.SELECTOR.LIST.COLUMN.DESCRIPTION",
+                  )}
+                />,
+              )}
+            </FormItem>
+
+            <FormItem
+              label={getIntlContent("SHENYU.SELECTOR.EXEORDER")}
+              {...formItemLayout}
+            >
+              {getFieldDecorator("sort", {
+                rules: [
+                  {
+                    required: true,
+                    message: getIntlContent("SHENYU.SELECTOR.INPUTNUMBER"),
+                  },
+                ],
+                initialValue: sort,
+              })(
+                <Input
+                  allowClear
+                  type="number"
+                  min={1}
+                  max={1000}
+                  placeholder={getIntlContent("SHENYU.SELECTOR.INPUTORDER")}
+                />,
+              )}
+            </FormItem>
+
+            <Row gutter={16}>
+              <Col span={12}>
+                <FormItem
+                  label={getIntlContent("SHENYU.SELECTOR.PRINTLOG")}
+                  {...formItemLayout}
+                  labelCol={{ sm: { span: 8 } }}
+                  wrapperCol={{ sm: { span: 16 } }}
+                >
+                  {getFieldDecorator("loged", {
+                    initialValue: loged,
+                    valuePropName: "checked",
+                    rules: [{ required: true }],
+                  })(<Switch />)}
+                </FormItem>
+              </Col>
+              <Col span={12}>
+                <FormItem
+                  label={getIntlContent("SHENYU.SELECTOR.MATCHRESTFUL")}
+                  {...formItemLayout}
+                  labelCol={{ sm: { span: 8 } }}
+                  wrapperCol={{ sm: { span: 16 } }}
+                >
+                  {getFieldDecorator("matchRestful", {
+                    initialValue: matchRestful,
+                    valuePropName: "checked",
+                    rules: [{ required: true }],
+                  })(<Switch />)}
+                </FormItem>
+              </Col>
+            </Row>
+
+            <FormItem
+              label={getIntlContent("SHENYU.COMMON.MATCHTYPE")}
+              {...formItemLayout}
+            >
+              {getFieldDecorator("matchMode", {
+                rules: [
+                  {
+                    required: true,
+                    message: getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE"),
+                  },
+                ],
+                initialValue: matchMode,
+              })(
+                <Select
+                  placeholder={getIntlContent("SHENYU.COMMON.INPUTMATCHTYPE")}
+                >
+                  <Option value="0">and</Option>
+                  <Option value="1">or</Option>
+                </Select>,
+              )}
+            </FormItem>
+
+            <div className={styles.condition}>
+              <FormItem
+                label={getIntlContent("SHENYU.COMMON.PARAMETER")}
+                {...formItemLayout}
               >
-                {getIntlContent("SHENYU.COMMON.ADD")}{" "}
-                {getIntlContent("SHENYU.COMMON.PARAMETER")}
-              </Button>
+                {parameters.map((item, index) => {
+                  return (
+                    <Row key={index} gutter={8}>
+                      <Col span={4}>
+                        <Input
+                          allowClear
+                          value={item.name}
+                          placeholder={getIntlContent(
+                            "SHENYU.COMMON.PARAMETER.NAME",
+                          )}
+                          onChange={(e) => {
+                            const newValue = e.target.value;
+                            const newParameters = [...parameters];
+                            newParameters[index].name = newValue;
+                            this.setState({ parameters: newParameters });
+                          }}
+                        />
+                      </Col>
+                      <Col span={5}>
+                        <Select
+                          value={item.type}
+                          placeholder={getIntlContent(
+                            "SHENYU.COMMON.PARAMETER.TYPE",
+                          )}
+                          onChange={(value) => {
+                            const newParameters = [...parameters];
+                            newParameters[index].type = value;
+                            this.setState({ parameters: newParameters });
+                          }}
+                        >
+                          <Option value="String">String</Option>
+                          <Option value="Integer">Integer</Option>
+                          <Option value="Long">Long</Option>
+                          <Option value="Double">Double</Option>
+                          <Option value="Float">Float</Option>
+                          <Option value="Boolean">Boolean</Option>
+                        </Select>
+                      </Col>
+                      <Col span={11}>
+                        <Input
+                          allowClear
+                          value={item.description}
+                          placeholder={getIntlContent(
+                            "SHENYU.COMMON.PARAMETER.DESCRIPTION",
+                          )}
+                          onChange={(e) => {
+                            const newValue = e.target.value;
+                            const newParameters = [...parameters];
+                            newParameters[index].description = newValue;
+                            this.setState({ parameters: newParameters });
+                          }}
+                        />
+                      </Col>
+                      <Col span={4}>
+                        <Button
+                          type="danger"
+                          onClick={() => {
+                            this.handleDelete(index);
+                          }}
+                        >
+                          {getIntlContent("SHENYU.COMMON.DELETE.NAME")}
+                        </Button>
+                      </Col>
+                    </Row>
+                  );
+                })}
+              </FormItem>
+              <FormItem label={" "} colon={false} {...formItemLayout}>
+                <Button
+                  className={styles.addButton}
+                  onClick={this.handleAdd}
+                  type="primary"
+                >
+                  {getIntlContent("SHENYU.COMMON.ADD")}{" "}
+                  {getIntlContent("SHENYU.COMMON.PARAMETER")}
+                </Button>
+              </FormItem>
+            </div>
+            <FormItem
+              label={getIntlContent("SHENYU.COMMON.TOOL.REQUESTCONFIG")}
+              {...formItemLayout}
+              required={true}
+            >
+              <ReactJson
+                src={questJson}
+                theme="monokai"
+                displayDataTypes={false}
+                name={false}
+                onAdd={this.updateJson}
+                onEdit={this.updateJson}
+                onDelete={this.updateJson}
+                style={{ borderRadius: 4, padding: 16 }}
+              />
             </FormItem>
-          </div>
-          <FormItem
-            label={getIntlContent("SHENYU.COMMON.TOOL.REQUESTCONFIG")}
-            {...formItemLayout}
-            required={true}
-          >
-            <ReactJson
-              src={questJson}
-              theme="monokai"
-              displayDataTypes={false}
-              name={false}
-              onAdd={this.updateJson}
-              onEdit={this.updateJson}
-              onDelete={this.updateJson}
-              style={{ borderRadius: 4, padding: 16 }}
-            />
-          </FormItem>
-          <FormItem
-            {...formItemLayout}
-            label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
+            <FormItem
+              {...formItemLayout}
+              label={getIntlContent("SHENYU.SELECTOR.WHETHEROPEN")}
+            >
+              {getFieldDecorator("enabled", {
+                initialValue: enabled,
+                valuePropName: "checked",
+                rules: [{ required: true }],
+              })(<Switch />)}
+            </FormItem>
+          </Form>
+        ) : (
+          // JSON模式
+          <Tabs
+            activeKey={activeTab}
+            onChange={(key) => this.setState({ activeTab: key })}
           >
-            {getFieldDecorator("enabled", {
-              initialValue: enabled,
-              valuePropName: "checked",
-              rules: [{ required: true }],
-            })(<Switch />)}
-          </FormItem>
-        </Form>
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.TEXT")}
+              key="1"
+            >
+              <div style={{ position: "relative" }}>
+                <TextArea
+                  value={jsonText}
+                  onChange={this.handleJsonTextChange}
+                  placeholder={getIntlContent(
+                    "SHENYU.MCP.TOOLS.ADD.JSON.PLACEHOLDER",
+                  )}
+                  autoSize={{ minRows: 15, maxRows: 25 }}
+                  style={{
+                    fontFamily: "Monaco, Menlo, 'Ubuntu Mono', monospace",
+                    fontSize: "13px",
+                    lineHeight: "1.4",
+                  }}
+                />
+                {jsonError && (
+                  <div
+                    style={{
+                      position: "absolute",
+                      bottom: "8px",
+                      right: "8px",
+                      background: "#ff4d4f",
+                      color: "white",
+                      padding: "4px 8px",
+                      borderRadius: "4px",
+                      fontSize: "12px",
+                      maxWidth: "300px",
+                      wordBreak: "break-word",
+                    }}
+                  >
+                    {getIntlContent("SHENYU.MCP.JSON.EDIT.ERROR.PREFIX")}
+                    {jsonError}
+                  </div>
+                )}
+              </div>
+            </TabPane>
+            <TabPane
+              tab={getIntlContent("SHENYU.MCP.JSON.EDIT.TAB.PREVIEW")}
+              key="2"
+            >
+              <div
+                style={{
+                  border: "1px solid #d9d9d9",
+                  borderRadius: "4px",
+                  maxHeight: "400px",
+                  overflow: "auto",
+                }}
+              >
+                <ReactJson
+                  src={previewJson}
+                  theme="monokai"
+                  displayDataTypes={false}
+                  displayObjectSize={false}
+                  name={false}
+                  enableClipboard={false}
+                  style={{
+                    padding: "16px",
+                    fontSize: "13px",
+                  }}
+                />
+              </div>
+            </TabPane>
+          </Tabs>
+        )}
+
+        {editMode === "json" && (
+          <div style={{ marginTop: "16px", color: "#666", fontSize: "12px" }}>
+            <p>
+              <strong>
+                {getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.TITLE")}
+              </strong>
+            </p>
+            <ul>
+              <li>
+                <code>name</code>:{" "}
+                {getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.NAME")}
+              </li>
+              <li>
+                <code>description</code>:{" "}
+                {getIntlContent(
+                  "SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.DESCRIPTION",
+                )}
+              </li>
+              <li>
+                <code>enabled</code>:{" "}
+                {getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.STRUCTURE.ENABLED")}
+              </li>
+              <li>
+                <code>parameters</code>:{" "}
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.PARAMETERS")}
+              </li>
+              <li>
+                <code>requestConfig</code>:{" "}
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.HANDLE.REQUESTCONFIG")}
+              </li>
+            </ul>
+            <p>
+              <strong>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.TITLE")}
+              </strong>
+            </p>
+            <ul>
+              <li>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.KEYBOARD")}
+              </li>
+              
<li>{getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.SWITCH")}</li>
+              <li>
+                {getIntlContent("SHENYU.MCP.JSON.EDIT.OPERATION.VALIDATE")}
+              </li>
+              <li>
+                
{getIntlContent("SHENYU.MCP.TOOLS.ADD.JSON.OPERATION.TEMPLATE")}
+              </li>
+            </ul>
+          </div>
+        )}
       </Modal>
     );
   }
diff --git a/src/routes/Plugin/McpServer/index.js 
b/src/routes/Plugin/McpServer/index.js
index ea497ed7..496f064c 100755
--- a/src/routes/Plugin/McpServer/index.js
+++ b/src/routes/Plugin/McpServer/index.js
@@ -34,6 +34,8 @@ import ReactJson from "react-json-view";
 import styles from "../index.less";
 import Selector from "../Common/Selector";
 import Tools from "./ToolsModal";
+import JsonEditModal from "./JsonEditModal";
+import McpConfigModal from "./McpConfigModal";
 import { getCurrentLocale, getIntlContent } from "../../../utils/IntlUtils";
 import AuthButton from "../../../utils/AuthButton";
 import {
@@ -65,6 +67,11 @@ export default class McpServer extends Component {
       isPluginEnabled: false,
       pluginName: "mcpServer",
       pluginRole: "Mcp",
+      jsonEditVisible: false,
+      currentJsonData: null,
+      mcpConfigVisible: false,
+      mcpConfigType: "sse", // "sse" or "streamable"
+      currentSelectorForConfig: null,
     };
   }
 
@@ -773,7 +780,7 @@ export default class McpServer extends Component {
   };
 
   editTool = (record) => {
-    console.log("record", record);
+    // record
     const { dispatch, currentSelector, plugins, currentNamespaceId } =
       this.props;
     const { toolPage, toolPageSize, pluginId, pluginName } = this.state;
@@ -907,6 +914,89 @@ export default class McpServer extends Component {
     });
   };
 
+  editToolByJson = (record) => {
+    const { dispatch, currentNamespaceId } = this.props;
+    const { id } = record;
+
+    dispatch({
+      type: "common/fetchRuleItem",
+      payload: {
+        id,
+        namespaceId: currentNamespaceId,
+      },
+      callback: (rule) => {
+        this.setState({
+          currentJsonData: rule,
+          jsonEditVisible: true,
+        });
+      },
+    });
+  };
+
+  handleJsonEditOk = (jsonData) => {
+    const { dispatch, currentSelector, currentNamespaceId } = this.props;
+    const { toolPage, toolPageSize } = this.state;
+    const selectorId = currentSelector ? currentSelector.id : "";
+
+    dispatch({
+      type: "common/updateRule",
+      payload: {
+        selectorId,
+        ...jsonData,
+        namespaceId: currentNamespaceId,
+      },
+      fetchValue: {
+        selectorId,
+        currentPage: toolPage,
+        pageSize: toolPageSize,
+        namespaceId: currentNamespaceId,
+      },
+      callback: () => {
+        this.setState({
+          jsonEditVisible: false,
+          currentJsonData: null,
+        });
+        message.success(getIntlContent("SHENYU.MCP.JSON.EDIT.UPDATE.SUCCESS"));
+
+        // 刷新工具列表以确保最新数据显示
+        this.getAllTools(toolPage, toolPageSize);
+      },
+    });
+  };
+
+  handleJsonEditCancel = () => {
+    this.setState({
+      jsonEditVisible: false,
+      currentJsonData: null,
+    });
+  };
+
+  // 显示SSE配置
+  showSSEConfig = (selector = null) => {
+    this.setState({
+      mcpConfigVisible: true,
+      mcpConfigType: "sse",
+      currentSelectorForConfig: selector,
+    });
+  };
+
+  // 显示Streamable配置
+  showStreamableConfig = (selector = null) => {
+    this.setState({
+      mcpConfigVisible: true,
+      mcpConfigType: "streamable",
+      currentSelectorForConfig: selector,
+    });
+  };
+
+  // 关闭MCP配置模态框
+  handleMcpConfigCancel = () => {
+    this.setState({
+      mcpConfigVisible: false,
+      currentSelectorForConfig: null,
+    });
+  };
+
   // eslint-disable-next-line react/no-unused-class-component-methods
   changeLocales(locale) {
     this.setState({
@@ -984,6 +1074,26 @@ export default class McpServer extends Component {
                   {getIntlContent("SHENYU.COMMON.CHANGE")}
                 </span>
               </AuthButton>
+              <span
+                style={{ marginRight: 8 }}
+                className="edit"
+                onClick={(e) => {
+                  e.stopPropagation();
+                  this.showSSEConfig(record);
+                }}
+              >
+                {getIntlContent("SHENYU.MCP.CONFIG.SSE")}
+              </span>
+              <span
+                style={{ marginRight: 8 }}
+                className="edit"
+                onClick={(e) => {
+                  e.stopPropagation();
+                  this.showStreamableConfig(record);
+                }}
+              >
+                {getIntlContent("SHENYU.MCP.CONFIG.STREAMABLE")}
+              </span>
               <AuthButton
                 perms={`plugin:${this.state.pluginName}Selector:delete`}
               >
@@ -1131,6 +1241,18 @@ export default class McpServer extends Component {
                   {getIntlContent("SHENYU.COMMON.CHANGE")}
                 </span>
               </AuthButton>
+              <AuthButton perms={`plugin:${this.state.pluginName}Rule:edit`}>
+                <span
+                  className="edit"
+                  style={{ marginRight: 8 }}
+                  onClick={(e) => {
+                    e.stopPropagation();
+                    this.editToolByJson(record);
+                  }}
+                >
+                  {getIntlContent("SHENYU.MCP.EDIT.JSON")}
+                </span>
+              </AuthButton>
               <AuthButton perms={`plugin:${this.state.pluginName}Rule:delete`}>
                 <Popconfirm
                   title={getIntlContent("SHENYU.COMMON.DELETE")}
@@ -1379,6 +1501,26 @@ export default class McpServer extends Component {
           </Col>
         </Row>
         {popup}
+        {this.state.jsonEditVisible && (
+          <JsonEditModal
+            visible={this.state.jsonEditVisible}
+            data={this.state.currentJsonData}
+            onOk={this.handleJsonEditOk}
+            onCancel={this.handleJsonEditCancel}
+          />
+        )}
+        {this.state.mcpConfigVisible && (
+          <McpConfigModal
+            visible={this.state.mcpConfigVisible}
+            configType={this.state.mcpConfigType}
+            selectorList={
+              this.state.currentSelectorForConfig
+                ? [this.state.currentSelectorForConfig]
+                : selectorList
+            }
+            onCancel={this.handleMcpConfigCancel}
+          />
+        )}
       </div>
     );
   }


Reply via email to