diff --git a/LinkToolAddin.csproj b/LinkToolAddin.csproj
index e0d0d0e..c53da32 100644
--- a/LinkToolAddin.csproj
+++ b/LinkToolAddin.csproj
@@ -106,6 +106,7 @@
+
diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json
index 5a2c881..e98eaad 100644
--- a/Properties/launchSettings.json
+++ b/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"LinkToolAddin": {
"commandName": "Executable",
- "executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
+ "executablePath": "C:\\Users\\PeterZhong\\AppData\\Local\\Programs\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
"applicationUrl": "https://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
diff --git a/client/tool/ArcGisPro.cs b/client/tool/ArcGisPro.cs
index 64e38d9..c94f2e3 100644
--- a/client/tool/ArcGisPro.cs
+++ b/client/tool/ArcGisPro.cs
@@ -1,6 +1,21 @@
-namespace LinkToolAddin.client.tool;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using LinkToolAddin.server;
+using ModelContextProtocol.Server;
+using Newtonsoft.Json;
+
+namespace LinkToolAddin.client.tool;
public class ArcGisPro
{
-
+ [McpServerTool, Description("ArcGIS Pro Tool")]
+ public static async Task ArcGisProTool(string toolName, List toolParams)
+ {
+ // Call the ArcGIS Pro method and get the result
+ var result = await server.CallArcGISPro.CallArcGISProTool(toolName, toolParams);
+
+ // Serialize the result back to a JSON string
+ return result;
+ }
}
\ No newline at end of file
diff --git a/host/CallMcp.cs b/host/CallMcp.cs
index a98488d..0e48789 100644
--- a/host/CallMcp.cs
+++ b/host/CallMcp.cs
@@ -18,8 +18,8 @@ namespace LinkToolAddin.host
log.Info("通过反射调用内部MCP工具");
var jsonRpcEntity = JsonConvert.DeserializeObject(jsonRpcString);
- Type type = Type.GetType("LinkToolAddin.client."+jsonRpcEntity.Method.Split('.')[0]);
- MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split('.')[1],BindingFlags.Public | BindingFlags.Static);
+ Type type = Type.GetType("LinkToolAddin.client.tool"+jsonRpcEntity.Method.Split(':')[0]);
+ MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split(':')[1],BindingFlags.Public | BindingFlags.Static);
var task = method.Invoke(null, new object[] { jsonRpcEntity.Params }) as Task;
JsonRpcResultEntity result = await task;
return JsonConvert.SerializeObject(result);
diff --git a/host/Gateway.cs b/host/Gateway.cs
index 2b209b1..847fcd3 100644
--- a/host/Gateway.cs
+++ b/host/Gateway.cs
@@ -1,7 +1,13 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
+using System.Threading.Tasks;
using System.Xml.Linq;
using LinkToolAddin.client;
using LinkToolAddin.client.prompt;
@@ -10,13 +16,22 @@ using LinkToolAddin.host.llm.entity;
using LinkToolAddin.host.mcp;
using LinkToolAddin.host.prompt;
using LinkToolAddin.message;
+using LinkToolAddin.server;
+using LinkToolAddin.ui.dockpane;
+using log4net;
+using Microsoft.Extensions.AI;
+using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Types;
using Newtonsoft.Json;
+using Newtonsoft.Json.Schema;
+using Newtonsoft.Json.Schema.Generation;
+using Tool = LinkToolAddin.host.mcp.Tool;
namespace LinkToolAddin.host;
public class Gateway
{
+ private static ILog log = LogManager.GetLogger(typeof(Gateway));
public static async void SendMessage(string message, string model, string gdbPath, Action callback)
{
Llm bailian = new Bailian
@@ -24,10 +39,12 @@ public class Gateway
api_key = "sk-db177155677e438f832860e7f4da6afc"
};
List messages = new List();
+ string toolInfos = await GetToolInfos(new McpServerList());
+ log.Info(SystemPrompt.SysPrompt(gdbPath, toolInfos));
messages.Add(new Message
{
Role = "system",
- Content = SystemPrompt.SysPromptTemplate
+ Content = SystemPrompt.SysPrompt(gdbPath, toolInfos)
});
messages.Add(new Message
{
@@ -37,7 +54,7 @@ public class Gateway
bool goOn = true;
string pattern = "[\\s\\S]*?<\\/tool_use>";
string promptPattern = "[\\s\\S]*?<\\/prompt>";
- Dictionary servers = new Dictionary();
+ McpServerList mcpServerList = new McpServerList();
while (goOn)
{
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
@@ -62,7 +79,7 @@ public class Gateway
Dictionary toolParams = JsonConvert.DeserializeObject>(toolArgs);
string serverName = fullToolName.Contains(":") ? fullToolName.Split(':')[0] : fullToolName;
string toolName = fullToolName.Contains(":") ? fullToolName.Split(':')[1] : fullToolName;
- McpServer mcpServer = servers[serverName];
+ McpServer mcpServer = mcpServerList.GetServer(serverName);
if (mcpServer is SseMcpServer)
{
SseMcpServer sseMcpServer = mcpServer as SseMcpServer;
@@ -111,6 +128,55 @@ public class Gateway
Content = JsonConvert.SerializeObject(toolResponse)
});
callback?.Invoke(toolMessageItem);
+ }else if (mcpServer is InnerMcpServer)
+ {
+ Type type = Type.GetType("LinkToolAddin.client.tool."+serverName);
+ MethodInfo method = type.GetMethod(toolName,BindingFlags.Public | BindingFlags.Static);
+ var task = method.Invoke(null, toolParams.Values.ToArray()) as Task;
+ JsonRpcResultEntity innerResult = await task;
+ if (innerResult is JsonRpcErrorEntity)
+ {
+ MessageListItem toolMessageItem = new ToolMessageItem
+ {
+ toolName = toolName,
+ toolParams = toolParams,
+ type = MessageType.TOOL_MESSAGE,
+ status = "fail",
+ content = JsonConvert.SerializeObject(innerResult)
+ };
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = SystemPrompt.ErrorPromptTemplate
+ });
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = JsonConvert.SerializeObject(innerResult)
+ });
+ callback?.Invoke(toolMessageItem);
+ }else if (innerResult is JsonRpcSuccessEntity)
+ {
+ MessageListItem toolMessageItem = new ToolMessageItem
+ {
+ toolName = toolName,
+ toolParams = toolParams,
+ type = MessageType.TOOL_MESSAGE,
+ status = "success",
+ content = JsonConvert.SerializeObject(innerResult)
+ };
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = SystemPrompt.ContinuePromptTemplate
+ });
+ messages.Add(new Message
+ {
+ Role = "user",
+ Content = JsonConvert.SerializeObject(innerResult)
+ });
+ callback?.Invoke(toolMessageItem);
+ }
}
}
else if (Regex.IsMatch(reponse, promptPattern))
@@ -145,6 +211,115 @@ public class Gateway
}
}
+ private static async Task GetToolInfos(McpServerList mcpServerList)
+ {
+ StringBuilder toolInfos = new StringBuilder();
+ foreach (McpServer mcpServer in mcpServerList.GetAllServers())
+ {
+ if (mcpServer is InnerMcpServer)
+ {
+ string serverName = mcpServer.Name;
+ if (serverName is null)
+ {
+ continue;
+ }
+ Type type = Type.GetType("LinkToolAddin.client.tool." + serverName);
+ Type type2 = Type.GetType("LinkToolAddin.client.tool.ArcGisPro");
+ MethodInfo[] methods = type.GetMethods();
+ foreach (MethodInfo method in methods)
+ {
+ if (method.IsPublic && method.IsStatic)
+ {
+ string methodName = method.Name;
+ string methodDescription = method.GetCustomAttribute()?.Description;
+ string methodParamSchema = GenerateMethodParamSchema(method);
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = methodName,
+ Description = methodDescription,
+ Arguments = methodParamSchema
+ }
+ };
+ toolInfos.AppendLine(JsonConvert.DeserializeXmlNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
+ }
+ }
+ }
+ else if(mcpServer is SseMcpServer)
+ {
+ SseMcpClient client = new SseMcpClient((mcpServer as SseMcpServer).BaseUrl);
+ IList tools = await client.GetToolListAsync();
+ foreach (McpClientTool tool in tools)
+ {
+ string toolName = tool.Name;
+ string toolDescription = tool.Description;
+ string toolParamSchema = tool.JsonSchema.ToString();
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = toolName,
+ Description = toolDescription,
+ Arguments = toolParamSchema
+ }
+ };
+ toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
+ }
+ }else if (mcpServer is StdioMcpServer)
+ {
+ StdioMcpClient client = new StdioMcpClient((mcpServer as StdioMcpServer).Command, (mcpServer as StdioMcpServer).Args);
+ IList tools = await client.GetToolListAsync();
+ foreach (McpClientTool tool in tools)
+ {
+ string toolName = tool.Name;
+ string toolDescription = tool.Description;
+ string toolParamSchema = tool.JsonSchema.ToString();
+ McpToolDefinition toolDefinition = new McpToolDefinition
+ {
+ Tool = new Tool
+ {
+ Name = toolName,
+ Description = toolDescription,
+ Arguments = toolParamSchema
+ }
+ };
+ toolInfos.AppendLine(JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(toolDefinition)).ToString());
+ }
+ }
+ }
+ return toolInfos.ToString();
+ }
+
+ private static string GenerateMethodParamSchema(MethodInfo method)
+ {
+ var generator = new JSchemaGenerator
+ {
+ // 启用属性注解处理
+ DefaultRequired = Required.DisallowNull,
+ SchemaReferenceHandling = SchemaReferenceHandling.None
+ };
+
+ var paramSchema = new JSchema { Type = JSchemaType.Object };
+
+ foreach (ParameterInfo param in method.GetParameters())
+ {
+ // 生成参数类型的基础Schema
+ JSchema typeSchema = generator.Generate(param.ParameterType);
+
+ // 添加Description描述
+ var descriptionAttr = param.GetCustomAttribute();
+ if (descriptionAttr != null)
+ {
+ typeSchema.Description = descriptionAttr.Description; // 网页6的Description特性处理
+ }
+
+ paramSchema.Properties.Add(param.Name, typeSchema);
+ }
+
+ return paramSchema.ToString();
+ }
+
public static async void TestChatMessage(string message, string model, string gdbPath,
Action callback)
{
@@ -178,7 +353,7 @@ public class Gateway
callback?.Invoke(toolListItem);
}
- public static async void TestWOrkflow(string message, string model, string gdbPath, Action callback)
+ public static async void TestWorkflow(string message, string model, string gdbPath, Action callback)
{
Thread.Sleep(2000);
MessageListItem chatListItem = new ChatMessageItem
diff --git a/host/McpServerList.cs b/host/McpServerList.cs
index 48f66c4..52dffd7 100644
--- a/host/McpServerList.cs
+++ b/host/McpServerList.cs
@@ -1,6 +1,54 @@
-namespace LinkToolAddin.host;
+using System.Collections.Generic;
+using LinkToolAddin.host.mcp;
+
+namespace LinkToolAddin.host;
public class McpServerList
{
+ private Dictionary servers = new Dictionary();
+
+ public McpServerList()
+ {
+ servers.Add("gaode",new SseMcpServer
+ {
+ Name = "gaode",
+ Type = "sse",
+ Description = "高德地图API",
+ IsActive = true,
+ BaseUrl = "https://mcp.amap.com/sse?key=ed418512c94ade8f83d42c37b77d2bb2",
+ Headers = new Dictionary()
+ {
+ {"Content-Type","application/json"}
+ }
+ });
+ servers.Add("arcgis", new InnerMcpServer
+ {
+ Name = "ArcGisPro",
+ Type = "inner",
+ Description = "可以调用arcgis的地理处理工具或执行python代码等",
+ IsActive = true
+ });
+ }
+ public McpServer GetServer(string name)
+ {
+ if (servers.ContainsKey(name))
+ {
+ return servers[name];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public List GetAllServers()
+ {
+ List serverList = new List();
+ foreach (var server in servers)
+ {
+ serverList.Add(server.Value);
+ }
+ return serverList;
+ }
}
\ No newline at end of file
diff --git a/host/mcp/InnerMcpServer.cs b/host/mcp/InnerMcpServer.cs
index ccf5884..2b06319 100644
--- a/host/mcp/InnerMcpServer.cs
+++ b/host/mcp/InnerMcpServer.cs
@@ -1,6 +1,24 @@
-namespace LinkToolAddin.host.mcp;
-
-public class InnerMcpServer
+namespace LinkToolAddin.host.mcp
{
-
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class InnerMcpServer : McpServer
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("isActive")]
+ public bool IsActive { get; set; }
+ }
}
\ No newline at end of file
diff --git a/host/mcp/McpToolDefinition.cs b/host/mcp/McpToolDefinition.cs
index 3bd9adc..6e79610 100644
--- a/host/mcp/McpToolDefinition.cs
+++ b/host/mcp/McpToolDefinition.cs
@@ -1,6 +1,27 @@
-namespace LinkToolAddin.host.mcp;
-
-public class McpDefinition
+namespace LinkToolAddin.host.mcp
{
-
+ using System;
+ using System.Collections.Generic;
+
+ using System.Globalization;
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Converters;
+
+ public partial class McpToolDefinition
+ {
+ [JsonProperty("tool")]
+ public Tool Tool { get; set; }
+ }
+
+ public partial class Tool
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("arguments")]
+ public string Arguments { get; set; }
+ }
}
\ No newline at end of file
diff --git a/host/prompt/SystemPrompt.cs b/host/prompt/SystemPrompt.cs
index bcd3768..186439b 100644
--- a/host/prompt/SystemPrompt.cs
+++ b/host/prompt/SystemPrompt.cs
@@ -8,4 +8,12 @@ public class SystemPrompt
public static string ErrorPromptTemplate = "执行上一个工具的时候出现以下错误,请根据报错信息重试";
+ public static string SysPrompt(string gdbPath, string toolInfos)
+ {
+ string sysPrompt = SysPromptTemplate;
+ sysPrompt = sysPrompt.Replace("{{gdbPath}}", gdbPath);
+ sysPrompt = sysPrompt.Replace("{{toolInfos}}", toolInfos);
+ return sysPrompt;
+ }
+
}
\ No newline at end of file
diff --git a/server/CallArcGISPro.cs b/server/CallArcGISPro.cs
index 39f5ef0..049fd3f 100644
--- a/server/CallArcGISPro.cs
+++ b/server/CallArcGISPro.cs
@@ -10,14 +10,29 @@ namespace LinkToolAddin.server;
public class CallArcGISPro
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CallArcGISPro));
+
public async static Task CallArcGISProTool(string toolName, List toolParams)
{
var results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
- log.Info($"CallArcGISProTool: {toolName} | {toolParams}");
- return new JsonRpcSuccessEntity()
+ JsonRpcResultEntity jsonRpcResultEntity;
+ if (results.ErrorCode == 0)
{
- Id = 1,
- Result = results.ToString()
- };
+ jsonRpcResultEntity = new JsonRpcErrorEntity()
+ {
+ Error = new Error()
+ {
+ Code = results.ErrorCode,
+ Message = results.ErrorMessages.ToString()
+ }
+ };
+ }
+ else
+ {
+ jsonRpcResultEntity = new JsonRpcSuccessEntity
+ {
+ Result = results.Messages.ToString()
+ };
+ }
+ return jsonRpcResultEntity;
}
}
\ No newline at end of file
diff --git a/server/JsonRpcErrorEntity.cs b/server/JsonRpcErrorEntity.cs
index 46381de..ad0d795 100644
--- a/server/JsonRpcErrorEntity.cs
+++ b/server/JsonRpcErrorEntity.cs
@@ -2,10 +2,10 @@ namespace LinkToolAddin.server
{
using Newtonsoft.Json;
- public partial class JsonRpcErrorEntity
+ public partial class JsonRpcErrorEntity : JsonRpcResultEntity
{
[JsonProperty("jsonrpc")]
- public string Jsonrpc { get; set; }
+ public string Jsonrpc { get; set; } = "2.0";
[JsonProperty("error")]
public Error Error { get; set; }
diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs
index 6f33bb6..802464a 100644
--- a/ui/dockpane/TestDockpane.xaml.cs
+++ b/ui/dockpane/TestDockpane.xaml.cs
@@ -146,7 +146,7 @@ namespace LinkToolAddin.ui.dockpane
private void TestWorkflow_OnClick(object sender, RoutedEventArgs e)
{
- Gateway.SendMessage("你好","qwen-max","test.gdb",ShowMessage);
+ Gateway.SendMessage("你有什么工具","qwen-max","test.gdb",ShowMessage);
}
public void ShowMessage(MessageListItem msg)