Skip to main content
Glama
Vivek-k3

Models PLUS

by Vivek-k3
server.ts19.7 kB
import { Hono } from 'hono'; import { handleMCPOptions, handleMCPRequest, handleMCPStream, } from './mcp-http'; // Type definitions type Model = { id: string; provider: string; name?: string; release_date?: string; last_updated?: string; attachment?: boolean; reasoning?: boolean; temperature?: boolean; tool_call?: boolean; open_weights?: boolean; knowledge?: string; cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number; }; limit?: { context?: number; output?: number; }; modalities?: { input?: string[]; output?: string[]; }; [key: string]: unknown; }; type Provider = { id: string; name: string; env: string[]; npm?: string; api?: string; doc?: string; [key: string]: unknown; }; type QueryOptions = { q?: string; provider?: string; tool_call?: string; attachment?: string; reasoning?: string; temperature?: string; open_weights?: string; min_input_cost?: string; max_input_cost?: string; min_output_cost?: string; max_output_cost?: string; min_context?: string; max_context?: string; min_output_limit?: string; max_output_limit?: string; modalities?: string; release_after?: string; release_before?: string; updated_after?: string; updated_before?: string; sort?: string; order?: string; limit?: string; offset?: string; fields?: string; }; // Import pre-generated JSON data (compatible with both Bun and Node.js) import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load JSON data at runtime for compatibility const modelsData: Model[] = JSON.parse( readFileSync(join(__dirname, 'models.json'), 'utf-8') ); const providersData: Provider[] = JSON.parse( readFileSync(join(__dirname, 'providers.json'), 'utf-8') ); // Helper functions function parseBoolean(value: string | undefined): boolean | undefined { if (!value) { return; } return value.toLowerCase() === 'true'; } function parseNumber(value: string | undefined): number | undefined { if (!value) { return; } const num = Number.parseFloat(value); return Number.isNaN(num) ? undefined : num; } function parseDate(value: string | undefined): Date | undefined { if (!value) { return; } const date = new Date(value); return Number.isNaN(date.getTime()) ? undefined : date; } function filterModels(models: Model[], options: QueryOptions): Model[] { const predicates: Array<(model: Model) => boolean> = []; // Text search const buildTextPredicate = () => { if (!options.q) { return [] as Array<(m: Model) => boolean>; } const query = options.q.toLowerCase(); return [ (model: Model) => `${model.id} ${model.name ?? ''} ${model.provider}` .toLowerCase() .includes(query), ]; }; // Provider filter const buildProviderPredicate = () => { if (!options.provider) { return [] as Array<(m: Model) => boolean>; } return [(model: Model) => model.provider === options.provider]; }; // Boolean filters const buildBooleanPredicates = () => { const list: Array<(m: Model) => boolean> = []; if (options.tool_call !== undefined) { const val = parseBoolean(options.tool_call); list.push((m) => m.tool_call === val); } if (options.attachment !== undefined) { const val = parseBoolean(options.attachment); list.push((m) => m.attachment === val); } if (options.reasoning !== undefined) { const val = parseBoolean(options.reasoning); list.push((m) => m.reasoning === val); } if (options.temperature !== undefined) { const val = parseBoolean(options.temperature); list.push((m) => m.temperature === val); } if (options.open_weights !== undefined) { const val = parseBoolean(options.open_weights); list.push((m) => m.open_weights === val); } return list; }; // Cost filters const buildCostPredicates = () => { const list: Array<(m: Model) => boolean> = []; if (options.min_input_cost !== undefined) { const min = parseNumber(options.min_input_cost); list.push( (m) => Boolean(min && m.cost?.input) && (m.cost?.input as number) >= (min as number) ); } if (options.max_input_cost !== undefined) { const max = parseNumber(options.max_input_cost); list.push( (m) => Boolean(max && m.cost?.input) && (m.cost?.input as number) <= (max as number) ); } if (options.min_output_cost !== undefined) { const min = parseNumber(options.min_output_cost); list.push( (m) => Boolean(min && m.cost?.output) && (m.cost?.output as number) >= (min as number) ); } if (options.max_output_cost !== undefined) { const max = parseNumber(options.max_output_cost); list.push( (m) => Boolean(max && m.cost?.output) && (m.cost?.output as number) <= (max as number) ); } return list; }; // Limit filters const buildLimitPredicates = () => { const list: Array<(m: Model) => boolean> = []; if (options.min_context !== undefined) { const min = parseNumber(options.min_context); list.push( (m) => Boolean(min && m.limit?.context) && (m.limit?.context as number) >= (min as number) ); } if (options.max_context !== undefined) { const max = parseNumber(options.max_context); list.push( (m) => Boolean(max && m.limit?.context) && (m.limit?.context as number) <= (max as number) ); } if (options.min_output_limit !== undefined) { const min = parseNumber(options.min_output_limit); list.push( (m) => Boolean(min && m.limit?.output) && (m.limit?.output as number) >= (min as number) ); } if (options.max_output_limit !== undefined) { const max = parseNumber(options.max_output_limit); list.push( (m) => Boolean(max && m.limit?.output) && (m.limit?.output as number) <= (max as number) ); } return list; }; // Date filters const buildDatePredicates = () => { const list: Array<(m: Model) => boolean> = []; if (options.release_after) { const after = parseDate(options.release_after); list.push( (m) => Boolean(after && m.release_date) && new Date(m.release_date as string) >= (after as Date) ); } if (options.release_before) { const before = parseDate(options.release_before); list.push( (m) => Boolean(before && m.release_date) && new Date(m.release_date as string) <= (before as Date) ); } if (options.updated_after) { const after = parseDate(options.updated_after); list.push( (m) => Boolean(after && m.last_updated) && new Date(m.last_updated as string) >= (after as Date) ); } if (options.updated_before) { const before = parseDate(options.updated_before); list.push( (m) => Boolean(before && m.last_updated) && new Date(m.last_updated as string) <= (before as Date) ); } return list; }; // Modalities const buildModalitiesPredicate = () => { if (!options.modalities) { return [] as Array<(m: Model) => boolean>; } const requested = options.modalities .split(',') .map((m) => m.trim().toLowerCase()); return [ (m: Model) => { const available = [ ...(m.modalities?.input || []), ...(m.modalities?.output || []), ].map((v) => v.toLowerCase()); return requested.every((mod) => available.includes(mod)); }, ]; }; predicates.push( ...buildTextPredicate(), ...buildProviderPredicate(), ...buildBooleanPredicates(), ...buildCostPredicates(), ...buildLimitPredicates(), ...buildDatePredicates(), ...buildModalitiesPredicate() ); if (!predicates.length) { return models; } return models.filter((m) => predicates.every((p) => p(m))); } function sortModels(models: Model[], sortBy = 'name', order = 'asc'): Model[] { const getters: Record<string, (m: Model) => string | number> = { name: (m) => (m.name || m.id).toLowerCase(), provider: (m) => m.provider.toLowerCase(), release_date: (m) => m.release_date ? new Date(m.release_date).getTime() : 0, last_updated: (m) => m.last_updated ? new Date(m.last_updated).getTime() : 0, cost_input: (m) => m.cost?.input || 0, cost_output: (m) => m.cost?.output || 0, context_limit: (m) => m.limit?.context || 0, output_limit: (m) => m.limit?.output || 0, }; const getter = getters[sortBy] ?? getters.name; return [...models].sort((a, b) => { const aVal = getter(a); const bVal = getter(b); let cmp = 0; if (aVal === bVal) { cmp = 0; } else if (aVal > bVal) { cmp = 1; } else { cmp = -1; } return order === 'desc' ? -cmp : cmp; }); } function selectFields<T extends Record<string, unknown>>( obj: T, fields: string[] ): Partial<T> { if (!fields.length) { return obj; } const result: Partial<T> = {}; for (const field of fields) { if (field in obj) { result[field as keyof T] = obj[field as keyof T]; } } return result; } const app = new Hono(); app.get('/v1/admin/reload', (c) => { return c.text('ok'); // In Cloudflare Workers, data is static }); // Health check endpoint for MCP server scanning app.get('/health', (c) => { return c.json({ status: 'healthy', timestamp: new Date().toISOString(), server: 'modelsplus', version: '0.0.1', }); }); // MCP server info endpoint for discovery app.get('/.well-known/mcp', (c) => { return c.json({ name: 'modelsplus', version: '0.0.1', protocolVersion: '2024-11-05', capabilities: { tools: {}, }, endpoints: { mcp: '/mcp', http: '/mcp/http', }, }); }); app.get('/v1/models', (c) => { // Parse query parameters const options: QueryOptions = { q: c.req.query('q'), provider: c.req.query('provider'), tool_call: c.req.query('tool_call'), attachment: c.req.query('attachment'), reasoning: c.req.query('reasoning'), temperature: c.req.query('temperature'), open_weights: c.req.query('open_weights'), min_input_cost: c.req.query('min_input_cost'), max_input_cost: c.req.query('max_input_cost'), min_output_cost: c.req.query('min_output_cost'), max_output_cost: c.req.query('max_output_cost'), min_context: c.req.query('min_context'), max_context: c.req.query('max_context'), min_output_limit: c.req.query('min_output_limit'), max_output_limit: c.req.query('max_output_limit'), modalities: c.req.query('modalities'), release_after: c.req.query('release_after'), release_before: c.req.query('release_before'), updated_after: c.req.query('updated_after'), updated_before: c.req.query('updated_before'), sort: c.req.query('sort') || 'name', order: c.req.query('order') || 'asc', limit: c.req.query('limit'), offset: c.req.query('offset'), fields: c.req.query('fields'), }; // Filter models let filteredModels = filterModels(modelsData, options); // Sort models filteredModels = sortModels(filteredModels, options.sort, options.order); // Apply pagination const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined; const offset = options.offset ? Number.parseInt(options.offset, 10) : 0; if (offset && offset > 0) { filteredModels = filteredModels.slice(offset); } if (limit && limit > 0) { filteredModels = filteredModels.slice(0, limit); } // Apply field selection let result: Model[] | Partial<Model>[] = filteredModels; if (options.fields) { const fields = options.fields.split(',').map((f) => f.trim()); result = filteredModels.map((model) => selectFields(model, fields)); } return c.json(result); }); // Count endpoints (must be defined before :id route) app.get('/v1/models/count', (c) => { const options: QueryOptions = { q: c.req.query('q'), provider: c.req.query('provider'), tool_call: c.req.query('tool_call'), attachment: c.req.query('attachment'), reasoning: c.req.query('reasoning'), temperature: c.req.query('temperature'), open_weights: c.req.query('open_weights'), min_input_cost: c.req.query('min_input_cost'), max_input_cost: c.req.query('max_input_cost'), min_output_cost: c.req.query('min_output_cost'), max_output_cost: c.req.query('max_output_cost'), min_context: c.req.query('min_context'), max_context: c.req.query('max_context'), min_output_limit: c.req.query('min_output_limit'), max_output_limit: c.req.query('max_output_limit'), modalities: c.req.query('modalities'), release_after: c.req.query('release_after'), release_before: c.req.query('release_before'), updated_after: c.req.query('updated_after'), updated_before: c.req.query('updated_before'), }; const count = filterModels(modelsData, options).length; return c.json({ count }); }); app.get('/v1/models/:id', (c) => { const m = modelsData.find((x: Model) => x.id === c.req.param('id')); return m ? c.json(m) : c.notFound(); }); app.get('/v1/providers', (c) => { const q = c.req.query('q'); const env = c.req.query('env'); const npm = c.req.query('npm'); const limit = c.req.query('limit'); const offset = c.req.query('offset'); let filteredProviders = providersData; // Text search in name if (q) { const query = q.toLowerCase(); filteredProviders = filteredProviders.filter( (p: Provider) => p.name.toLowerCase().includes(query) || p.id.toLowerCase().includes(query) ); } // Filter by environment variable if (env) { filteredProviders = filteredProviders.filter((p: Provider) => p.env.some((e) => e.toLowerCase().includes(env.toLowerCase())) ); } // Filter by npm package if (npm) { filteredProviders = filteredProviders.filter((p: Provider) => p.npm?.toLowerCase().includes(npm.toLowerCase()) ); } // Apply pagination const limitNum = limit ? Number.parseInt(limit, 10) : undefined; const offsetNum = offset ? Number.parseInt(offset, 10) : 0; if (offsetNum && offsetNum > 0) { filteredProviders = filteredProviders.slice(offsetNum); } if (limitNum && limitNum > 0) { filteredProviders = filteredProviders.slice(0, limitNum); } return c.json(filteredProviders); }); app.get('/v1/providers/count', (c) => { const q = c.req.query('q'); const env = c.req.query('env'); const npm = c.req.query('npm'); let filteredProviders = providersData; // Text search in name if (q) { const query = q.toLowerCase(); filteredProviders = filteredProviders.filter( (p: Provider) => p.name.toLowerCase().includes(query) || p.id.toLowerCase().includes(query) ); } // Filter by environment variable if (env) { filteredProviders = filteredProviders.filter((p: Provider) => p.env.some((e) => e.toLowerCase().includes(env.toLowerCase())) ); } // Filter by npm package if (npm) { filteredProviders = filteredProviders.filter((p: Provider) => p.npm?.toLowerCase().includes(npm.toLowerCase()) ); } return c.json({ count: filteredProviders.length }); }); // Search suggestions app.get('/v1/search/suggestions', (c) => { const q = c.req.query('q')?.toLowerCase(); const limit = Number.parseInt(c.req.query('limit') || '10', 10); if (!q || q.length < 2) { return c.json({ suggestions: [] }); } const suggestions = new Set<string>(); // Model name suggestions for (const model of modelsData) { if (model.name?.toLowerCase().includes(q)) { suggestions.add(model.name); } if (model.id.toLowerCase().includes(q)) { suggestions.add(model.id); } } // Provider name suggestions for (const provider of providersData) { if (provider.name.toLowerCase().includes(q)) { suggestions.add(provider.name); } } const result = Array.from(suggestions).slice(0, limit); return c.json({ suggestions: result }); }); // MCP HTTP endpoint - Regular HTTP for JSON-RPC app.get('/mcp/http', (c) => { return handleMCPRequest(c.req.raw); }); app.post('/mcp/http', (c) => { return handleMCPRequest(c.req.raw); }); // MCP endpoint - Combined HTTP endpoint for both GET and POST app.get('/mcp', (c) => { return handleMCPStream(c.req.raw); }); app.post('/mcp', (c) => { return handleMCPRequest(c.req.raw); }); // MCP CORS preflight app.options('/mcp/http', (_c) => { return handleMCPOptions(); }); app.options('/mcp', (_c) => { return handleMCPOptions(); }); // For standalone server execution if ( import.meta.main || (typeof process !== 'undefined' && process.argv[1]?.endsWith('server.js')) ) { const DEFAULT_PORT = 8788; const port = process.env.PORT || DEFAULT_PORT; // Try Bun first, fallback to Node.js if (typeof (globalThis as unknown as { Bun?: unknown }).Bun !== 'undefined') { ( globalThis as unknown as { Bun: { serve: (opts: unknown) => void } } ).Bun.serve({ port: Number.parseInt(port.toString(), 10), fetch: app.fetch, }); } else { // Node.js fallback using built-in HTTP server import('node:http').then(({ createServer }) => { const server = createServer(async (req, res) => { const request = await convertNodeRequestToWebRequest(req, port); const response = await app.fetch(request); await sendWebResponseToNodeResponse(response, res); }); server.listen(port); }); } } // Helper functions to reduce cognitive complexity async function convertNodeRequestToWebRequest( req: import('node:http').IncomingMessage, port: number ): Promise<Request> { const url = `http://localhost:${port}${req.url}`; const method = req.method || 'GET'; const headers = convertNodeHeadersToWebHeaders(req.headers); const body = await getRequestBody(req, method); const options: RequestInit = { method, headers }; if (body) { options.body = body; options.duplex = 'half'; } return new Request(url, options); } function convertNodeHeadersToWebHeaders( nodeHeaders: import('node:http').IncomingHttpHeaders ): Headers { const headers = new Headers(); for (const [key, value] of Object.entries(nodeHeaders)) { if (value) { headers.set(key, Array.isArray(value) ? value.join(', ') : value); } } return headers; } async function getRequestBody( req: import('node:http').IncomingMessage, method: string ): Promise<ReadableStream<Uint8Array> | undefined> { if (method === 'GET' || method === 'HEAD') { return; } const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(chunk); } const buffer = Buffer.concat(chunks); return new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(buffer)); controller.close(); }, }); } async function sendWebResponseToNodeResponse( response: Response, res: import('node:http').ServerResponse ): Promise<void> { res.statusCode = response.status; response.headers.forEach((value, key) => { res.setHeader(key, value); }); const responseBody = await response.text(); res.end(responseBody); } export default app;

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/Vivek-k3/modelsplus'

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