自主实现JSON Schema生成,接入数据查看工具

This commit is contained in:
PeterZhong 2025-05-23 20:47:36 +08:00
parent b6397e5db3
commit 3b1f65b3ba
4 changed files with 215 additions and 9 deletions

View File

@ -24,17 +24,47 @@ public class ArcGisPro
return result;
}
[McpServerTool, Description("查看指定数据的属性,包括坐标系、范围、数据类型等")]
[McpServerTool, Description("查看指定数据的坐标系、范围、几何类型、是否有Z坐标和M坐标获取字段列表等")]
public static async Task<JsonRpcResultEntity> DataProperty(string datasetPath,string dataName)
{
using Geodatabase gdb = new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(datasetPath)));
FeatureClass featureClass = gdb.OpenDataset<FeatureClass>(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<FeatureClass>(dataName);
FeatureClassDefinition featureClassDefinition = featureClass.GetDefinition();
SpatialReference spatialReference = featureClassDefinition.GetSpatialReference();
GeometryType geometryType = featureClassDefinition.GetShapeType();
result = new JsonRpcSuccessEntity()
{
Id = 1,
Result = JsonConvert.SerializeObject(new Dictionary<string, object>()
{
{"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;
}

View File

@ -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<JsonRpcResultEntity> 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;
}
}

View File

@ -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<string, object>();
var required = new List<string>();
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<string, object>
{
{ "$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<T>
if (IsCollectionType(type, out Type elementType))
{
return new Dictionary<string, object>
{
{ "type", "array" },
{ "items", GenerateSchemaForType(elementType) }
};
}
// 处理基本类型int, string, bool, etc.
if (IsPrimitiveType(type))
{
string jsonType = MapClrTypeToJsonType(type);
var schema = new Dictionary<string, object> { { "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<string, object>();
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
props[prop.Name] = GenerateSchemaForType(prop.PropertyType);
}
return new Dictionary<string, object>
{
{ "type", "object" },
{ "properties", props }
};
}
// 默认情况
return new Dictionary<string, object> { { "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";
}
}

View File

@ -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<DescriptionAttribute>()?.Description;
string methodParamSchema = GenerateMethodParamSchema(method);
string methodParamSchema = LinkToolAddin.common.JsonSchemaGenerator.GenerateJsonSchema(method);
McpToolDefinition toolDefinition = new McpToolDefinition
{
Tool = new Tool