MATLAB MCP Server
by Tsuchijo
- scripts
#!/usr/bin/env node
import { createClient } from '@supabase/supabase-js';
import { execSync } from 'child_process';
import * as fs from 'fs';
// Get environment variables
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_KEY = process.env.SUPABASE_KEY;
if (!SUPABASE_URL || !SUPABASE_KEY) {
console.error('Error: SUPABASE_URL and SUPABASE_KEY environment variables are required');
process.exit(1);
}
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
interface Entity {
name: string;
entity_type: string;
observations: string[];
}
interface Relation {
from: string;
to: string;
relation_type: string;
}
interface KnowledgeGraph {
entities: Entity[];
relations: Relation[];
}
async function getSupabaseMemory(): Promise<KnowledgeGraph> {
const { data: entities, error: entitiesError } = await supabase
.from('entities')
.select('*');
if (entitiesError) throw entitiesError;
const { data: relations, error: relationsError } = await supabase
.from('relations')
.select('*');
if (relationsError) throw relationsError;
return {
entities: entities || [],
relations: relations || []
};
}
function getMcpMemory(): KnowledgeGraph {
try {
// Read and parse the JSONL file
const lines = fs.readFileSync('memory/memory.jsonl', 'utf-8').split('\n').filter(Boolean);
const entities: Entity[] = [];
const relations: Relation[] = [];
lines.forEach((line: string) => {
const entry = JSON.parse(line);
if (entry.type === 'entity') {
entities.push({
name: entry.name,
entity_type: entry.entityType,
observations: entry.observations
});
} else if (entry.type === 'relation') {
relations.push({
from: entry.from,
to: entry.to,
relation_type: entry.relationType
});
}
});
return { entities, relations };
} catch (error) {
console.error('Failed to get MCP memory:', error);
throw error;
}
}
function compareEntities(supabaseEntities: Entity[], mcpEntities: Entity[]) {
const supabaseMap = new Map(supabaseEntities.map(e => [e.name, e]));
const mcpMap = new Map(mcpEntities.map(e => [e.name, e]));
// Find entities only in Supabase
const onlyInSupabase = supabaseEntities.filter(e => !mcpMap.has(e.name));
// Find entities only in MCP
const onlyInMcp = mcpEntities.filter(e => !supabaseMap.has(e.name));
// Find entities with different content
const different = supabaseEntities.filter(se => {
const me = mcpMap.get(se.name);
if (!me) return false;
return (
se.entity_type !== me.entity_type ||
JSON.stringify(se.observations.sort()) !== JSON.stringify(me.observations.sort())
);
});
return { onlyInSupabase, onlyInMcp, different };
}
function compareRelations(supabaseRelations: Relation[], mcpRelations: Relation[]) {
const normalizeRelation = (r: Relation) => `${r.from}|${r.to}|${r.relation_type}`;
const supabaseSet = new Set(supabaseRelations.map(normalizeRelation));
const mcpSet = new Set(mcpRelations.map(normalizeRelation));
const onlyInSupabase = supabaseRelations.filter(r => !mcpSet.has(normalizeRelation(r)));
const onlyInMcp = mcpRelations.filter(r => !supabaseSet.has(normalizeRelation(r)));
return { onlyInSupabase, onlyInMcp };
}
async function compareMemory() {
console.log('Fetching memory from both sources...');
const [supabaseMemory, mcpMemory] = [
await getSupabaseMemory(),
getMcpMemory()
];
console.log('\nCounts:');
console.log('Supabase:', {
entities: supabaseMemory.entities.length,
relations: supabaseMemory.relations.length
});
console.log('MCP:', {
entities: mcpMemory.entities.length,
relations: mcpMemory.relations.length
});
console.log('\nComparing entities...');
const entityComparison = compareEntities(supabaseMemory.entities, mcpMemory.entities);
if (entityComparison.onlyInSupabase.length > 0) {
console.log('\nEntities only in Supabase:');
entityComparison.onlyInSupabase.forEach(e => {
console.log(`- ${e.name} (${e.entity_type})`);
});
}
if (entityComparison.onlyInMcp.length > 0) {
console.log('\nEntities only in MCP:');
entityComparison.onlyInMcp.forEach(e => {
console.log(`- ${e.name} (${e.entity_type})`);
});
}
if (entityComparison.different.length > 0) {
console.log('\nEntities with differences:');
entityComparison.different.forEach(e => {
const mcpEntity = mcpMemory.entities.find(me => me.name === e.name);
console.log(`\n${e.name}:`);
if (e.entity_type !== mcpEntity?.entity_type) {
console.log(` Type: ${mcpEntity?.entity_type} -> ${e.entity_type}`);
}
const mcpObs = new Set(mcpEntity?.observations || []);
const supabaseObs = new Set(e.observations);
const onlyInSupabase = [...supabaseObs].filter(o => !mcpObs.has(o));
const onlyInMcp = [...mcpObs].filter(o => !supabaseObs.has(o));
if (onlyInSupabase.length > 0) {
console.log(' Added observations:', onlyInSupabase);
}
if (onlyInMcp.length > 0) {
console.log(' Missing observations:', onlyInMcp);
}
});
}
console.log('\nComparing relations...');
const relationComparison = compareRelations(supabaseMemory.relations, mcpMemory.relations);
if (relationComparison.onlyInSupabase.length > 0) {
console.log('\nRelations only in Supabase:');
relationComparison.onlyInSupabase.forEach(r => {
console.log(`- ${r.from} ${r.relation_type} ${r.to}`);
});
}
if (relationComparison.onlyInMcp.length > 0) {
console.log('\nRelations only in MCP:');
relationComparison.onlyInMcp.forEach(r => {
console.log(`- ${r.from} ${r.relation_type} ${r.to}`);
});
}
console.log('\nComparison complete!');
}
// Run the comparison
compareMemory().catch(error => {
console.error('Comparison failed:', error);
process.exit(1);
});