Skip to main content
Glama
Seitrace

Seitrace Insights MCP Server

Official
by Seitrace
index.ts8.66 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { fileURLToPath } from 'url'; import fs from 'fs'; import NodePath from 'path'; import { findAction, findResource, GetSnippetToolArgs, ITopic } from '../base.js'; import { endpointDefinitionMap } from './resources/definition.js'; import { camelToSnake, controllerNameToToolName, getExecutor, McpResponse, withMcpResponse, SNIPPET_GENERATOR_MAP, SUPPORTED_SNIPPET_LANGUAGES, SUPPORTED_RPC_SNIPPET_LANGUAGES, SUPPORTED_GENERAL_SNIPPET_LANGUAGES, } from '../../utils/index.js'; import { McpGroupedToolDefinition } from '../../types.js'; import { INSIGHTS_API_BASE_URL, securitySchemes } from '../../constants.js'; import { TOPIC_KEY } from './definition.js'; import { RESOLVER_MAP } from './definition.js'; /** * Arguments for invoking an insights tool action */ export interface InsightsToolArgs extends GetSnippetToolArgs {} /** * Insights topic class. */ export class InsightsTopic implements ITopic<InsightsToolArgs> { /** * The unique key for the topic */ public TOPIC_KEY = TOPIC_KEY; /** * The map of available resources for the topic */ private resources: Map<string, McpGroupedToolDefinition>; /** * Constructor for the InsightsTopic class. s */ constructor() { const map = new Map<string, McpGroupedToolDefinition>(); /** * Populate the resource map with endpoint definitions. */ for (const [fullName, def] of endpointDefinitionMap.entries()) { // fullName format: <ControllerName>-<ActionNameCamel> const [controllerPart, actionCamel = ''] = fullName.split('-'); const resourceName = `${this.TOPIC_KEY}_${controllerNameToToolName(controllerPart)}`; const actionName = camelToSnake(actionCamel); // Initialize grouped entry if needed if (!map.has(resourceName)) { map.set(resourceName, { name: resourceName, actions: {}, }); } // Add the action definition to the grouped entry const grouped = map.get(resourceName)!; grouped.actions[actionName] = def; } this.resources = map; } /** * Checks if a resource has a specific action. * @param resource The name of the resource * @param action The name of the action * @returns True if the resource has the action, false otherwise */ public hasResourceAction(resource: string, action: string): boolean { if (!resource || !action) return false; const foundResource = findResource(this.getResources(), resource); return !!foundResource.actions[action]; } /** * Retrieves the available resources. * @returns A list of available resources. */ public getResources(): Map<string, McpGroupedToolDefinition> { return this.resources; } /** * Retrieves the action schema for a specific resource. * @param toolArgs The arguments provided to the tool * @returns The result of the tool execution */ public getResourceActionSchema(toolArgs: InsightsToolArgs): 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 })); }); } /** * Retrieves the code snippet for a specific resource action. * @param toolArgs The arguments provided to the tool * @returns The result of the tool execution */ public getResourceActionSnippet( toolArgs: InsightsToolArgs ): Promise<CallToolResult> | CallToolResult { const { resource, action, language, payload } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundAction = findAction(this.getResources(), resource, action!); const snippetGen = (foundAction as any).snippetGenerator || 'oas'; const generator = (SNIPPET_GENERATOR_MAP as any)[snippetGen]; if (!generator) return McpResponse(JSON.stringify({ error: 'SNIPPET_GENERATION_NOT_SUPPORTED' })); // If generator is 'oas', load specs and call the function signature used by that generator if (snippetGen === 'oas') { if (typeof language !== 'string' || !SUPPORTED_SNIPPET_LANGUAGES.includes(language)) { return McpResponse( JSON.stringify({ error: `Unsupported or missing language '${language}'. Supported languages: ${SUPPORTED_SNIPPET_LANGUAGES.join(', ')}`, }) ); } const fileName = fileURLToPath(import.meta.url); const dirName = NodePath.dirname(fileName); const specs = fs .readFileSync(NodePath.join(dirName, './resources/api-specs.json')) .toString(); const snippet = generator(foundAction.pathTemplate!, language!, specs); return McpResponse(JSON.stringify({ resource, action, language, snippet })); } // For other generators (e.g., rpc, general), call with common signature const supported = snippetGen === 'rpc' ? SUPPORTED_RPC_SNIPPET_LANGUAGES : SUPPORTED_GENERAL_SNIPPET_LANGUAGES; if (typeof language !== 'string' || !supported.includes(language as any)) { return McpResponse( JSON.stringify({ error: `Unsupported or missing language '${language}'. Supported languages: ${supported.join(', ')}`, }) ); } const snippet = generator(foundAction, action!, language as any, payload as any); return McpResponse(JSON.stringify({ resource, action, language, snippet })); }); } /** * Lists all available resources. * @returns A list of available resources. */ public listResource(): Promise<CallToolResult> | CallToolResult { return withMcpResponse<CallToolResult>(async () => { const resources = Array.from(this.getResources().keys()).sort(); return McpResponse(JSON.stringify({ resources })); }); } /** * Lists all available actions for a specific resource. * @param toolArgs The arguments provided to the tool * @returns A list of available actions for the specified resource. */ public listResourceActions(toolArgs: InsightsToolArgs): Promise<CallToolResult> | CallToolResult { 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 })); }); } /** * Handles the 'invokeResourceAction' tool request * @param toolArgs The arguments provided to the tool * @param overrideApiKey Optional API key to override the default * @returns The result of the tool execution */ public async invokeResourceAction( toolArgs: InsightsToolArgs, overrideApiKey?: string ): Promise<CallToolResult> { const { resource, action, payload } = toolArgs; return withMcpResponse<CallToolResult>(async () => { const foundAction = findAction(this.getResources(), resource, action!); /** * Validate the payload against the action schema */ if ( payload === undefined || payload === null || 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: INSIGHTS_API_BASE_URL, overrideApiKey, }); // Post-process with resolver if defined try { const resolverId = (foundAction as any).resolver as string | undefined; if (!resolverId || !(RESOLVER_MAP as any)[resolverId]) return result; const resolver = (RESOLVER_MAP as any)[resolverId] as any; // Provide payload to resolver when supported (assets search/detail need it) return resolver.length >= 2 ? resolver(result, payload) : resolver(result); } catch (_e) { console.error('Error occurred while resolving:', _e); 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