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
JavaScript
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)}`);
}
}
}