Skip to main content
Glama

Joke MCP Server

by ericqian77
jokes-mcp.js4.21 kB
#!/usr/bin/env node import readline from 'node:readline'; import { performance } from 'node:perf_hooks'; import { loadConfig } from './config.js'; import { validateGetJokeInput } from './validation.js'; import { logEvent, logError } from './logger.js'; import * as jokeApiProvider from './providers/jokeapi.js'; import * as officialProvider from './providers/official.js'; import * as localProvider from './providers/local.js'; const config = loadConfig(process.env); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); const PROVIDER_CHAIN = buildProviderChain(config.provider); rl.on('line', async (line) => { const trimmed = line.trim(); if (!trimmed) return; if (trimmed === 'health') { respond({ ok: true }); return; } if (trimmed.startsWith('getJoke')) { const payloadText = extractPayload(trimmed); try { const payload = payloadText ? JSON.parse(payloadText) : undefined; const preferences = validateGetJokeInput(payload, { defaultCategory: config.defaultCategory, lang: config.lang, }); const result = await resolveJoke(preferences, config); respond(result); } catch (error) { const printable = toResponseError(error); respond(printable); } return; } respond({ ok: false, error: `Unknown command: ${trimmed}` }); }); rl.on('close', () => { process.exit(0); }); process.on('SIGINT', () => { rl.close(); }); async function resolveJoke(preferences, cfg) { const verbose = Boolean(cfg.verbose); const infoLog = verbose ? logEvent : noop; const errorLog = verbose ? logError : noop; const attempts = []; for (const providerName of PROVIDER_CHAIN) { const start = performance.now(); if (!cfg.allowNet && providerName !== 'local') { const latency = Math.round(performance.now() - start); const message = 'Network access disabled'; attempts.push({ provider: providerName, message, latency }); infoLog({ event: 'provider_skipped', provider: providerName, reason: message, latency_ms: latency, fallback_depth: attempts.length, }); continue; } try { const provider = getProvider(providerName); const result = await provider.getJoke(preferences, { timeoutMs: cfg.timeoutMs, retries: cfg.retries, allowNet: cfg.allowNet, }); const latency = Math.round(performance.now() - start); const response = { text: result.text, source: result.source ?? providerName, category: result.category ?? preferences.category, latency_ms: latency, }; infoLog({ event: 'getJoke', provider: response.source, latency_ms: latency, fallback_depth: attempts.length, }); return response; } catch (error) { const latency = Math.round(performance.now() - start); attempts.push({ provider: providerName, message: error.message, latency }); errorLog(error, { event: 'provider_failure', provider: providerName, latency_ms: latency }); } } const failure = new Error('All joke providers failed'); failure.details = attempts; throw failure; } function buildProviderChain(primary) { if (primary === 'jokeapi') { return ['jokeapi', 'official', 'local']; } return ['official', 'jokeapi', 'local']; } function getProvider(name) { switch (name) { case 'jokeapi': return jokeApiProvider; case 'official': return officialProvider; case 'local': return localProvider; default: throw new Error(`Unknown provider ${name}`); } } function extractPayload(commandLine) { const firstSpace = commandLine.indexOf(' '); if (firstSpace === -1) return ''; return commandLine.slice(firstSpace + 1).trim(); } function respond(payload) { process.stdout.write(`${JSON.stringify(payload)}\n`); } function toResponseError(error) { const info = { ok: false, error: error?.message ?? 'Unknown error', }; if (error?.details) { info.details = error.details; } return info; } export { resolveJoke, buildProviderChain }; function noop() {}

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/ericqian77/joke-mcp'

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