@sofianedjerbi/knowledge-tree-mcp
Version:
MCP server for hierarchical project knowledge management
105 lines • 3.53 kB
JavaScript
/**
* Link knowledge tool implementation
* Creates relationships between existing knowledge entries
*/
import { join } from 'path';
import { isBidirectionalRelationship } from '../constants/index.js';
import { ensureJsonExtension, fileExists, readKnowledgeEntry, writeKnowledgeEntry } from '../utils/index.js';
/**
* Handler for the link_knowledge tool
*/
export const linkKnowledgeHandler = async (args, context) => {
const { from, to, relationship, description } = args;
// Ensure paths end with .json
const fromPath = ensureJsonExtension(from);
const toPath = ensureJsonExtension(to);
const fromFullPath = join(context.knowledgeRoot, fromPath);
const toFullPath = join(context.knowledgeRoot, toPath);
// Read the source entry
let fromEntry;
try {
if (!await fileExists(fromFullPath)) {
throw new Error(`Source entry does not exist: ${fromPath}`);
}
fromEntry = await readKnowledgeEntry(fromFullPath);
}
catch (error) {
return {
content: [
{
type: "text",
text: `❌ Cannot read source entry: ${fromPath}`,
},
],
};
}
// Verify target exists
if (!await fileExists(toFullPath)) {
return {
content: [
{
type: "text",
text: `❌ Target entry does not exist: ${toPath}`,
},
],
};
}
// Initialize related_to array if it doesn't exist
if (!fromEntry.related_to) {
fromEntry.related_to = [];
}
// Check if link already exists
const existingLink = fromEntry.related_to.find(link => link.path === toPath);
if (existingLink) {
// Update existing link
existingLink.relationship = relationship;
if (description)
existingLink.description = description;
}
else {
// Add new link
const newLink = { path: toPath, relationship };
if (description)
newLink.description = description;
fromEntry.related_to.push(newLink);
}
// Save updated entry
await writeKnowledgeEntry(fromFullPath, fromEntry);
// Create bidirectional links only for symmetric relationships
if (isBidirectionalRelationship(relationship)) {
try {
const targetEntry = await readKnowledgeEntry(toFullPath);
if (!targetEntry.related_to) {
targetEntry.related_to = [];
}
const reverseLink = targetEntry.related_to.find(link => link.path === fromPath);
if (!reverseLink) {
const newReverseLink = {
path: fromPath,
relationship: relationship
};
if (description)
newReverseLink.description = description;
targetEntry.related_to.push(newReverseLink);
await writeKnowledgeEntry(toFullPath, targetEntry);
}
}
catch (error) {
// Silently continue if we can't update the reverse link
}
}
// Broadcast the updated entry
await context.broadcastUpdate('entryUpdated', {
path: fromPath,
data: fromEntry
});
return {
content: [
{
type: "text",
text: `Link created: ${fromPath} ${relationship} ${toPath}`,
},
],
};
};
//# sourceMappingURL=link.js.map