lint: fix code format
This commit is contained in:
parent
4663794ba6
commit
55aac1cb9b
@ -1,9 +1,9 @@
|
|||||||
import { getConfigDir } from '@main/utils/file'
|
import { getConfigDir } from '@main/utils/file'
|
||||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||||
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js'
|
||||||
|
import { Mutex } from 'async-mutex' // 引入 Mutex
|
||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Mutex } from 'async-mutex' // 引入 Mutex
|
|
||||||
|
|
||||||
// Define memory file path
|
// Define memory file path
|
||||||
const defaultMemoryPath = path.join(getConfigDir(), 'memory.json')
|
const defaultMemoryPath = path.join(getConfigDir(), 'memory.json')
|
||||||
@ -62,7 +62,10 @@ class KnowledgeGraphManager {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to ensure memory path exists:', error)
|
console.error('Failed to ensure memory path exists:', error)
|
||||||
// Propagate the error or handle it more gracefully depending on requirements
|
// Propagate the error or handle it more gracefully depending on requirements
|
||||||
throw new McpError(ErrorCode.InternalError, `Failed to ensure memory path: ${error instanceof Error ? error.message : String(error)}`)
|
throw new McpError(
|
||||||
|
ErrorCode.InternalError,
|
||||||
|
`Failed to ensure memory path: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,8 +84,8 @@ class KnowledgeGraphManager {
|
|||||||
const graph: KnowledgeGraph = JSON.parse(data)
|
const graph: KnowledgeGraph = JSON.parse(data)
|
||||||
this.entities.clear()
|
this.entities.clear()
|
||||||
this.relations.clear()
|
this.relations.clear()
|
||||||
graph.entities.forEach(entity => this.entities.set(entity.name, entity))
|
graph.entities.forEach((entity) => this.entities.set(entity.name, entity))
|
||||||
graph.relations.forEach(relation => this.relations.add(this._serializeRelation(relation)))
|
graph.relations.forEach((relation) => this.relations.add(this._serializeRelation(relation)))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {
|
||||||
// File doesn't exist (should have been created by _ensureMemoryPathExists, but handle defensively)
|
// File doesn't exist (should have been created by _ensureMemoryPathExists, but handle defensively)
|
||||||
@ -90,14 +93,17 @@ class KnowledgeGraphManager {
|
|||||||
this.relations = new Set()
|
this.relations = new Set()
|
||||||
await this._persistGraph() // Create the file with empty structure
|
await this._persistGraph() // Create the file with empty structure
|
||||||
} else if (error instanceof SyntaxError) {
|
} else if (error instanceof SyntaxError) {
|
||||||
console.error('Failed to parse memory.json, initializing with empty graph:', error)
|
console.error('Failed to parse memory.json, initializing with empty graph:', error)
|
||||||
// If JSON is invalid, start fresh and overwrite the corrupted file
|
// If JSON is invalid, start fresh and overwrite the corrupted file
|
||||||
this.entities = new Map()
|
this.entities = new Map()
|
||||||
this.relations = new Set()
|
this.relations = new Set()
|
||||||
await this._persistGraph()
|
await this._persistGraph()
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed to load knowledge graph from disk:', error)
|
console.error('Failed to load knowledge graph from disk:', error)
|
||||||
throw new McpError(ErrorCode.InternalError, `Failed to load graph: ${error instanceof Error ? error.message : String(error)}`)
|
throw new McpError(
|
||||||
|
ErrorCode.InternalError,
|
||||||
|
`Failed to load graph: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +114,16 @@ class KnowledgeGraphManager {
|
|||||||
try {
|
try {
|
||||||
const graphData: KnowledgeGraph = {
|
const graphData: KnowledgeGraph = {
|
||||||
entities: Array.from(this.entities.values()),
|
entities: Array.from(this.entities.values()),
|
||||||
relations: Array.from(this.relations).map(rStr => this._deserializeRelation(rStr))
|
relations: Array.from(this.relations).map((rStr) => this._deserializeRelation(rStr))
|
||||||
}
|
}
|
||||||
await fs.writeFile(this.memoryPath, JSON.stringify(graphData, null, 2))
|
await fs.writeFile(this.memoryPath, JSON.stringify(graphData, null, 2))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save knowledge graph:', error)
|
console.error('Failed to save knowledge graph:', error)
|
||||||
// Decide how to handle write errors - potentially retry or notify
|
// Decide how to handle write errors - potentially retry or notify
|
||||||
throw new McpError(ErrorCode.InternalError, `Failed to save graph: ${error instanceof Error ? error.message : String(error)}`)
|
throw new McpError(
|
||||||
|
ErrorCode.InternalError,
|
||||||
|
`Failed to save graph: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
release()
|
release()
|
||||||
}
|
}
|
||||||
@ -133,10 +142,10 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
async createEntities(entities: Entity[]): Promise<Entity[]> {
|
async createEntities(entities: Entity[]): Promise<Entity[]> {
|
||||||
const newEntities: Entity[] = []
|
const newEntities: Entity[] = []
|
||||||
entities.forEach(entity => {
|
entities.forEach((entity) => {
|
||||||
if (!this.entities.has(entity.name)) {
|
if (!this.entities.has(entity.name)) {
|
||||||
// Ensure observations is always an array
|
// Ensure observations is always an array
|
||||||
const newEntity = { ...entity, observations: Array.isArray(entity.observations) ? entity.observations : [] };
|
const newEntity = { ...entity, observations: Array.isArray(entity.observations) ? entity.observations : [] }
|
||||||
this.entities.set(entity.name, newEntity)
|
this.entities.set(entity.name, newEntity)
|
||||||
newEntities.push(newEntity)
|
newEntities.push(newEntity)
|
||||||
}
|
}
|
||||||
@ -149,11 +158,11 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
async createRelations(relations: Relation[]): Promise<Relation[]> {
|
async createRelations(relations: Relation[]): Promise<Relation[]> {
|
||||||
const newRelations: Relation[] = []
|
const newRelations: Relation[] = []
|
||||||
relations.forEach(relation => {
|
relations.forEach((relation) => {
|
||||||
// Ensure related entities exist before creating a relation
|
// Ensure related entities exist before creating a relation
|
||||||
if (!this.entities.has(relation.from) || !this.entities.has(relation.to)) {
|
if (!this.entities.has(relation.from) || !this.entities.has(relation.to)) {
|
||||||
console.warn(`Skipping relation creation: Entity not found for relation ${relation.from} -> ${relation.to}`)
|
console.warn(`Skipping relation creation: Entity not found for relation ${relation.from} -> ${relation.to}`)
|
||||||
return; // Skip this relation
|
return // Skip this relation
|
||||||
}
|
}
|
||||||
const relationStr = this._serializeRelation(relation)
|
const relationStr = this._serializeRelation(relation)
|
||||||
if (!this.relations.has(relationStr)) {
|
if (!this.relations.has(relationStr)) {
|
||||||
@ -172,20 +181,20 @@ class KnowledgeGraphManager {
|
|||||||
): Promise<{ entityName: string; addedObservations: string[] }[]> {
|
): Promise<{ entityName: string; addedObservations: string[] }[]> {
|
||||||
const results: { entityName: string; addedObservations: string[] }[] = []
|
const results: { entityName: string; addedObservations: string[] }[] = []
|
||||||
let changed = false
|
let changed = false
|
||||||
observations.forEach(o => {
|
observations.forEach((o) => {
|
||||||
const entity = this.entities.get(o.entityName)
|
const entity = this.entities.get(o.entityName)
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
// Option 1: Throw error
|
// Option 1: Throw error
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Entity with name ${o.entityName} not found`)
|
throw new McpError(ErrorCode.InvalidParams, `Entity with name ${o.entityName} not found`)
|
||||||
// Option 2: Skip and warn
|
// Option 2: Skip and warn
|
||||||
// console.warn(`Entity with name ${o.entityName} not found when adding observations. Skipping.`);
|
// console.warn(`Entity with name ${o.entityName} not found when adding observations. Skipping.`);
|
||||||
// return;
|
// return;
|
||||||
}
|
}
|
||||||
// Ensure observations array exists
|
// Ensure observations array exists
|
||||||
if (!Array.isArray(entity.observations)) {
|
if (!Array.isArray(entity.observations)) {
|
||||||
entity.observations = [];
|
entity.observations = []
|
||||||
}
|
}
|
||||||
const newObservations = o.contents.filter(content => !entity.observations.includes(content))
|
const newObservations = o.contents.filter((content) => !entity.observations.includes(content))
|
||||||
if (newObservations.length > 0) {
|
if (newObservations.length > 0) {
|
||||||
entity.observations.push(...newObservations)
|
entity.observations.push(...newObservations)
|
||||||
results.push({ entityName: o.entityName, addedObservations: newObservations })
|
results.push({ entityName: o.entityName, addedObservations: newObservations })
|
||||||
@ -206,7 +215,7 @@ class KnowledgeGraphManager {
|
|||||||
const namesToDelete = new Set(entityNames)
|
const namesToDelete = new Set(entityNames)
|
||||||
|
|
||||||
// Delete entities
|
// Delete entities
|
||||||
namesToDelete.forEach(name => {
|
namesToDelete.forEach((name) => {
|
||||||
if (this.entities.delete(name)) {
|
if (this.entities.delete(name)) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
@ -214,14 +223,14 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
// Delete relations involving deleted entities
|
// Delete relations involving deleted entities
|
||||||
const relationsToDelete = new Set<string>()
|
const relationsToDelete = new Set<string>()
|
||||||
this.relations.forEach(relStr => {
|
this.relations.forEach((relStr) => {
|
||||||
const rel = this._deserializeRelation(relStr)
|
const rel = this._deserializeRelation(relStr)
|
||||||
if (namesToDelete.has(rel.from) || namesToDelete.has(rel.to)) {
|
if (namesToDelete.has(rel.from) || namesToDelete.has(rel.to)) {
|
||||||
relationsToDelete.add(relStr)
|
relationsToDelete.add(relStr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
relationsToDelete.forEach(relStr => {
|
relationsToDelete.forEach((relStr) => {
|
||||||
if (this.relations.delete(relStr)) {
|
if (this.relations.delete(relStr)) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
@ -234,12 +243,12 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
|
async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
|
||||||
let changed = false
|
let changed = false
|
||||||
deletions.forEach(d => {
|
deletions.forEach((d) => {
|
||||||
const entity = this.entities.get(d.entityName)
|
const entity = this.entities.get(d.entityName)
|
||||||
if (entity && Array.isArray(entity.observations)) {
|
if (entity && Array.isArray(entity.observations)) {
|
||||||
const initialLength = entity.observations.length
|
const initialLength = entity.observations.length
|
||||||
const observationsToDelete = new Set(d.observations)
|
const observationsToDelete = new Set(d.observations)
|
||||||
entity.observations = entity.observations.filter(o => !observationsToDelete.has(o))
|
entity.observations = entity.observations.filter((o) => !observationsToDelete.has(o))
|
||||||
if (entity.observations.length !== initialLength) {
|
if (entity.observations.length !== initialLength) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
@ -252,7 +261,7 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
async deleteRelations(relations: Relation[]): Promise<void> {
|
async deleteRelations(relations: Relation[]): Promise<void> {
|
||||||
let changed = false
|
let changed = false
|
||||||
relations.forEach(rel => {
|
relations.forEach((rel) => {
|
||||||
const relStr = this._serializeRelation(rel)
|
const relStr = this._serializeRelation(rel)
|
||||||
if (this.relations.delete(relStr)) {
|
if (this.relations.delete(relStr)) {
|
||||||
changed = true
|
changed = true
|
||||||
@ -266,27 +275,29 @@ class KnowledgeGraphManager {
|
|||||||
// Read the current state from memory
|
// Read the current state from memory
|
||||||
async readGraph(): Promise<KnowledgeGraph> {
|
async readGraph(): Promise<KnowledgeGraph> {
|
||||||
// Return a deep copy to prevent external modification of the internal state
|
// Return a deep copy to prevent external modification of the internal state
|
||||||
return JSON.parse(JSON.stringify({
|
return JSON.parse(
|
||||||
|
JSON.stringify({
|
||||||
entities: Array.from(this.entities.values()),
|
entities: Array.from(this.entities.values()),
|
||||||
relations: Array.from(this.relations).map(rStr => this._deserializeRelation(rStr))
|
relations: Array.from(this.relations).map((rStr) => this._deserializeRelation(rStr))
|
||||||
}));
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search operates on the in-memory graph
|
// Search operates on the in-memory graph
|
||||||
async searchNodes(query: string): Promise<KnowledgeGraph> {
|
async searchNodes(query: string): Promise<KnowledgeGraph> {
|
||||||
const lowerCaseQuery = query.toLowerCase()
|
const lowerCaseQuery = query.toLowerCase()
|
||||||
const filteredEntities = Array.from(this.entities.values()).filter(
|
const filteredEntities = Array.from(this.entities.values()).filter(
|
||||||
e =>
|
(e) =>
|
||||||
e.name.toLowerCase().includes(lowerCaseQuery) ||
|
e.name.toLowerCase().includes(lowerCaseQuery) ||
|
||||||
e.entityType.toLowerCase().includes(lowerCaseQuery) ||
|
e.entityType.toLowerCase().includes(lowerCaseQuery) ||
|
||||||
(Array.isArray(e.observations) && e.observations.some(o => o.toLowerCase().includes(lowerCaseQuery)))
|
(Array.isArray(e.observations) && e.observations.some((o) => o.toLowerCase().includes(lowerCaseQuery)))
|
||||||
)
|
)
|
||||||
|
|
||||||
const filteredEntityNames = new Set(filteredEntities.map(e => e.name))
|
const filteredEntityNames = new Set(filteredEntities.map((e) => e.name))
|
||||||
|
|
||||||
const filteredRelations = Array.from(this.relations)
|
const filteredRelations = Array.from(this.relations)
|
||||||
.map(rStr => this._deserializeRelation(rStr))
|
.map((rStr) => this._deserializeRelation(rStr))
|
||||||
.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
.filter((r) => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities: filteredEntities,
|
entities: filteredEntities,
|
||||||
@ -296,26 +307,26 @@ class KnowledgeGraphManager {
|
|||||||
|
|
||||||
// Open operates on the in-memory graph
|
// Open operates on the in-memory graph
|
||||||
async openNodes(names: string[]): Promise<KnowledgeGraph> {
|
async openNodes(names: string[]): Promise<KnowledgeGraph> {
|
||||||
const nameSet = new Set(names);
|
const nameSet = new Set(names)
|
||||||
const filteredEntities = Array.from(this.entities.values()).filter(e => nameSet.has(e.name));
|
const filteredEntities = Array.from(this.entities.values()).filter((e) => nameSet.has(e.name))
|
||||||
const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
|
const filteredEntityNames = new Set(filteredEntities.map((e) => e.name))
|
||||||
|
|
||||||
const filteredRelations = Array.from(this.relations)
|
const filteredRelations = Array.from(this.relations)
|
||||||
.map(rStr => this._deserializeRelation(rStr))
|
.map((rStr) => this._deserializeRelation(rStr))
|
||||||
.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to));
|
.filter((r) => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entities: filteredEntities,
|
entities: filteredEntities,
|
||||||
relations: filteredRelations
|
relations: filteredRelations
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MemoryServer {
|
class MemoryServer {
|
||||||
public server: Server
|
public server: Server
|
||||||
// Hold the manager instance, initialized asynchronously
|
// Hold the manager instance, initialized asynchronously
|
||||||
private knowledgeGraphManager: KnowledgeGraphManager | null = null;
|
private knowledgeGraphManager: KnowledgeGraphManager | null = null
|
||||||
private initializationPromise: Promise<void>; // To track initialization
|
private initializationPromise: Promise<void> // To track initialization
|
||||||
|
|
||||||
constructor(envPath: string = '') {
|
constructor(envPath: string = '') {
|
||||||
const memoryPath = envPath
|
const memoryPath = envPath
|
||||||
@ -336,33 +347,32 @@ class MemoryServer {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Start initialization, but don't block constructor
|
// Start initialization, but don't block constructor
|
||||||
this.initializationPromise = this._initializeManager(memoryPath);
|
this.initializationPromise = this._initializeManager(memoryPath)
|
||||||
this.setupRequestHandlers(); // Setup handlers immediately
|
this.setupRequestHandlers() // Setup handlers immediately
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private async method to handle manager initialization
|
// Private async method to handle manager initialization
|
||||||
private async _initializeManager(memoryPath: string): Promise<void> {
|
private async _initializeManager(memoryPath: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.knowledgeGraphManager = await KnowledgeGraphManager.create(memoryPath);
|
this.knowledgeGraphManager = await KnowledgeGraphManager.create(memoryPath)
|
||||||
console.log("KnowledgeGraphManager initialized successfully.");
|
console.log('KnowledgeGraphManager initialized successfully.')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to initialize KnowledgeGraphManager:", error);
|
console.error('Failed to initialize KnowledgeGraphManager:', error)
|
||||||
// Server might be unusable, consider how to handle this state
|
// Server might be unusable, consider how to handle this state
|
||||||
// Maybe set a flag and return errors for all tool calls?
|
// Maybe set a flag and return errors for all tool calls?
|
||||||
this.knowledgeGraphManager = null; // Ensure it's null if init fails
|
this.knowledgeGraphManager = null // Ensure it's null if init fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensures the manager is initialized before handling tool calls
|
// Ensures the manager is initialized before handling tool calls
|
||||||
private async _getManager(): Promise<KnowledgeGraphManager> {
|
private async _getManager(): Promise<KnowledgeGraphManager> {
|
||||||
await this.initializationPromise; // Wait for initialization to complete
|
await this.initializationPromise // Wait for initialization to complete
|
||||||
if (!this.knowledgeGraphManager) {
|
if (!this.knowledgeGraphManager) {
|
||||||
throw new McpError(ErrorCode.InternalError, "Memory server failed to initialize. Cannot process requests.");
|
throw new McpError(ErrorCode.InternalError, 'Memory server failed to initialize. Cannot process requests.')
|
||||||
}
|
}
|
||||||
return this.knowledgeGraphManager;
|
return this.knowledgeGraphManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Setup handlers (can be called from constructor)
|
// Setup handlers (can be called from constructor)
|
||||||
setupRequestHandlers() {
|
setupRequestHandlers() {
|
||||||
// ListTools remains largely the same, descriptions might be updated if needed
|
// ListTools remains largely the same, descriptions might be updated if needed
|
||||||
@ -371,196 +381,197 @@ class MemoryServer {
|
|||||||
// Although ListTools itself doesn't *call* the manager, it implies the
|
// Although ListTools itself doesn't *call* the manager, it implies the
|
||||||
// manager is ready to handle calls for those tools.
|
// manager is ready to handle calls for those tools.
|
||||||
try {
|
try {
|
||||||
await this._getManager(); // Wait for initialization before confirming tools are available
|
await this._getManager() // Wait for initialization before confirming tools are available
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If manager failed to init, maybe return an empty tool list or throw?
|
// If manager failed to init, maybe return an empty tool list or throw?
|
||||||
console.error("Cannot list tools, manager initialization failed:", error);
|
console.error('Cannot list tools, manager initialization failed:', error)
|
||||||
return { tools: [] }; // Return empty list if server is not ready
|
return { tools: [] } // Return empty list if server is not ready
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tools: [
|
tools: [
|
||||||
{
|
{
|
||||||
name: 'create_entities',
|
name: 'create_entities',
|
||||||
description: 'Create multiple new entities in the knowledge graph. Skips existing entities.',
|
description: 'Create multiple new entities in the knowledge graph. Skips existing entities.',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
entities: {
|
entities: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string', description: 'The name of the entity' },
|
name: { type: 'string', description: 'The name of the entity' },
|
||||||
entityType: { type: 'string', description: 'The type of the entity' },
|
entityType: { type: 'string', description: 'The type of the entity' },
|
||||||
observations: {
|
observations: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'An array of observation contents associated with the entity',
|
description: 'An array of observation contents associated with the entity',
|
||||||
default: [] // Add default empty array
|
default: [] // Add default empty array
|
||||||
}
|
}
|
||||||
},
|
|
||||||
required: ['name', 'entityType'] // Observations are optional now on creation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['entities']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'create_relations',
|
|
||||||
description: 'Create multiple new relations between EXISTING entities. Skips existing relations or relations with non-existent entities.',
|
|
||||||
inputSchema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
relations: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
from: { type: 'string', description: 'The name of the entity where the relation starts' },
|
|
||||||
to: { type: 'string', description: 'The name of the entity where the relation ends' },
|
|
||||||
relationType: { type: 'string', description: 'The type of the relation' }
|
|
||||||
},
|
|
||||||
required: ['from', 'to', 'relationType']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['relations']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'add_observations',
|
|
||||||
description: 'Add new observations to existing entities. Skips duplicate observations.',
|
|
||||||
inputSchema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
observations: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
entityName: { type: 'string', description: 'The name of the entity to add the observations to' },
|
|
||||||
contents: {
|
|
||||||
type: 'array',
|
|
||||||
items: { type: 'string' },
|
|
||||||
description: 'An array of observation contents to add'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['entityName', 'contents']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['observations']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'delete_entities',
|
|
||||||
description: 'Delete multiple entities and their associated relations.',
|
|
||||||
inputSchema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
entityNames: {
|
|
||||||
type: 'array',
|
|
||||||
items: { type: 'string' },
|
|
||||||
description: 'An array of entity names to delete'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['entityNames']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'delete_observations',
|
|
||||||
description: 'Delete specific observations from entities.',
|
|
||||||
inputSchema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
deletions: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
entityName: { type: 'string', description: 'The name of the entity containing the observations' },
|
|
||||||
observations: {
|
|
||||||
type: 'array',
|
|
||||||
items: { type: 'string' },
|
|
||||||
description: 'An array of observations to delete'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['entityName', 'observations']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['deletions']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'delete_relations',
|
|
||||||
description: 'Delete multiple specific relations.',
|
|
||||||
inputSchema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
relations: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
from: { type: 'string', description: 'The name of the entity where the relation starts' },
|
|
||||||
to: { type: 'string', description: 'The name of the entity where the relation ends' },
|
|
||||||
relationType: { type: 'string', description: 'The type of the relation' }
|
|
||||||
},
|
|
||||||
required: ['from', 'to', 'relationType']
|
|
||||||
},
|
},
|
||||||
description: 'An array of relations to delete'
|
required: ['name', 'entityType'] // Observations are optional now on creation
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
required: ['relations']
|
},
|
||||||
}
|
required: ['entities']
|
||||||
},
|
}
|
||||||
{
|
},
|
||||||
name: 'read_graph',
|
{
|
||||||
description: 'Read the entire knowledge graph from memory.',
|
name: 'create_relations',
|
||||||
inputSchema: {
|
description:
|
||||||
type: 'object',
|
'Create multiple new relations between EXISTING entities. Skips existing relations or relations with non-existent entities.',
|
||||||
properties: {}
|
inputSchema: {
|
||||||
}
|
type: 'object',
|
||||||
},
|
properties: {
|
||||||
{
|
relations: {
|
||||||
name: 'search_nodes',
|
type: 'array',
|
||||||
description: 'Search nodes (entities and relations) in memory based on a query.',
|
items: {
|
||||||
inputSchema: {
|
type: 'object',
|
||||||
type: 'object',
|
properties: {
|
||||||
properties: {
|
from: { type: 'string', description: 'The name of the entity where the relation starts' },
|
||||||
query: {
|
to: { type: 'string', description: 'The name of the entity where the relation ends' },
|
||||||
type: 'string',
|
relationType: { type: 'string', description: 'The type of the relation' }
|
||||||
description: 'The search query to match against entity names, types, and observation content'
|
},
|
||||||
|
required: ['from', 'to', 'relationType']
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
required: ['query']
|
},
|
||||||
}
|
required: ['relations']
|
||||||
},
|
}
|
||||||
{
|
},
|
||||||
name: 'open_nodes',
|
{
|
||||||
description: 'Retrieve specific entities and their connecting relations from memory by name.',
|
name: 'add_observations',
|
||||||
inputSchema: {
|
description: 'Add new observations to existing entities. Skips duplicate observations.',
|
||||||
type: 'object',
|
inputSchema: {
|
||||||
properties: {
|
type: 'object',
|
||||||
names: {
|
properties: {
|
||||||
type: 'array',
|
observations: {
|
||||||
items: { type: 'string' },
|
type: 'array',
|
||||||
description: 'An array of entity names to retrieve'
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
entityName: { type: 'string', description: 'The name of the entity to add the observations to' },
|
||||||
|
contents: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' },
|
||||||
|
description: 'An array of observation contents to add'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['entityName', 'contents']
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
required: ['names']
|
},
|
||||||
}
|
required: ['observations']
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
{
|
||||||
|
name: 'delete_entities',
|
||||||
|
description: 'Delete multiple entities and their associated relations.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
entityNames: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' },
|
||||||
|
description: 'An array of entity names to delete'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['entityNames']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'delete_observations',
|
||||||
|
description: 'Delete specific observations from entities.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
deletions: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
entityName: { type: 'string', description: 'The name of the entity containing the observations' },
|
||||||
|
observations: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' },
|
||||||
|
description: 'An array of observations to delete'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['entityName', 'observations']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['deletions']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'delete_relations',
|
||||||
|
description: 'Delete multiple specific relations.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
relations: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
from: { type: 'string', description: 'The name of the entity where the relation starts' },
|
||||||
|
to: { type: 'string', description: 'The name of the entity where the relation ends' },
|
||||||
|
relationType: { type: 'string', description: 'The type of the relation' }
|
||||||
|
},
|
||||||
|
required: ['from', 'to', 'relationType']
|
||||||
|
},
|
||||||
|
description: 'An array of relations to delete'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['relations']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'read_graph',
|
||||||
|
description: 'Read the entire knowledge graph from memory.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'search_nodes',
|
||||||
|
description: 'Search nodes (entities and relations) in memory based on a query.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The search query to match against entity names, types, and observation content'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['query']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'open_nodes',
|
||||||
|
description: 'Retrieve specific entities and their connecting relations from memory by name.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
names: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' },
|
||||||
|
description: 'An array of entity names to retrieve'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['names']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// CallTool handler needs to await the manager and the async methods
|
// CallTool handler needs to await the manager and the async methods
|
||||||
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||||
const manager = await this._getManager(); // Ensure manager is ready
|
const manager = await this._getManager() // Ensure manager is ready
|
||||||
const { name, arguments: args } = request.params
|
const { name, arguments: args } = request.params
|
||||||
|
|
||||||
if (!args) {
|
if (!args) {
|
||||||
@ -573,41 +584,75 @@ class MemoryServer {
|
|||||||
case 'create_entities':
|
case 'create_entities':
|
||||||
// Validate args structure if necessary, though SDK might do basic validation
|
// Validate args structure if necessary, though SDK might do basic validation
|
||||||
if (!args.entities || !Array.isArray(args.entities)) {
|
if (!args.entities || !Array.isArray(args.entities)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'entities' array is required.`);
|
throw new McpError(
|
||||||
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'entities' array is required.`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text', text: JSON.stringify(await manager.createEntities(args.entities as Entity[]), null, 2) }]
|
content: [
|
||||||
|
{ type: 'text', text: JSON.stringify(await manager.createEntities(args.entities as Entity[]), null, 2) }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case 'create_relations':
|
case 'create_relations':
|
||||||
if (!args.relations || !Array.isArray(args.relations)) {
|
if (!args.relations || !Array.isArray(args.relations)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'relations' array is required.`);
|
throw new McpError(
|
||||||
}
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'relations' array is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text', text: JSON.stringify(await manager.createRelations(args.relations as Relation[]), null, 2) }]
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify(await manager.createRelations(args.relations as Relation[]), null, 2)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case 'add_observations':
|
case 'add_observations':
|
||||||
if (!args.observations || !Array.isArray(args.observations)) {
|
if (!args.observations || !Array.isArray(args.observations)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'observations' array is required.`);
|
throw new McpError(
|
||||||
}
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'observations' array is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text', text: JSON.stringify(await manager.addObservations(args.observations as { entityName: string; contents: string[] }[]), null, 2) }]
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify(
|
||||||
|
await manager.addObservations(args.observations as { entityName: string; contents: string[] }[]),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case 'delete_entities':
|
case 'delete_entities':
|
||||||
if (!args.entityNames || !Array.isArray(args.entityNames)) {
|
if (!args.entityNames || !Array.isArray(args.entityNames)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'entityNames' array is required.`);
|
throw new McpError(
|
||||||
}
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'entityNames' array is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
await manager.deleteEntities(args.entityNames as string[])
|
await manager.deleteEntities(args.entityNames as string[])
|
||||||
return { content: [{ type: 'text', text: 'Entities deleted successfully' }] }
|
return { content: [{ type: 'text', text: 'Entities deleted successfully' }] }
|
||||||
case 'delete_observations':
|
case 'delete_observations':
|
||||||
if (!args.deletions || !Array.isArray(args.deletions)) {
|
if (!args.deletions || !Array.isArray(args.deletions)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'deletions' array is required.`);
|
throw new McpError(
|
||||||
}
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'deletions' array is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
await manager.deleteObservations(args.deletions as { entityName: string; observations: string[] }[])
|
await manager.deleteObservations(args.deletions as { entityName: string; observations: string[] }[])
|
||||||
return { content: [{ type: 'text', text: 'Observations deleted successfully' }] }
|
return { content: [{ type: 'text', text: 'Observations deleted successfully' }] }
|
||||||
case 'delete_relations':
|
case 'delete_relations':
|
||||||
if (!args.relations || !Array.isArray(args.relations)) {
|
if (!args.relations || !Array.isArray(args.relations)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'relations' array is required.`);
|
throw new McpError(
|
||||||
}
|
ErrorCode.InvalidParams,
|
||||||
|
`Invalid arguments for ${name}: 'relations' array is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
await manager.deleteRelations(args.relations as Relation[])
|
await manager.deleteRelations(args.relations as Relation[])
|
||||||
return { content: [{ type: 'text', text: 'Relations deleted successfully' }] }
|
return { content: [{ type: 'text', text: 'Relations deleted successfully' }] }
|
||||||
case 'read_graph':
|
case 'read_graph':
|
||||||
@ -616,30 +661,37 @@ class MemoryServer {
|
|||||||
content: [{ type: 'text', text: JSON.stringify(await manager.readGraph(), null, 2) }]
|
content: [{ type: 'text', text: JSON.stringify(await manager.readGraph(), null, 2) }]
|
||||||
}
|
}
|
||||||
case 'search_nodes':
|
case 'search_nodes':
|
||||||
if (typeof args.query !== 'string') {
|
if (typeof args.query !== 'string') {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'query' string is required.`);
|
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'query' string is required.`)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text', text: JSON.stringify(await manager.searchNodes(args.query as string), null, 2) }]
|
content: [
|
||||||
|
{ type: 'text', text: JSON.stringify(await manager.searchNodes(args.query as string), null, 2) }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case 'open_nodes':
|
case 'open_nodes':
|
||||||
if (!args.names || !Array.isArray(args.names)) {
|
if (!args.names || !Array.isArray(args.names)) {
|
||||||
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'names' array is required.`);
|
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for ${name}: 'names' array is required.`)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text', text: JSON.stringify(await manager.openNodes(args.names as string[]), null, 2) }]
|
content: [
|
||||||
|
{ type: 'text', text: JSON.stringify(await manager.openNodes(args.names as string[]), null, 2) }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Catch errors from manager methods (like entity not found) or other issues
|
// Catch errors from manager methods (like entity not found) or other issues
|
||||||
if (error instanceof McpError) {
|
if (error instanceof McpError) {
|
||||||
throw error; // Re-throw McpErrors directly
|
throw error // Re-throw McpErrors directly
|
||||||
}
|
}
|
||||||
console.error(`Error executing tool ${name}:`, error);
|
console.error(`Error executing tool ${name}:`, error)
|
||||||
// Throw a generic internal error for unexpected issues
|
// Throw a generic internal error for unexpected issues
|
||||||
throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
throw new McpError(
|
||||||
|
ErrorCode.InternalError,
|
||||||
|
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -324,7 +324,6 @@ const McpSettings: React.FC<Props> = ({ server }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onToggleActive = async (active: boolean) => {
|
const onToggleActive = async (active: boolean) => {
|
||||||
|
|
||||||
if (isFormChanged && active) {
|
if (isFormChanged && active) {
|
||||||
await onSave()
|
await onSave()
|
||||||
return
|
return
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import '@renderer/databases'
|
|||||||
|
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import store, { persistor, useAppDispatch } from '@renderer/store'
|
import store, { persistor, useAppDispatch } from '@renderer/store'
|
||||||
import { message } from 'antd'
|
|
||||||
import { setCustomCss } from '@renderer/store/settings'
|
import { setCustomCss } from '@renderer/store/settings'
|
||||||
|
import { message } from 'antd'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { PersistGate } from 'redux-persist/integration/react'
|
import { PersistGate } from 'redux-persist/integration/react'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user