Skip to main content
Glama
by RadonX
index.js•7.05 kB
#!/usr/bin/env node import 'dotenv/config'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { logger } from './utils/logger.js'; import { TriliumClient, TriliumAPIError } from './utils/trilium-client.js'; import { ValidationError } from './utils/validation.js'; import { createNote } from './tools/create-note.js'; import { searchNotes } from './tools/search-notes.js'; import { getNote } from './tools/get-note.js'; import { updateNote } from './tools/update-note.js'; import { getRecentNotesResource } from './resources/recent-notes.js'; class TriliumMCPServer { constructor() { this.server = new Server( { name: 'mcp-triliumnext', version: '0.1.0', }, { capabilities: { tools: {}, resources: {}, }, } ); this.triliumClient = new TriliumClient(); this.setupHandlers(); logger.info('TriliumMCP Server initialized'); } setupHandlers() { this.setupToolHandlers(); this.setupResourceHandlers(); this.setupErrorHandling(); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { logger.debug('Listing available tools'); return { tools: [ { name: 'create_note', description: 'Create a new note in TriliumNext', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the note (max 200 characters)', }, content: { type: 'string', description: 'The content of the note (max 1MB)', }, type: { type: 'string', enum: ['text', 'code', 'file', 'image', 'search', 'book', 'relationMap', 'canvas'], default: 'text', description: 'The type of note to create', }, parentNoteId: { type: 'string', description: 'ID of the parent note (defaults to "root" if not provided)', }, }, required: ['title', 'content'], }, }, { name: 'search_notes', description: 'Search for notes in TriliumNext', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (fulltext or structured, max 500 characters)', }, limit: { type: 'number', minimum: 1, maximum: 100, default: 10, description: 'Maximum number of results to return', }, }, required: ['query'], }, }, { name: 'get_note', description: 'Get details of a specific note', inputSchema: { type: 'object', properties: { noteId: { type: 'string', description: 'The ID of the note to retrieve', }, }, required: ['noteId'], }, }, { name: 'update_note', description: 'Update the content of an existing note', inputSchema: { type: 'object', properties: { noteId: { type: 'string', description: 'The ID of the note to update', }, content: { type: 'string', description: 'The new content for the note (max 1MB)', }, }, required: ['noteId', 'content'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { logger.info(`Executing tool: ${request.params.name}`); switch (request.params.name) { case 'create_note': return await this.createNote(request.params.arguments); case 'search_notes': return await this.searchNotes(request.params.arguments); case 'get_note': return await this.getNote(request.params.arguments); case 'update_note': return await this.updateNote(request.params.arguments); default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error) { logger.error(`Tool execution failed: ${error.message}`); throw error; } }); } setupResourceHandlers() { this.server.setRequestHandler(ListResourcesRequestSchema, async () => { logger.debug('Listing available resources'); return { resources: [ { uri: 'trilium://recent-notes', name: 'Recent Notes', description: 'Recently modified notes in TriliumNext', mimeType: 'application/json', }, ], }; }); this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { logger.info(`Reading resource: ${request.params.uri}`); const uri = request.params.uri; if (uri === 'trilium://recent-notes') { return await this.getRecentNotesResource(); } throw new Error(`Unknown resource: ${uri}`); }); } setupErrorHandling() { process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled promise rejection:', reason); }); process.on('uncaughtException', (error) => { logger.error('Uncaught exception:', error); process.exit(1); }); } async createNote(args) { return await createNote(this.triliumClient, args); } async searchNotes(args) { return await searchNotes(this.triliumClient, args); } async getNote(args) { return await getNote(this.triliumClient, args); } async updateNote(args) { return await updateNote(this.triliumClient, args); } async getRecentNotesResource() { return await getRecentNotesResource(this.triliumClient); } async run() { try { const transport = new StdioServerTransport(); await this.server.connect(transport); logger.info('TriliumNext MCP server running on stdio'); } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } } } // Start the server const server = new TriliumMCPServer(); server.run().catch((error) => { logger.error('Server startup failed:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RadonX/mcp-trilium'

If you have feedback or need assistance with the MCP directory API, please join our Discord server