Skip to main content
Glama
server.js9.72 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const DATA_DIR = path.join(__dirname, '..', 'data'); const MOOD_LOG_PATH = path.join(DATA_DIR, 'mood_log.json'); const JOURNAL_PATH = path.join(DATA_DIR, 'journal.json'); function ensureDataFiles() { if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } if (!fs.existsSync(MOOD_LOG_PATH)) { fs.writeFileSync(MOOD_LOG_PATH, JSON.stringify({ entries: [] }, null, 2)); } if (!fs.existsSync(JOURNAL_PATH)) { fs.writeFileSync(JOURNAL_PATH, JSON.stringify({ entries: [] }, null, 2)); } } ensureDataFiles(); let buffer = Buffer.alloc(0); process.stdin.on('data', (chunk) => { buffer = Buffer.concat([buffer, chunk]); parseMessages(); }); function parseMessages() { while (true) { const headerEnd = buffer.indexOf('\r\n\r\n'); if (headerEnd === -1) { return; } const header = buffer.slice(0, headerEnd).toString('utf8'); const match = header.match(/Content-Length: (\d+)/i); if (!match) { buffer = Buffer.alloc(0); return; } const length = Number(match[1]); const totalLength = headerEnd + 4 + length; if (buffer.length < totalLength) { return; } const body = buffer.slice(headerEnd + 4, totalLength).toString('utf8'); buffer = buffer.slice(totalLength); try { const message = JSON.parse(body); handleMessage(message); } catch (error) { console.error('Failed to parse message', error); } } } function sendMessage(message) { const payload = Buffer.from(JSON.stringify(message)); const header = Buffer.from(`Content-Length: ${payload.length}\r\n\r\n`); process.stdout.write(Buffer.concat([header, payload])); } function readJson(filePath) { try { const raw = fs.readFileSync(filePath, 'utf8'); return JSON.parse(raw); } catch (error) { return { entries: [] }; } } function writeJson(filePath, data) { fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); } function handleMessage(message) { if (message.method) { handleRequest(message); } else if (message.id !== undefined) { // Unknown message type, respond with error sendMessage({ jsonrpc: '2.0', id: message.id, error: { code: -32601, message: 'Invalid message type' } }); } } function handleRequest(request) { const { id, method, params } = request; switch (method) { case 'initialize': sendMessage({ jsonrpc: '2.0', id, result: { protocolVersion: '2024-11-05', serverInfo: { name: 'MentalHealthCompanion', version: '0.1.0' }, capabilities: { tools: { listChanged: false }, resources: { listChanged: false, read: { streaming: false } } } } }); break; case 'tools/list': sendMessage({ jsonrpc: '2.0', id, result: { tools: getToolsList() } }); break; case 'tools/call': handleToolCall(id, params || {}); break; case 'resources/list': sendMessage({ jsonrpc: '2.0', id, result: { resources: getResourcesList() } }); break; case 'resources/read': handleResourceRead(id, params || {}); break; case 'shutdown': sendMessage({ jsonrpc: '2.0', id, result: null }); break; case 'exit': process.exit(0); break; default: sendMessage({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown method: ${method}` } }); } } function getToolsList() { return [ { name: 'mood_tracker', description: 'Log your current mood with optional reflection notes to build awareness over time.', inputSchema: { type: 'object', required: ['mood'], properties: { mood: { type: 'string', description: 'A word or short phrase describing how you feel right now.' }, notes: { type: 'string', description: 'Optional details about what is influencing your mood.' } } } }, { name: 'check_in', description: 'Receive gentle, open-ended questions designed to help you explore your current emotional state.', inputSchema: { type: 'object', properties: {} } }, { name: 'coping_tools', description: 'Get tailored self-care practices based on your current stress level.', inputSchema: { type: 'object', required: ['stress_level'], properties: { stress_level: { type: 'integer', minimum: 0, maximum: 10, description: 'How intense is your stress or overwhelm right now on a 0-10 scale?' } } } } ]; } function handleToolCall(id, params) { const { name, arguments: args = {} } = params; switch (name) { case 'mood_tracker': handleMoodTracker(id, args); break; case 'check_in': handleCheckIn(id); break; case 'coping_tools': handleCopingTools(id, args); break; default: sendMessage({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } }); } } function handleMoodTracker(id, args) { const mood = (args.mood || '').trim(); const notes = typeof args.notes === 'string' ? args.notes.trim() : ''; if (!mood) { sendMessage({ jsonrpc: '2.0', id, error: { code: -32602, message: 'The "mood" field is required.' } }); return; } const log = readJson(MOOD_LOG_PATH); const entry = { timestamp: new Date().toISOString(), mood, notes }; log.entries.unshift(entry); writeJson(MOOD_LOG_PATH, log); sendMessage({ jsonrpc: '2.0', id, result: { content: [ { type: 'text', text: `Thank you for checking in. I saved that you are feeling "${mood}" today${notes ? ` and noted: ${notes}` : ''}. Your awareness is a powerful step toward caring for yourself.` } ] } }); } function handleCheckIn(id) { const prompts = [ 'What emotions are most present for you right now?', 'What has felt especially heavy or encouraging today?', 'Is there a small act of care you can offer yourself in this moment?' ]; sendMessage({ jsonrpc: '2.0', id, result: { content: [ { type: 'text', text: `Let\'s take a gentle pause together. ${prompts.join(' ')} I\'m here to listen to whatever you choose to share.` } ] } }); } function handleCopingTools(id, args) { const level = typeof args.stress_level === 'number' ? args.stress_level : Number(args.stress_level); if (Number.isNaN(level)) { sendMessage({ jsonrpc: '2.0', id, error: { code: -32602, message: 'Please share your stress level as a number between 0 and 10.' } }); return; } const stressLevel = Math.max(0, Math.min(10, Math.round(level))); let suggestion; if (stressLevel <= 3) { suggestion = 'Try a mindful check-in: breathe slowly, notice three things you can see, two things you can feel, and one thing you can hear. Celebrate a small win from today.'; } else if (stressLevel <= 6) { suggestion = 'Consider a five-minute reset. Step away from screens, stretch your body, and practice five counted breaths in and seven counted breaths out. Jot down one supportive thought you want to carry forward.'; } else { suggestion = 'You deserve immediate care. Pause for box breathing (inhale 4, hold 4, exhale 4, hold 4) and reach out to someone you trust. If you feel unsafe, please contact a crisis line right away; you are not alone.'; } sendMessage({ jsonrpc: '2.0', id, result: { content: [ { type: 'text', text: suggestion } ] } }); } function getResourcesList() { const moodLog = readJson(MOOD_LOG_PATH); const journal = readJson(JOURNAL_PATH); return [ { uri: 'mcp://mental-health/mood-log', name: 'Mood log', description: 'Your saved mood entries with timestamps and optional reflections.', mimeType: 'application/json', metadata: { entries: moodLog.entries.length } }, { uri: 'mcp://mental-health/journal', name: 'Emotional journal', description: 'Long-form reflections stored privately for you.', mimeType: 'application/json', metadata: { entries: journal.entries.length } } ]; } function handleResourceRead(id, params) { const { uri } = params || {}; if (!uri) { sendMessage({ jsonrpc: '2.0', id, error: { code: -32602, message: 'A resource URI is required.' } }); return; } let data; if (uri === 'mcp://mental-health/mood-log') { data = readJson(MOOD_LOG_PATH); } else if (uri === 'mcp://mental-health/journal') { data = readJson(JOURNAL_PATH); } else { sendMessage({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown resource: ${uri}` } }); return; } sendMessage({ jsonrpc: '2.0', id, result: { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify(data, null, 2) } ] } }); } process.stdin.resume();

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/muminfarooq190/MCP'

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