memos-mcp-server
Version:
A Model Context Protocol (MCP) server for Memos API with search, create, retrieve, and tag listing capabilities.
137 lines (136 loc) • 4.71 kB
JavaScript
import { FastMCP, UserError } from "fastmcp";
import { z } from "zod";
import { MemosClient } from "./memos-client.js";
import { MemosError } from "./types.js";
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
// Constants definition
const MEMOS_URL = process.env.MEMOS_URL || '';
const MEMOS_API_KEY = process.env.MEMOS_API_KEY || '';
const MEMOS_TIMEOUT = process.env.MEMOS_TIMEOUT ? parseInt(process.env.MEMOS_TIMEOUT) : 15000;
// Initialize FastMCP server
const server = new FastMCP({
name: "memos-mcp-server",
version: "0.1.0",
});
// Initialize Memos client
let memosClient;
try {
memosClient = new MemosClient(MEMOS_URL, MEMOS_API_KEY, MEMOS_TIMEOUT);
}
catch (error) {
console.error(`Error initializing Memos client: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
}
server.addTool({
name: "get_user",
description: "Get user information",
parameters: z.object({}),
execute: async () => {
try {
const user = await memosClient.getUser();
return `User: ${JSON.stringify(user)}`;
}
catch (error) {
if (error instanceof MemosError) {
throw new UserError(error.message);
}
throw new UserError(`Error retrieving user details: ${error instanceof Error ? error.message : String(error)}`);
}
},
});
// Register search Memos tool
server.addTool({
name: "search_memo",
description: "Search for memos. By default, no state selection is required unless specified by the user.",
parameters: z.object({
key_word: z.string().describe("The key words to search for in the memo content."),
state: z.enum(["NORMAL", "ARCHIVED"]).default("NORMAL").describe("The state of the memos to list."),
}),
execute: async (args) => {
try {
const results = await memosClient.searchMemos(args.key_word, args.state);
const memos = results.map(memo => ({
name: memo.name,
state: memo.state,
creator: memo.creator,
displayTime: memo.displayTime,
visibility: memo.visibility,
tags: memo.tags || [],
pinned: memo.pinned,
content: memo.content,
}));
return `Search results: ${JSON.stringify(memos)}`;
}
catch (error) {
if (error instanceof MemosError) {
throw new UserError(error.message);
}
throw new UserError(`Error searching memos: ${String(error)}`);
}
},
});
// Register create Memo tool
server.addTool({
name: "create_memo",
description: "Create a new memo",
parameters: z.object({
content: z.string().describe("The content of the memo."),
visibility: z.enum(["PUBLIC", "PROTECTED", "PRIVATE"]).default("PRIVATE").describe("The visibility of the memo."),
tags: z.array(z.string()).optional().describe("List of tags for the memo"),
}),
execute: async (args) => {
try {
const visibility = args.visibility;
const result = await memosClient.createMemo(args.content, args.tags || [], visibility);
return `Memo created: ${result.id}`;
}
catch (error) {
if (error instanceof MemosError) {
throw new UserError(error.message);
}
throw new UserError(`Error creating memo: ${String(error)}`);
}
},
});
// Register get Memo tool
server.addTool({
name: "get_memo",
description: "Get a memo",
parameters: z.object({
name: z.string().describe("The name of the memo."),
}),
execute: async (args) => {
try {
const memo = await memosClient.getMemo(args.name);
return `Memo: ${JSON.stringify(memo)}`;
}
catch (error) {
if (error instanceof MemosError) {
throw new UserError(error.message);
}
throw new UserError(`Error getting memo: ${String(error)}`);
}
},
});
// Check connection to Memos server
(async () => {
try {
// Testing connection to Memos server
await memosClient.getUser();
server.start({
transportType: "stdio",
});
}
catch (error) {
console.error(`Error connecting to Memos server: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
}
})();
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error(`Uncaught exception: ${error.message}`);
process.exit(1);
});