Skip to main content
Glama
nodit-apidoc-helper.ts9.42 kB
import yaml from 'js-yaml'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export interface OpenApiOperation { operationId: string; description: string; requestBody: any; responses: any; parameters: Array<{ name: string; schema: { type: string; enum?: string[]; } }> } export interface OpenApiPathItem { post?: OpenApiOperation; get?: OpenApiOperation; put?: OpenApiOperation; patch?: OpenApiOperation; delete?: OpenApiOperation; } export interface NoditOpenApiSpecType { openapi: string; info: { title: string; version: string }; servers: [{ url: string; variables?: Record<string, { default: string }> }]; paths: Record<string, OpenApiPathItem>; components: any; security: any[]; } export const BLOCKED_OPERATION_IDS = new Set([ "solana-getProgramAccounts", "solana-getClusterNodes", "solana-getLeaderSchedule", "solana-getSignaturesForAddress", "solana-getBlock", "solana-getBlocks", "solana-getBlocksWithLimit", "solana-getVoteAccounts", "solana-getInflationGovernor", "solana-getInflationRate", "solana-getInflationReward", "solana-getSupply", ]); export function log(message: string, ...args: any[]) { console.error(message, ...args); } export function loadNoditDataApiSpec(): NoditOpenApiSpecType { const specPath = path.resolve(__dirname, '../spec/reference/web3-data-api.yaml'); return loadOpenapiSpecFile(specPath) as NoditOpenApiSpecType; } export function loadNoditWebhookApiSpec(): NoditOpenApiSpecType { const specPath = path.resolve(__dirname, '../spec/reference/webhook.yaml'); return loadOpenapiSpecFile(specPath) as NoditOpenApiSpecType; } export function loadNoditNodeApiSpecMap(): Map<string, NoditOpenApiSpecType> { const noditApiSpecMap = new Map<string, NoditOpenApiSpecType>(); const specDir = path.resolve(__dirname, '../spec/reference'); try { const files = fs.readdirSync(specDir); const evmSpecFiles = files.filter(file => file.startsWith('evm-') && file.endsWith('.yaml')); for (const file of evmSpecFiles) { const parts = file.replace('.yaml', '').split('-'); if (parts.length >= 2) { const chain = parts[1]; const filePath = path.join(specDir, file); try { const spec = loadOpenapiSpecFile(filePath) as NoditOpenApiSpecType; const operationId = spec.paths['/']!.post!.operationId; if (operationId) { const key = chain === 'ethereum' ? `ethereum-${operationId}` : operationId; noditApiSpecMap.set(key, spec); } else { log(`Could not extract operationId from spec file ${file}`); } } catch (error) { log(`Error loading spec file ${file}:`, error); } } } const suiNodeApiSpecDir = path.resolve(__dirname, '../spec/reference/sui-node-api'); const suiNodeApiSpecFiles = fs.readdirSync(suiNodeApiSpecDir); for (const file of suiNodeApiSpecFiles) { if (file.endsWith('.yaml')) { const filePath = path.join(suiNodeApiSpecDir, file); const suiNodeApiSpecMap = loadMultiPathApiSpec(filePath); suiNodeApiSpecMap.forEach((spec, operationId) => { noditApiSpecMap.set(operationId, spec); }); } } const solanaNodeApiSpecDir = path.resolve(__dirname, '../spec/reference/solana-node-api/http-methods'); const solanaNodeApiSpecFiles = fs.readdirSync(solanaNodeApiSpecDir); for (const file of solanaNodeApiSpecFiles) { if (file.endsWith('.yaml')) { const filePath = path.join(solanaNodeApiSpecDir, file); const solanaNodeApiSpecMap = loadMultiPathApiSpec(filePath); solanaNodeApiSpecMap.forEach((spec, operationId) => { noditApiSpecMap.set(operationId, spec); }); } } return noditApiSpecMap; } catch (error) { log('Error reading spec directory:', error); return new Map(); } } function loadMultiPathApiSpec(filePath: string): Map<string, NoditOpenApiSpecType> { const specMap = new Map<string, NoditOpenApiSpecType>(); try { const spec = loadOpenapiSpecFile(filePath) as NoditOpenApiSpecType; const operationId = spec.paths['/']!.post!.operationId; if (operationId) { specMap.set(operationId, spec); } else { log(`Could not extract operationId from spec file ${filePath}`); } } catch (error) { log(`Error loading spec file ${filePath}:`, error); } return specMap } export interface Relationship { name?: string; using?: { manual_configuration?: { remote_table?: { name?: string; }; column_mapping?: Record<string, any>; }; }; } export interface GraphQLSpec { name: string; table?: string; columns: string[]; relationships: { object: Array<{ name: string; remote_table: string; column_mapping: Record<string, any>; }>; array: Array<{ name: string; remote_table: string; column_mapping: Record<string, any>; }>; }; } export interface AptosIndexerApiSpec { metadata?: { sources?: Array<{ tables?: Array<{ table?: string; configuration?: { custom_name?: string; }; select_permissions?: Array<{ permission?: { columns?: string[]; }; }>; object_relationships?: Array<Relationship>; array_relationships?: Array<Relationship>; }>; }>; }; } export function loadNoditAptosIndexerApiSpec(): AptosIndexerApiSpec { const schemaPath = path.resolve(__dirname, '../nodit-aptos-indexer-api-schema.json'); return loadOpenapiSpecFile(schemaPath) as AptosIndexerApiSpec; } export function isNodeApi(operationId: string): boolean { return operationId.includes("_") || operationId.startsWith("solana-"); } export function isEthereumNodeApi(operationId: string): boolean { return !operationId.includes("-") } export function isWebhookApi(operationId: string): boolean { return operationId.includes("Webhook"); } export function isBlockedOperationId(operationId: string): boolean { return BLOCKED_OPERATION_IDS.has(operationId); } export function findNoditNodeApiSpec(operationId: string, noditNodeApiSpecMap: Map<string, NoditOpenApiSpecType>): NoditOpenApiSpecType | undefined { let key = operationId; if (isEthereumNodeApi(operationId)) { key = `ethereum-${operationId}`; } return noditNodeApiSpecMap.get(key); } export function isValidNodeApi(operationId: string, noditNodeApiSpecMap: Map<string, NoditOpenApiSpecType>): boolean { return findNoditNodeApiSpec(operationId, noditNodeApiSpecMap) !== undefined; } export function loadOpenapiSpecFile(path: string): unknown { const fileContents = fs.readFileSync(path, 'utf8'); if (path.endsWith('.json')) { return JSON.parse(fileContents); } return yaml.load(fileContents); } export function normalizeDescription(description: string | undefined): string { if (!description) { return "No description available." } const lines = description.split('\n'); const filteredLines = lines.filter(line => !line.trimStart().startsWith('>')); return filteredLines.join('\n').trim(); } export function findNoditDataApiDetails(operationId: string, spec: NoditOpenApiSpecType): { path: string; method: string; details: OpenApiOperation } | null { if (!spec || !spec.paths) { log("findApiDetails: Invalid spec object or missing paths."); return null; } for (const pathKey in spec.paths) { if (Object.prototype.hasOwnProperty.call(spec.paths, pathKey)) { const pathItem = spec.paths[pathKey]; if (pathItem?.post?.operationId === operationId) { return { path: pathKey, method: 'post', details: pathItem.post }; } } } return null; } export function findNoditNodeApiDetails(operationId: string, specMap: Map<string, NoditOpenApiSpecType>): { path: string; method: string; details: OpenApiOperation } | null { const spec = findNoditNodeApiSpec(operationId, specMap); if (spec && spec.paths['/']?.post) { return { path: '/', method: 'post', details: spec.paths['/']?.post } } return null; } export function findNoditWebhookApiDetails(operationId: string, spec: NoditOpenApiSpecType): { path: string; method: string; details: OpenApiOperation } | null { for (const [path, pathItem] of Object.entries(spec.paths)) { const methods: Array<{ method: string; operation?: OpenApiOperation }> = [ { method: 'get', operation: pathItem.get }, { method: 'post', operation: pathItem.post }, { method: 'put', operation: pathItem.put }, { method: 'patch', operation: pathItem.patch }, { method: 'delete', operation: pathItem.delete } ]; for (const { method, operation } of methods) { if (operation?.operationId === operationId) { return { path, method, details: operation }; } } } return null; } export function createErrorResponse(message: string, toolName: string): { content: { type: "text"; text: string }[] } { log(`Tool Error (${toolName}): ${message}`); return { content: [{ type: "text" as const, text: `Tool Error: ${message}` }] }; }

Implementation Reference

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/noditlabs/nodit-mcp-server'

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