permamind
Version:
An MCP server that provides an immortal memory layer for AI agents and clients
183 lines (182 loc) • 6.79 kB
JavaScript
// SSE transport allows normal logging without protocol interference
import Arweave from "arweave";
import dotenv from "dotenv";
import { FastMCP } from "fastmcp";
import { HUB_REGISTRY_ID } from "./constants.js";
import { getKeyFromMnemonic } from "./mnemonic.js";
import { defaultProcessService } from "./services/DefaultProcessService.js";
import { hubRegistryService } from "./services/RegistryService.js";
import { TokenProcessTemplateService } from "./services/TokenProcessTemplateService.js";
import { ContactToolFactory } from "./tools/contact/ContactToolFactory.js";
import { DocumentationToolFactory } from "./tools/documentation/DocumentationToolFactory.js";
import { toolRegistry } from "./tools/index.js";
import { MemoryToolFactory } from "./tools/memory/MemoryToolFactory.js";
import { ProcessToolFactory } from "./tools/process/ProcessToolFactory.js";
import { TokenToolFactory } from "./tools/token/TokenToolFactory.js";
import { UserToolFactory } from "./tools/user/UserToolFactory.js";
let keyPair;
let publicKey;
let hubId;
let embeddedTemplates;
let initializationComplete = false;
// Export getters for current user state
export const getCurrentUserState = () => ({
embeddedTemplates,
hubId,
initializationComplete,
keyPair,
publicKey,
});
// Export template availability checker
export const isTemplateAvailable = (templateType) => {
return embeddedTemplates?.has(templateType) ?? false;
};
// Export embedded templates getter
export const getEmbeddedTemplates = () => {
return embeddedTemplates;
};
// Configure environment variables silently for MCP protocol compatibility
// Suppress all output from dotenv and any other initialization
const originalLog = globalThis.console.log;
const originalError = globalThis.console.error;
globalThis.console.log = () => { };
globalThis.console.error = () => { };
dotenv.config({ debug: false });
// Only restore console after dotenv is loaded (for MCP protocol compatibility)
if (process.env.NODE_ENV !== "production") {
globalThis.console.log = originalLog;
globalThis.console.error = originalError;
}
async function init() {
const arweave = Arweave.init({});
if (process.env.SEED_PHRASE) {
keyPair = await getKeyFromMnemonic(process.env.SEED_PHRASE);
}
else {
keyPair = await arweave.wallets.generate();
}
publicKey = await arweave.wallets.jwkToAddress(keyPair);
try {
const zone = await hubRegistryService.getZoneById(HUB_REGISTRY_ID(), publicKey);
hubId = zone.spec.processId;
}
catch (e) {
if (e ==
"TypeError: Cannot read properties of undefined (reading 'processId')") {
const profile = {
bot: true,
coverImage: "",
description: "",
displayName: "",
thumbnail: "",
userName: "",
website: "",
};
hubId = await hubRegistryService.create(keyPair, profile);
}
}
// Verify default process templates are loaded (silently for MCP compatibility)
defaultProcessService.getDefaultProcesses();
// Initialize embedded templates
embeddedTemplates = new Map();
embeddedTemplates.set("token", TokenProcessTemplateService.getTokenTemplate(""));
// Verify template availability (silent verification for MCP compatibility)
if (!embeddedTemplates.has("token")) {
throw new Error("Failed to initialize embedded token template");
}
// Mark initialization as complete
initializationComplete = true;
// No automatic context loading on startup for better performance
}
// Setup tool registry with all available tools
function setupToolRegistry() {
// Clear registry first
toolRegistry.clear();
// Create tool context
const context = {
embeddedTemplates,
hubId,
keyPair,
publicKey,
};
// Note: BMAD and Claude Code tools removed
// Register Memory tools
const memoryFactory = new MemoryToolFactory({
categoryDescription: "AI Memory management tools for persistent storage and retrieval",
categoryName: "Memory",
context,
});
memoryFactory.registerTools(toolRegistry);
// Register Contact tools
const contactFactory = new ContactToolFactory({
categoryDescription: "Contact and address management tools",
categoryName: "Contact",
context,
});
contactFactory.registerTools(toolRegistry);
// Register Process tools
const processFactory = new ProcessToolFactory({
categoryDescription: "AO process communication and blockchain query tools",
categoryName: "Process",
context,
});
processFactory.registerTools(toolRegistry);
// Register Token tools
const tokenFactory = new TokenToolFactory({
categoryDescription: "Token operations for balance, transfer, and info queries",
categoryName: "Token",
context,
});
tokenFactory.registerTools(toolRegistry);
// Register Documentation tools
const documentationFactory = new DocumentationToolFactory({
categoryDescription: "Permaweb documentation and deployment tools",
categoryName: "Documentation",
context,
});
documentationFactory.registerTools(toolRegistry);
// Register User tools
const userFactory = new UserToolFactory({
categoryDescription: "User information tools for getting public key and hub ID",
categoryName: "User",
context,
});
userFactory.registerTools(toolRegistry);
}
const server = new FastMCP({
name: "Permamind Memory Server",
version: "1.0.0",
});
// Initialize properly first, then register tools and start server
async function initializeAndStart() {
try {
// Complete initialization first to get real keyPair and context
await init();
// Now setup tool registry with proper context
setupToolRegistry();
// Get tool definitions with proper context and register them
const context = {
embeddedTemplates,
hubId,
keyPair,
publicKey,
};
const toolDefinitions = toolRegistry.getToolDefinitions(context);
for (const toolDefinition of toolDefinitions) {
server.addTool(toolDefinition);
}
// Start the server with fully initialized tools
server.start({
transportType: "stdio",
});
}
catch {
// Silent error handling for stdio transport compatibility
// Fallback: start server without tools if initialization fails
server.start({
transportType: "stdio",
});
}
}
// Initialize and start the server
initializeAndStart();