UNPKG

memos-mcp-server

Version:

A Model Context Protocol (MCP) server for Memos API with search, create, retrieve, and tag listing capabilities.

156 lines (155 loc) 5.1 kB
import axios from 'axios'; import { MemosError, Visibility } from './types.js'; /** * Memos Client Class * Used for interacting with Memos API */ export class MemosClient { client; url; apiKey; timeout; /** * Initialize Memos client * @param memosUrl Memos API URL * @param memosApiKey Memos API Key * @param timeout Request timeout (milliseconds) */ constructor(memosUrl, memosApiKey, timeout = 15000) { if (!memosUrl) { throw new MemosError('Memos URL is required'); } if (!memosApiKey) { throw new MemosError('Memos API Key is required'); } this.url = memosUrl; this.apiKey = memosApiKey; this.timeout = timeout; // Create axios instance this.client = axios.create({ baseURL: this.url, timeout: this.timeout, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Accept': 'application/json', 'Content-Type': 'application/json', } }); } /** * Get user details through authentication status * @returns User details */ async getUser() { try { const response = await this.client.post('/api/v1/auth/status'); if (!response.data) { throw new MemosError('Could not retrieve user details from auth status'); } return response.data; } catch (error) { if (axios.isAxiosError(error)) { throw new MemosError(`Error getting user details: ${error.message}`); } throw new MemosError(`Error getting user details: ${String(error)}`); } } /** * Get user ID through authentication status * @returns User ID */ async getUserId() { try { const userDetails = await this.getUser(); const userId = userDetails.name; if (!userId) { throw new MemosError('Could not retrieve user ID from user details'); } return userId; } catch (error) { if (axios.isAxiosError(error)) { throw new MemosError(`Error getting user ID: ${error.message}`); } throw new MemosError(`Error getting user ID: ${String(error)}`); } } /** * Search Memos * @param keyWord Search keyword * @param state Filter by memo state (default is NORMAL, can also be ARCHIVED) * @returns List of Memos matching the criteria */ async searchMemos(keyWord, state) { try { // First get user ID const userId = await this.getUserId(); // Configure request parameters const params = { filter: `content.contains("${keyWord}")`, state: state, }; // Send request const response = await this.client.get(`/api/v1/${userId}/memos`, { params }); return response.data.memos || []; } catch (error) { if (axios.isAxiosError(error)) { throw new MemosError(`Error searching memos: ${error.message}`); } throw new MemosError(`Error searching memos: ${String(error)}`); } } /** * Create new Memo * @param content Memo content * @param tags List of tags * @param visibility Visibility settings * @returns Created Memo object */ async createMemo(content, tags = [], visibility = Visibility.PRIVATE) { try { // Format content, including tags let formattedContent = content; if (tags.length > 0) { // Add tags at the end of content formattedContent += '\n\n' + tags.join(' '); } // Prepare request payload const payload = { content: formattedContent, visibility: visibility, }; // Send request const response = await this.client.post('/api/v1/memos', payload); return response.data; } catch (error) { if (axios.isAxiosError(error)) { throw new MemosError(`Error creating memo: ${error.message}`); } throw new MemosError(`Error creating memo: ${String(error)}`); } } /** * Get specified Memo * @param memoId Memo ID * @returns Memo object */ async getMemo(memoId) { try { // Format name const name = memoId.startsWith('memos/') ? memoId : `memos/${memoId}`; // Send request const response = await this.client.get(`/api/v1/${name}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { throw new MemosError(`Error getting memo: ${error.message}`); } throw new MemosError(`Error getting memo: ${String(error)}`); } } }