Skip to main content
Glama
ssv445

Lorem Ipsum MCP Server

by ssv445
FastMCP.js28.7 kB
// src/FastMCP.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, CompleteRequestSchema, ErrorCode, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, RootsListChangedNotificationSchema, SetLevelRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { EventEmitter } from "events"; import { fileTypeFromBuffer } from "file-type"; import { readFile } from "fs/promises"; import Fuse from "fuse.js"; import { startHTTPStreamServer, startSSEServer } from "mcp-proxy"; import { setTimeout as delay } from "timers/promises"; import { fetch } from "undici"; import parseURITemplate from "uri-templates"; import { toJsonSchema } from "xsschema"; import { z } from "zod"; var imageContent = async (input) => { let rawData; try { if ("url" in input) { try { const response = await fetch(input.url); if (!response.ok) { throw new Error( `Server responded with status: ${response.status} - ${response.statusText}` ); } rawData = Buffer.from(await response.arrayBuffer()); } catch (error) { throw new Error( `Failed to fetch image from URL (${input.url}): ${error instanceof Error ? error.message : String(error)}` ); } } else if ("path" in input) { try { rawData = await readFile(input.path); } catch (error) { throw new Error( `Failed to read image from path (${input.path}): ${error instanceof Error ? error.message : String(error)}` ); } } else if ("buffer" in input) { rawData = input.buffer; } else { throw new Error( "Invalid input: Provide a valid 'url', 'path', or 'buffer'" ); } const mimeType = await fileTypeFromBuffer(rawData); if (!mimeType || !mimeType.mime.startsWith("image/")) { console.warn( `Warning: Content may not be a valid image. Detected MIME: ${mimeType?.mime || "unknown"}` ); } const base64Data = rawData.toString("base64"); return { data: base64Data, mimeType: mimeType?.mime ?? "image/png", type: "image" }; } catch (error) { if (error instanceof Error) { throw error; } else { throw new Error(`Unexpected error processing image: ${String(error)}`); } } }; var audioContent = async (input) => { let rawData; try { if ("url" in input) { try { const response = await fetch(input.url); if (!response.ok) { throw new Error( `Server responded with status: ${response.status} - ${response.statusText}` ); } rawData = Buffer.from(await response.arrayBuffer()); } catch (error) { throw new Error( `Failed to fetch audio from URL (${input.url}): ${error instanceof Error ? error.message : String(error)}` ); } } else if ("path" in input) { try { rawData = await readFile(input.path); } catch (error) { throw new Error( `Failed to read audio from path (${input.path}): ${error instanceof Error ? error.message : String(error)}` ); } } else if ("buffer" in input) { rawData = input.buffer; } else { throw new Error( "Invalid input: Provide a valid 'url', 'path', or 'buffer'" ); } const mimeType = await fileTypeFromBuffer(rawData); if (!mimeType || !mimeType.mime.startsWith("audio/")) { console.warn( `Warning: Content may not be a valid audio file. Detected MIME: ${mimeType?.mime || "unknown"}` ); } const base64Data = rawData.toString("base64"); return { data: base64Data, mimeType: mimeType?.mime ?? "audio/mpeg", type: "audio" }; } catch (error) { if (error instanceof Error) { throw error; } else { throw new Error(`Unexpected error processing audio: ${String(error)}`); } } }; var FastMCPError = class extends Error { constructor(message) { super(message); this.name = new.target.name; } }; var UnexpectedStateError = class extends FastMCPError { extras; constructor(message, extras) { super(message); this.name = new.target.name; this.extras = extras; } }; var UserError = class extends UnexpectedStateError { }; var TextContentZodSchema = z.object({ /** * The text content of the message. */ text: z.string(), type: z.literal("text") }).strict(); var ImageContentZodSchema = z.object({ /** * The base64-encoded image data. */ data: z.string().base64(), /** * The MIME type of the image. Different providers may support different image types. */ mimeType: z.string(), type: z.literal("image") }).strict(); var AudioContentZodSchema = z.object({ /** * The base64-encoded audio data. */ data: z.string().base64(), mimeType: z.string(), type: z.literal("audio") }).strict(); var ContentZodSchema = z.discriminatedUnion("type", [ TextContentZodSchema, ImageContentZodSchema, AudioContentZodSchema ]); var ContentResultZodSchema = z.object({ content: ContentZodSchema.array(), isError: z.boolean().optional() }).strict(); var CompletionZodSchema = z.object({ /** * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. */ hasMore: z.optional(z.boolean()), /** * The total number of completion options available. This can exceed the number of values actually sent in the response. */ total: z.optional(z.number().int()), /** * An array of completion values. Must not exceed 100 items. */ values: z.array(z.string()).max(100) }); var FastMCPSessionEventEmitterBase = EventEmitter; var FastMCPSessionEventEmitter = class extends FastMCPSessionEventEmitterBase { }; var FastMCPSession = class extends FastMCPSessionEventEmitter { get clientCapabilities() { return this.#clientCapabilities ?? null; } get loggingLevel() { return this.#loggingLevel; } get roots() { return this.#roots; } get server() { return this.#server; } #auth; #capabilities = {}; #clientCapabilities; #loggingLevel = "info"; #pingConfig; #pingInterval = null; #prompts = []; #resources = []; #resourceTemplates = []; #roots = []; #rootsConfig; #server; constructor({ auth, instructions, name, ping, prompts, resources, resourcesTemplates, roots, tools, version }) { super(); this.#auth = auth; this.#pingConfig = ping; this.#rootsConfig = roots; if (tools.length) { this.#capabilities.tools = {}; } if (resources.length || resourcesTemplates.length) { this.#capabilities.resources = {}; } if (prompts.length) { for (const prompt of prompts) { this.addPrompt(prompt); } this.#capabilities.prompts = {}; } this.#capabilities.logging = {}; this.#server = new Server( { name, version }, { capabilities: this.#capabilities, instructions } ); this.setupErrorHandling(); this.setupLoggingHandlers(); this.setupRootsHandlers(); this.setupCompleteHandlers(); if (tools.length) { this.setupToolHandlers(tools); } if (resources.length || resourcesTemplates.length) { for (const resource of resources) { this.addResource(resource); } this.setupResourceHandlers(resources); if (resourcesTemplates.length) { for (const resourceTemplate of resourcesTemplates) { this.addResourceTemplate(resourceTemplate); } this.setupResourceTemplateHandlers(resourcesTemplates); } } if (prompts.length) { this.setupPromptHandlers(prompts); } } async close() { if (this.#pingInterval) { clearInterval(this.#pingInterval); } try { await this.#server.close(); } catch (error) { console.error("[FastMCP error]", "could not close server", error); } } async connect(transport) { if (this.#server.transport) { throw new UnexpectedStateError("Server is already connected"); } await this.#server.connect(transport); let attempt = 0; while (attempt++ < 10) { const capabilities = await this.#server.getClientCapabilities(); if (capabilities) { this.#clientCapabilities = capabilities; break; } await delay(100); } if (!this.#clientCapabilities) { console.warn("[FastMCP warning] could not infer client capabilities"); } if (this.#clientCapabilities?.roots?.listChanged && typeof this.#server.listRoots === "function") { try { const roots = await this.#server.listRoots(); this.#roots = roots.roots; } catch (e) { if (e instanceof McpError && e.code === ErrorCode.MethodNotFound) { console.debug( "[FastMCP debug] listRoots method not supported by client" ); } else { console.error( `[FastMCP error] received error listing roots. ${e instanceof Error ? e.stack : JSON.stringify(e)}` ); } } } if (this.#clientCapabilities) { const pingConfig = this.#getPingConfig(transport); if (pingConfig.enabled) { this.#pingInterval = setInterval(async () => { try { await this.#server.ping(); } catch { const logLevel = pingConfig.logLevel; if (logLevel === "debug") { console.debug("[FastMCP debug] server ping failed"); } else if (logLevel === "warning") { console.warn( "[FastMCP warning] server is not responding to ping" ); } else if (logLevel === "error") { console.error("[FastMCP error] server is not responding to ping"); } else { console.info("[FastMCP info] server ping failed"); } } }, pingConfig.intervalMs); } } } async requestSampling(message) { return this.#server.createMessage(message); } #getPingConfig(transport) { const pingConfig = this.#pingConfig || {}; let defaultEnabled = false; if ("type" in transport) { if (transport.type === "sse" || transport.type === "httpStream") { defaultEnabled = true; } } return { enabled: pingConfig.enabled !== void 0 ? pingConfig.enabled : defaultEnabled, intervalMs: pingConfig.intervalMs || 5e3, logLevel: pingConfig.logLevel || "debug" }; } addPrompt(inputPrompt) { const completers = {}; const enums = {}; for (const argument of inputPrompt.arguments ?? []) { if (argument.complete) { completers[argument.name] = argument.complete; } if (argument.enum) { enums[argument.name] = argument.enum; } } const prompt = { ...inputPrompt, complete: async (name, value) => { if (completers[name]) { return await completers[name](value); } if (enums[name]) { const fuse = new Fuse(enums[name], { keys: ["value"] }); const result = fuse.search(value); return { total: result.length, values: result.map((item) => item.item) }; } return { values: [] }; } }; this.#prompts.push(prompt); } addResource(inputResource) { this.#resources.push(inputResource); } addResourceTemplate(inputResourceTemplate) { const completers = {}; for (const argument of inputResourceTemplate.arguments ?? []) { if (argument.complete) { completers[argument.name] = argument.complete; } } const resourceTemplate = { ...inputResourceTemplate, complete: async (name, value) => { if (completers[name]) { return await completers[name](value); } return { values: [] }; } }; this.#resourceTemplates.push(resourceTemplate); } setupCompleteHandlers() { this.#server.setRequestHandler(CompleteRequestSchema, async (request) => { if (request.params.ref.type === "ref/prompt") { const prompt = this.#prompts.find( (prompt2) => prompt2.name === request.params.ref.name ); if (!prompt) { throw new UnexpectedStateError("Unknown prompt", { request }); } if (!prompt.complete) { throw new UnexpectedStateError("Prompt does not support completion", { request }); } const completion = CompletionZodSchema.parse( await prompt.complete( request.params.argument.name, request.params.argument.value ) ); return { completion }; } if (request.params.ref.type === "ref/resource") { const resource = this.#resourceTemplates.find( (resource2) => resource2.uriTemplate === request.params.ref.uri ); if (!resource) { throw new UnexpectedStateError("Unknown resource", { request }); } if (!("uriTemplate" in resource)) { throw new UnexpectedStateError("Unexpected resource"); } if (!resource.complete) { throw new UnexpectedStateError( "Resource does not support completion", { request } ); } const completion = CompletionZodSchema.parse( await resource.complete( request.params.argument.name, request.params.argument.value ) ); return { completion }; } throw new UnexpectedStateError("Unexpected completion request", { request }); }); } setupErrorHandling() { this.#server.onerror = (error) => { console.error("[FastMCP error]", error); }; } setupLoggingHandlers() { this.#server.setRequestHandler(SetLevelRequestSchema, (request) => { this.#loggingLevel = request.params.level; return {}; }); } setupPromptHandlers(prompts) { this.#server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: prompts.map((prompt) => { return { arguments: prompt.arguments, complete: prompt.complete, description: prompt.description, name: prompt.name }; }) }; }); this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => { const prompt = prompts.find( (prompt2) => prompt2.name === request.params.name ); if (!prompt) { throw new McpError( ErrorCode.MethodNotFound, `Unknown prompt: ${request.params.name}` ); } const args = request.params.arguments; for (const arg of prompt.arguments ?? []) { if (arg.required && !(args && arg.name in args)) { throw new McpError( ErrorCode.InvalidRequest, `Missing required argument: ${arg.name}` ); } } let result; try { result = await prompt.load(args); } catch (error) { throw new McpError( ErrorCode.InternalError, `Error loading prompt: ${error}` ); } return { description: prompt.description, messages: [ { content: { text: result, type: "text" }, role: "user" } ] }; }); } setupResourceHandlers(resources) { this.#server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: resources.map((resource) => { return { mimeType: resource.mimeType, name: resource.name, uri: resource.uri }; }) }; }); this.#server.setRequestHandler( ReadResourceRequestSchema, async (request) => { if ("uri" in request.params) { const resource = resources.find( (resource2) => "uri" in resource2 && resource2.uri === request.params.uri ); if (!resource) { for (const resourceTemplate of this.#resourceTemplates) { const uriTemplate = parseURITemplate( resourceTemplate.uriTemplate ); const match = uriTemplate.fromUri(request.params.uri); if (!match) { continue; } const uri = uriTemplate.fill(match); const result = await resourceTemplate.load(match); return { contents: [ { mimeType: resourceTemplate.mimeType, name: resourceTemplate.name, uri, ...result } ] }; } throw new McpError( ErrorCode.MethodNotFound, `Unknown resource: ${request.params.uri}` ); } if (!("uri" in resource)) { throw new UnexpectedStateError("Resource does not support reading"); } let maybeArrayResult; try { maybeArrayResult = await resource.load(); } catch (error) { throw new McpError( ErrorCode.InternalError, `Error reading resource: ${error}`, { uri: resource.uri } ); } if (Array.isArray(maybeArrayResult)) { return { contents: maybeArrayResult.map((result) => ({ mimeType: resource.mimeType, name: resource.name, uri: resource.uri, ...result })) }; } else { return { contents: [ { mimeType: resource.mimeType, name: resource.name, uri: resource.uri, ...maybeArrayResult } ] }; } } throw new UnexpectedStateError("Unknown resource request", { request }); } ); } setupResourceTemplateHandlers(resourceTemplates) { this.#server.setRequestHandler( ListResourceTemplatesRequestSchema, async () => { return { resourceTemplates: resourceTemplates.map((resourceTemplate) => { return { name: resourceTemplate.name, uriTemplate: resourceTemplate.uriTemplate }; }) }; } ); } setupRootsHandlers() { if (this.#rootsConfig?.enabled === false) { console.debug( "[FastMCP debug] roots capability explicitly disabled via config" ); return; } if (typeof this.#server.listRoots === "function") { this.#server.setNotificationHandler( RootsListChangedNotificationSchema, () => { this.#server.listRoots().then((roots) => { this.#roots = roots.roots; this.emit("rootsChanged", { roots: roots.roots }); }).catch((error) => { if (error instanceof McpError && error.code === ErrorCode.MethodNotFound) { console.debug( "[FastMCP debug] listRoots method not supported by client" ); } else { console.error("[FastMCP error] Error listing roots", error); } }); } ); } else { console.debug( "[FastMCP debug] roots capability not available, not setting up notification handler" ); } } setupToolHandlers(tools) { this.#server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: await Promise.all( tools.map(async (tool) => { return { annotations: tool.annotations, description: tool.description, inputSchema: tool.parameters ? await toJsonSchema(tool.parameters) : { additionalProperties: false, properties: {}, type: "object" }, // More complete schema for Cursor compatibility name: tool.name }; }) ) }; }); this.#server.setRequestHandler(CallToolRequestSchema, async (request) => { const tool = tools.find((tool2) => tool2.name === request.params.name); if (!tool) { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } let args = void 0; if (tool.parameters) { const parsed = await tool.parameters["~standard"].validate( request.params.arguments ); if (parsed.issues) { throw new McpError( ErrorCode.InvalidParams, `Invalid ${request.params.name} parameters: ${JSON.stringify(parsed.issues)}` ); } args = parsed.value; } const progressToken = request.params?._meta?.progressToken; let result; try { const reportProgress = async (progress) => { await this.#server.notification({ method: "notifications/progress", params: { ...progress, progressToken } }); }; const log = { debug: (message, context) => { this.#server.sendLoggingMessage({ data: { context, message }, level: "debug" }); }, error: (message, context) => { this.#server.sendLoggingMessage({ data: { context, message }, level: "error" }); }, info: (message, context) => { this.#server.sendLoggingMessage({ data: { context, message }, level: "info" }); }, warn: (message, context) => { this.#server.sendLoggingMessage({ data: { context, message }, level: "warning" }); } }; const executeToolPromise = tool.execute(args, { log, reportProgress, session: this.#auth }); const maybeStringResult = await (tool.timeoutMs ? Promise.race([ executeToolPromise, new Promise((_, reject) => { setTimeout(() => { reject( new UserError( `Tool execution timed out after ${tool.timeoutMs}ms` ) ); }, tool.timeoutMs); }) ]) : executeToolPromise); if (typeof maybeStringResult === "string") { result = ContentResultZodSchema.parse({ content: [{ text: maybeStringResult, type: "text" }] }); } else if ("type" in maybeStringResult) { result = ContentResultZodSchema.parse({ content: [maybeStringResult] }); } else { result = ContentResultZodSchema.parse(maybeStringResult); } } catch (error) { if (error instanceof UserError) { return { content: [{ text: error.message, type: "text" }], isError: true }; } return { content: [{ text: `Error: ${error}`, type: "text" }], isError: true }; } return result; }); } }; var FastMCPEventEmitterBase = EventEmitter; var FastMCPEventEmitter = class extends FastMCPEventEmitterBase { }; var FastMCP = class extends FastMCPEventEmitter { constructor(options) { super(); this.options = options; this.#options = options; this.#authenticate = options.authenticate; } get sessions() { return this.#sessions; } #authenticate; #httpStreamServer = null; #options; #prompts = []; #resources = []; #resourcesTemplates = []; #sessions = []; #sseServer = null; #tools = []; /** * Adds a prompt to the server. */ addPrompt(prompt) { this.#prompts.push(prompt); } /** * Adds a resource to the server. */ addResource(resource) { this.#resources.push(resource); } /** * Adds a resource template to the server. */ addResourceTemplate(resource) { this.#resourcesTemplates.push(resource); } /** * Adds a tool to the server. */ addTool(tool) { this.#tools.push(tool); } /** * Starts the server. */ async start(options = { transportType: "stdio" }) { if (options.transportType === "stdio") { const transport = new StdioServerTransport(); const session = new FastMCPSession({ instructions: this.#options.instructions, name: this.#options.name, ping: this.#options.ping, prompts: this.#prompts, resources: this.#resources, resourcesTemplates: this.#resourcesTemplates, roots: this.#options.roots, tools: this.#tools, version: this.#options.version }); await session.connect(transport); this.#sessions.push(session); this.emit("connect", { session }); } else if (options.transportType === "sse") { this.#sseServer = await startSSEServer({ createServer: async (request) => { let auth; if (this.#authenticate) { auth = await this.#authenticate(request); } return new FastMCPSession({ auth, name: this.#options.name, ping: this.#options.ping, prompts: this.#prompts, resources: this.#resources, resourcesTemplates: this.#resourcesTemplates, roots: this.#options.roots, tools: this.#tools, version: this.#options.version }); }, endpoint: options.sse.endpoint, onClose: (session) => { this.emit("disconnect", { session }); }, onConnect: async (session) => { this.#sessions.push(session); this.emit("connect", { session }); }, port: options.sse.port }); console.info( `[FastMCP info] server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}` ); } else if (options.transportType === "httpStream") { this.#httpStreamServer = await startHTTPStreamServer({ createServer: async (request) => { let auth; if (this.#authenticate) { auth = await this.#authenticate(request); } return new FastMCPSession({ auth, name: this.#options.name, ping: this.#options.ping, prompts: this.#prompts, resources: this.#resources, resourcesTemplates: this.#resourcesTemplates, roots: this.#options.roots, tools: this.#tools, version: this.#options.version }); }, endpoint: options.httpStream.endpoint, onClose: (session) => { this.emit("disconnect", { session }); }, onConnect: async (session) => { this.#sessions.push(session); this.emit("connect", { session }); }, port: options.httpStream.port }); console.info( `[FastMCP info] server is running on HTTP Stream at http://localhost:${options.httpStream.port}${options.httpStream.endpoint}` ); } else { throw new Error("Invalid transport type"); } } /** * Stops the server. */ async stop() { if (this.#sseServer) { await this.#sseServer.close(); } if (this.#httpStreamServer) { await this.#httpStreamServer.close(); } } }; export { FastMCP, FastMCPSession, UnexpectedStateError, UserError, audioContent, imageContent }; //# sourceMappingURL=FastMCP.js.map

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/ssv445/lorem-ipsum-mcp'

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