diff --git a/client/tool/ArcGisPro.cs b/client/tool/ArcGisPro.cs index 2808200..da2e604 100644 --- a/client/tool/ArcGisPro.cs +++ b/client/tool/ArcGisPro.cs @@ -24,17 +24,47 @@ public class ArcGisPro return result; } - [McpServerTool, Description("查看指定数据的属性,包括坐标系、范围、数据类型等")] + [McpServerTool, Description("查看指定数据的坐标系、范围、几何类型、是否有Z坐标和M坐标,获取字段列表等")] public static async Task DataProperty(string datasetPath,string dataName) { - using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath))); - FeatureClass featureClass = gdb.OpenDataset(dataName); - FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition(); - JsonRpcResultEntity result = new JsonRpcSuccessEntity() + JsonRpcResultEntity result = new JsonRpcResultEntity(); + await QueuedTask.Run(() => { - Id = 1, - Result = JsonConvert.SerializeObject(featureClassDefinition) - }; + try + { + using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath))); + FeatureClass featureClass = gdb.OpenDataset(dataName); + FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition(); + SpatialReference spatialReference = featureClassDefinition.GetSpatialReference(); + GeometryType geometryType = featureClassDefinition.GetShapeType(); + result = new JsonRpcSuccessEntity() + { + Id = 1, + Result = JsonConvert.SerializeObject(new Dictionary() + { + {"spatialReference", spatialReference.Name+"(WKID:"+spatialReference.Wkid+")"}, + {"dataName", dataName}, + {"geometryType", geometryType.ToString()}, + {"hasZValue", featureClassDefinition.HasZ()}, + {"hasMValue", featureClassDefinition.HasM()}, + {"fields",featureClassDefinition.GetFields()} + }) + }; + return result; + } + catch (Exception ex) + { + result = new JsonRpcErrorEntity() + { + Error = new Error() + { + Message = ex.Message + }, + Id = 1 + }; + return result; + } + }); return result; } diff --git a/client/tool/KnowledgeBase.cs b/client/tool/KnowledgeBase.cs new file mode 100644 index 0000000..9d2b162 --- /dev/null +++ b/client/tool/KnowledgeBase.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; +using LinkToolAddin.host.llm.entity; +using LinkToolAddin.resource; +using LinkToolAddin.server; +using ModelContextProtocol.Server; +using Newtonsoft.Json; + +namespace LinkToolAddin.client.tool; + +public class KnowledgeBase +{ + [McpServerTool, Description("可以查询ArcGIS Pro的帮助文档获取关于地理处理工具使用参数的说明")] + public static async Task QueryArcgisHelpDoc(string query) + { + DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProHelpDoc); + KnowledgeResult knowledgeResult = await docDb.Retrieve(query); + JsonRpcResultEntity result = new JsonRpcSuccessEntity() + { + Result = JsonConvert.SerializeObject(knowledgeResult.ChunkList), + }; + return result; + } +} \ No newline at end of file diff --git a/common/JsonSchemaGenerator.cs b/common/JsonSchemaGenerator.cs new file mode 100644 index 0000000..ff799c4 --- /dev/null +++ b/common/JsonSchemaGenerator.cs @@ -0,0 +1,150 @@ +using System.Collections.ObjectModel; + +namespace LinkToolAddin.common; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.Json; + +public static class JsonSchemaGenerator +{ + public static string GenerateJsonSchema(MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + var properties = new Dictionary(); + var required = new List(); + + foreach (var param in parameters) + { + var paramName = param.Name ?? throw new InvalidOperationException("参数没有名称。"); + var paramSchema = GenerateSchemaForType(param.ParameterType); + properties[paramName] = paramSchema; + + if (!param.IsOptional) + { + required.Add(paramName); + } + } + + var schemaRoot = new Dictionary + { + { "$schema", "http://json-schema.org/draft-07/schema#" }, + { "type", "object" }, + { "properties", properties } + }; + + if (required.Count > 0) + { + schemaRoot["required"] = required; + } + + var options = new JsonSerializerOptions { WriteIndented = true }; + return JsonSerializer.Serialize(schemaRoot, options); + } + + private static object GenerateSchemaForType(Type type) + { + // 处理可空类型 + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var underlyingType = Nullable.GetUnderlyingType(type); + return new[] { GenerateSchemaForType(underlyingType), "null" }; + } + + // 处理集合类型(数组或IEnumerable) + if (IsCollectionType(type, out Type elementType)) + { + return new Dictionary + { + { "type", "array" }, + { "items", GenerateSchemaForType(elementType) } + }; + } + + // 处理基本类型(int, string, bool, etc.) + if (IsPrimitiveType(type)) + { + string jsonType = MapClrTypeToJsonType(type); + var schema = new Dictionary { { "type", jsonType } }; + + if (type == typeof(DateTime)) + schema["format"] = "date-time"; + else if (type == typeof(Guid)) + schema["format"] = "uuid"; + + return schema; + } + + // 处理复杂类型(类、结构体) + if (type.IsClass || type.IsValueType) + { + var props = new Dictionary(); + foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + props[prop.Name] = GenerateSchemaForType(prop.PropertyType); + } + + return new Dictionary + { + { "type", "object" }, + { "properties", props } + }; + } + + // 默认情况 + return new Dictionary { { "type", "any" } }; + } + + private static bool IsCollectionType(Type type, out Type elementType) + { + if (type == typeof(string)) + { + elementType = null; + return false; + } + + if (type.IsArray) + { + elementType = type.GetElementType(); + return true; + } + + if (type.IsGenericType) + { + var genericTypeDef = type.GetGenericTypeDefinition(); + if (genericTypeDef == typeof(IEnumerable<>) || + genericTypeDef == typeof(List<>) || + genericTypeDef == typeof(Collection<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + } + + elementType = null; + return false; + } + + private static bool IsPrimitiveType(Type type) + { + return type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(Guid); + } + + private static string MapClrTypeToJsonType(Type type) + { + if (type == typeof(int) || type == typeof(short) || type == typeof(long) || + type == typeof(uint) || type == typeof(ushort) || type == typeof(ulong)) + return "integer"; + if (type == typeof(float) || type == typeof(double) || type == typeof(decimal)) + return "number"; + if (type == typeof(bool)) + return "boolean"; + if (type == typeof(string)) + return "string"; + if (type == typeof(DateTime) || type == typeof(Guid)) + return "string"; + return "any"; + } +} \ No newline at end of file diff --git a/host/Gateway.cs b/host/Gateway.cs index 15e128d..fca37cc 100644 --- a/host/Gateway.cs +++ b/host/Gateway.cs @@ -30,6 +30,7 @@ using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; using Newtonsoft.Json.Schema.Generation; using Tool = LinkToolAddin.host.mcp.Tool; +using LinkToolAddin.common; namespace LinkToolAddin.host; @@ -471,7 +472,7 @@ public class Gateway { string methodName = method.Name; string methodDescription = method.GetCustomAttribute()?.Description; - string methodParamSchema = GenerateMethodParamSchema(method); + string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method); McpToolDefinition toolDefinition = new McpToolDefinition { Tool = new Tool