Skip to main content
Glama
Seitrace

Seitrace Insights MCP Server

Official
by Seitrace
index.ts7.92 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { findAction, findResource, GetSnippetToolArgs, ITopic } from '../base.js'; import { endpointDefinitionMap, RESOLVER_MAP, TOPIC_KEY } from './definition.js'; import { camelToSnake, controllerNameToToolName, getExecutor, McpResponse, withMcpResponse, SNIPPET_GENERATOR_MAP, SUPPORTED_RPC_SNIPPET_LANGUAGES, SUPPORTED_GENERAL_SNIPPET_LANGUAGES, } from '../../utils/index.js'; import { McpGroupedToolDefinition } from '../../types.js'; import { GENERAL_API_BASE_URL, securitySchemes } from '../../constants.js'; /** * Arguments for general topic tools. */ export interface GeneralToolArgs extends GetSnippetToolArgs {} /** * General topic providing utility resources not tied to Insights controllers. * * Resource naming: `${TOPIC_KEY}_${controllerName}` -> `general_faucet`. * Only one action is present: `request_faucet`. */ export class GeneralTopic implements ITopic<GeneralToolArgs> { public TOPIC_KEY = TOPIC_KEY; private resources: Map<string, McpGroupedToolDefinition>; /** * Construct the General topic and build its resource/action map. * Currently wires a single resource `general_faucet` with action `request_faucet`. */ constructor() { // Build resource map from our endpoint definitions. This mirrors the // Insights topic strategy so handlers can treat topics uniformly. const map = new Map<string, McpGroupedToolDefinition>(); for (const [fullName, def] of endpointDefinitionMap.entries()) { const [controllerPart, actionCamel = ''] = fullName.split('-'); // faucet_controller -> general_faucet const resourceName = `${this.TOPIC_KEY}_${controllerNameToToolName(controllerPart)}`; const actionName = camelToSnake(actionCamel); if (!map.has(resourceName)) { map.set(resourceName, { name: resourceName, actions: {} }); } const grouped = map.get(resourceName)!; grouped.actions[actionName] = def; } this.resources = map; } /** * Check whether a given resource has an action. * @param resource Full resource name (e.g., `general_faucet`). * @param action Action name (e.g., `request_faucet`). * @returns True if the action exists on the resource; otherwise false. */ public hasResourceAction(resource: string, action: string): boolean { if (!resource || !action) return false; const foundResource = findResource(this.getResources(), resource); return Object.prototype.hasOwnProperty.call(foundResource.actions, action); } /** * Get the resource map for this topic. * @returns Map keyed by resource name with grouped tool definitions. */ public getResources(): Map<string, McpGroupedToolDefinition> { return this.resources; } /** * Return the JSON Schema for a specific resource action. * @param toolArgs Object containing `resource` and `action`. * @returns MCP text content with `{ resource, action, schema }` JSON. */ public getResourceActionSchema(toolArgs: GeneralToolArgs): Promise<CallToolResult> { const { resource, action } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundAction = findAction(this.getResources(), resource, action!); const schema = foundAction.inputSchema; return McpResponse(JSON.stringify({ resource, action, schema })); }); } /** * Generate a code snippet for invoking a resource action. * Falls back to a cURL snippet for POST faucet if generic generator is unsuitable. * @param toolArgs Object containing `resource`, `action`, and `language`. * @returns MCP text content with `{ resource, action, language, snippet }` JSON. */ public getResourceActionSnippet(toolArgs: GeneralToolArgs) { const { resource, action, language, payload } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundAction = findAction(this.getResources(), resource, action!); // Language validation is handled per generator below const snippetGen = (foundAction as any).snippetGenerator; if (!snippetGen) return McpResponse('SNIPPET_GENERATION_NOT_SUPPORTED'); // rpc/general generators use a common signature const generator = (SNIPPET_GENERATOR_MAP as any)[snippetGen]; if (!generator) return McpResponse('SNIPPET_GENERATION_NOT_SUPPORTED'); const supported = snippetGen === 'rpc' ? SUPPORTED_RPC_SNIPPET_LANGUAGES : SUPPORTED_GENERAL_SNIPPET_LANGUAGES; if (typeof language !== 'string' || !supported.includes(language as any)) { return McpResponse( `Unsupported or missing language '${language}'. Supported languages: ${supported.join( ', ' )}` ); } const snippet = generator(foundAction as any, action!, language as any, payload as any); return McpResponse(JSON.stringify({ resource, action, language, snippet })); }); } /** * List resources for this topic only. * @returns MCP text content with `{ resources }` JSON. */ public listResource(): Promise<CallToolResult> | CallToolResult { return withMcpResponse<CallToolResult>(async () => { const resources = Array.from(this.getResources().keys()).sort(); return McpResponse(JSON.stringify({ resources })); }); } /** * List actions available on a specific resource. * @param toolArgs Object containing `resource`. * @returns MCP text content with `{ resource, actions: [{ name, description }] }` JSON. */ public listResourceActions(toolArgs: GeneralToolArgs) { const { resource } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundResource = findResource(this.getResources(), resource); const actions = Object.entries(foundResource.actions) .sort(([a], [b]) => a.localeCompare(b)) .map(([name, def]) => ({ name, description: (def.description || '').trim() })); return McpResponse(JSON.stringify({ resource, actions })); }); } /** * Invoke a resource action with a validated payload. * @param toolArgs Object containing `resource`, `action`, and `payload`. * @returns MCP text content with formatted API response or an error message. */ public async invokeResourceAction(toolArgs: GeneralToolArgs): Promise<CallToolResult> { const { resource, action, payload } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundAction = findAction(this.getResources(), resource, action!); // Decide execution path using executor field if (foundAction.executor === null && (foundAction as any).staticResponse) { return McpResponse(JSON.stringify((foundAction as any).staticResponse)); } // Remote-like executors require a payload validated against schema if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { return McpResponse( JSON.stringify({ error: `Invalid or missing 'payload' for tool 'invokeResourceAction'. Provide an object matching the action schema.`, }) ); } const executorFn = getExecutor((foundAction as any).executor); const result = await executorFn({ toolName: `${resource}.${action}`, definition: foundAction, toolArgs: payload, securitySchemes, baseUrl: GENERAL_API_BASE_URL, }); // Post-process with resolver if defined try { const resolverId = (foundAction as any).resolver as string | undefined; if (!resolverId || !RESOLVER_MAP[resolverId]) return result; const resolver = RESOLVER_MAP[resolverId]; return resolver(result); } catch (_e) { console.error('Error occurred while resolving:', _e); // If resolver fails, return original result return result; } }); } }

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/Seitrace/seitrace-mcp'

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