Skip to main content
Glama

call_nodit_api

Execute blockchain API calls to access and process real-time data across multiple networks like Ethereum and Polygon using operation IDs.

Instructions

This function calls a specific Nodit Blockchain Context API using its operationId. Before making the call, it's recommended to verify the detailed API specifications using the 'get_nodit_api_spec' tool. Please note that using this tool will consume your API quota.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chainYesNodit chain to call. e.g. 'ethereum' or 'polygon'.
networkYesNodit network to call. e.g. 'mainnet' or 'amoy'.
operationIdYesNodit API operationId to call.
requestBodyYesJSON request body matching the API's spec.

Implementation Reference

  • The core handler function that implements the 'call_nodit_api' tool. It validates the operationId, determines if it's a node or data API, constructs the appropriate API URL, checks API key, makes a POST request with timeout, handles various HTTP errors with customized user-friendly messages, parses the JSON response, and returns it as tool content.
      async ({ chain, network, operationId, requestBody }) => {
        const toolName = "call_nodit_api";
    
        if (isWebhookApi(operationId)) {
          return createErrorResponse(
              `The Nodit Webhook APIs cannot be invoked via "${toolName}".`,
              toolName,
          )
        }
    
        if (isBlockedOperationId(operationId)) {
          return createErrorResponse(
              `The operationId(${operationId}) cannot be invoked via "${toolName}".`,
              toolName,
          )
        }
    
        const apiKey = process.env.NODIT_API_KEY;
        if (!apiKey) {
            return createErrorResponse(`NODIT_API_KEY environment variable is not set. It is required to call nodit api. Please check your tool configuration.`, toolName);
        }
    
        const isNodeApiCall = isNodeApi(operationId);
        const canFindOperationId = isNodeApiCall ? isValidNodeApi(operationId, noditNodeApiSpecMap) : findNoditDataApiDetails(operationId, noditDataApiSpec)
        if (!canFindOperationId) {
          return createErrorResponse(`Invalid operationId '${operationId}'. Use 'list_nodit_data_apis' or 'list_nodit_node_apis' first.`, toolName);
        }
    
        const commonMistakeForOperationIdRules = isNodeApiCall && chain !== "ethereum" && !operationId.includes("-")
        if (commonMistakeForOperationIdRules) {
          return createErrorResponse(`Invalid operationId '${operationId}'. For non-ethereum chains, operationId must include the chain prefix.`, toolName);
        }
    
        let apiUrl;
        if (isNodeApiCall) {
            const apiUrlTemplate = findNoditNodeApiSpec(operationId, noditNodeApiSpecMap)!.servers[0].url
            apiUrl = apiUrlTemplate.replace(`{${chain}-network}`, `${chain}-${network}`)
        } else {
            const noditDataApiPath = Object.entries(noditDataApiSpec.paths).find(([, spec]) => spec.post?.operationId === operationId)
            if (!noditDataApiPath) {
                return createErrorResponse(`Invalid operationId '${operationId}'. No API URL found for operationId '${operationId}'.`, toolName);
            }
            const apiUrlTemplate = noditDataApiSpec.servers[0].url + noditDataApiPath[0];
            apiUrl = apiUrlTemplate.replace('{chain}/{network}', `${chain}/${network}`)
        }
    
        if (!apiUrl) {
          return createErrorResponse(`Invalid operationId '${operationId}'. No API URL found for operationId '${operationId}'.`, toolName);
        }
    
        const { signal, cleanup } = createTimeoutSignal(TIMEOUT_MS);
        try {
          const apiOptions: RequestInit = {
              method: 'POST',
              headers: { 'X-API-KEY': apiKey, 'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'nodit-mcp-server' },
              body: JSON.stringify(requestBody),
              signal,
          }
    
          log(`Calling apiUrl: ${apiUrl}, apiOptions: ${JSON.stringify(apiOptions, null, 2)}`);
    
          const response = await fetch(apiUrl, apiOptions);
    
          const responseBodyText = await response.text();
    
          if (!response.ok) {
            const statusMessages: Record<number, string> = {
              400: `${responseBodyText}. Help the user identify what went wrong in their request. Explain the likely issue based on the error message, and provide a corrected example if possible.`,
              403: `${responseBodyText}. Let the user know that this API is only available to paid plan users. Explain that their current plan does not include access, and suggest upgrading to a paid tier via https://nodit.io/pricing .`,
              404: `${responseBodyText}. Let the user know that no data was found for the provided ID or address. This usually means the resource doesn't exist or hasn't been indexed yet. Suggest double-checking the input or trying again later.`,
              429: `${responseBodyText}. Inform the user that they've reached their current plan's usage limit. Recommend reviewing their usage or upgrading via https://nodit.io/pricing. Optionally mention the Referral Program: https://developer.nodit.io/docs/referral-program.`,
              500: `${responseBodyText}. This is not the user's fault. Let them know it's likely a temporary issue. Suggest retrying soon or contacting support at https://developer.nodit.io/discuss if the problem continues.`,
              503: `${responseBodyText}. Inform the user that the service may be under maintenance or experiencing high load. Suggest retrying shortly, and checking the Notice section in the Nodit Developer Portal (https://developer.nodit.io).`
            };
    
            if (statusMessages[response.status]) {
              return createErrorResponse(statusMessages[response.status], toolName);
            }
    
            let errorDetails = `Raw error response: ${responseBodyText}`;
            try {
              const errorJson = JSON.parse(responseBodyText);
              errorDetails = `Error Details (JSON):\n${JSON.stringify(errorJson, null, 2)}`;
            } catch (e) { /* ignore parsing error, use raw text */ }
            return createErrorResponse(`API Error (Status ${response.status}). ${errorDetails}`, toolName);
          }
    
          try {
            JSON.parse(responseBodyText);
            log(`Tool (${toolName}): API Success (${response.status}) for ${operationId}`);
            return { content: [{ type: "text", text: responseBodyText }] };
          } catch (parseError) {
            return createErrorResponse(`API returned OK status but body was not valid JSON. Raw response: ${responseBodyText}`, toolName);
          }
    
        } catch (error) {
          let message = (error as Error).message;
          if (error instanceof Error && error.name === 'AbortError') {
            message = `The request took longer than expected and has been terminated. This may be due to high server load or because the requested data is taking longer to process. Please try again later.`;
          }
          return createErrorResponse(`Network/fetch error calling API: ${message}`, toolName);
        } finally {
          cleanup();
        }
      }
    );
  • Zod-based input schema defining the parameters for the 'call_nodit_api' tool: chain, network, operationId, and requestBody.
    {
      chain: z.string().describe("Nodit chain to call. e.g. 'ethereum' or 'polygon'."),
      network: z.string().describe("Nodit network to call. e.g. 'mainnet' or 'amoy'."),
      operationId: z.string().describe("Nodit API operationId to call."),
      requestBody: z.record(z.any()).describe("JSON request body matching the API's spec."),
    },
  • Registers the 'call_nodit_api' tool on the McpServer instance, loading necessary API specs, defining name, description, input schema, and handler function.
    export function registerCallNoditApiTool(server: McpServer) {
      const noditNodeApiSpecMap: Map<string, NoditOpenApiSpecType> = loadNoditNodeApiSpecMap();
      const noditDataApiSpec: NoditOpenApiSpecType = loadNoditDataApiSpec();
    
      server.tool(
        "call_nodit_api",
        "This function calls a specific Nodit Blockchain Context API using its operationId. Before making the call, it's recommended to verify the detailed API specifications using the 'get_nodit_api_spec' tool. Please note that using this tool will consume your API quota.",
        {
          chain: z.string().describe("Nodit chain to call. e.g. 'ethereum' or 'polygon'."),
          network: z.string().describe("Nodit network to call. e.g. 'mainnet' or 'amoy'."),
          operationId: z.string().describe("Nodit API operationId to call."),
          requestBody: z.record(z.any()).describe("JSON request body matching the API's spec."),
        },
        async ({ chain, network, operationId, requestBody }) => {
          const toolName = "call_nodit_api";
    
          if (isWebhookApi(operationId)) {
            return createErrorResponse(
                `The Nodit Webhook APIs cannot be invoked via "${toolName}".`,
                toolName,
            )
          }
    
          if (isBlockedOperationId(operationId)) {
            return createErrorResponse(
                `The operationId(${operationId}) cannot be invoked via "${toolName}".`,
                toolName,
            )
          }
    
          const apiKey = process.env.NODIT_API_KEY;
          if (!apiKey) {
              return createErrorResponse(`NODIT_API_KEY environment variable is not set. It is required to call nodit api. Please check your tool configuration.`, toolName);
          }
    
          const isNodeApiCall = isNodeApi(operationId);
          const canFindOperationId = isNodeApiCall ? isValidNodeApi(operationId, noditNodeApiSpecMap) : findNoditDataApiDetails(operationId, noditDataApiSpec)
          if (!canFindOperationId) {
            return createErrorResponse(`Invalid operationId '${operationId}'. Use 'list_nodit_data_apis' or 'list_nodit_node_apis' first.`, toolName);
          }
    
          const commonMistakeForOperationIdRules = isNodeApiCall && chain !== "ethereum" && !operationId.includes("-")
          if (commonMistakeForOperationIdRules) {
            return createErrorResponse(`Invalid operationId '${operationId}'. For non-ethereum chains, operationId must include the chain prefix.`, toolName);
          }
    
          let apiUrl;
          if (isNodeApiCall) {
              const apiUrlTemplate = findNoditNodeApiSpec(operationId, noditNodeApiSpecMap)!.servers[0].url
              apiUrl = apiUrlTemplate.replace(`{${chain}-network}`, `${chain}-${network}`)
          } else {
              const noditDataApiPath = Object.entries(noditDataApiSpec.paths).find(([, spec]) => spec.post?.operationId === operationId)
              if (!noditDataApiPath) {
                  return createErrorResponse(`Invalid operationId '${operationId}'. No API URL found for operationId '${operationId}'.`, toolName);
              }
              const apiUrlTemplate = noditDataApiSpec.servers[0].url + noditDataApiPath[0];
              apiUrl = apiUrlTemplate.replace('{chain}/{network}', `${chain}/${network}`)
          }
    
          if (!apiUrl) {
            return createErrorResponse(`Invalid operationId '${operationId}'. No API URL found for operationId '${operationId}'.`, toolName);
          }
    
          const { signal, cleanup } = createTimeoutSignal(TIMEOUT_MS);
          try {
            const apiOptions: RequestInit = {
                method: 'POST',
                headers: { 'X-API-KEY': apiKey, 'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': 'nodit-mcp-server' },
                body: JSON.stringify(requestBody),
                signal,
            }
    
            log(`Calling apiUrl: ${apiUrl}, apiOptions: ${JSON.stringify(apiOptions, null, 2)}`);
    
            const response = await fetch(apiUrl, apiOptions);
    
            const responseBodyText = await response.text();
    
            if (!response.ok) {
              const statusMessages: Record<number, string> = {
                400: `${responseBodyText}. Help the user identify what went wrong in their request. Explain the likely issue based on the error message, and provide a corrected example if possible.`,
                403: `${responseBodyText}. Let the user know that this API is only available to paid plan users. Explain that their current plan does not include access, and suggest upgrading to a paid tier via https://nodit.io/pricing .`,
                404: `${responseBodyText}. Let the user know that no data was found for the provided ID or address. This usually means the resource doesn't exist or hasn't been indexed yet. Suggest double-checking the input or trying again later.`,
                429: `${responseBodyText}. Inform the user that they've reached their current plan's usage limit. Recommend reviewing their usage or upgrading via https://nodit.io/pricing. Optionally mention the Referral Program: https://developer.nodit.io/docs/referral-program.`,
                500: `${responseBodyText}. This is not the user's fault. Let them know it's likely a temporary issue. Suggest retrying soon or contacting support at https://developer.nodit.io/discuss if the problem continues.`,
                503: `${responseBodyText}. Inform the user that the service may be under maintenance or experiencing high load. Suggest retrying shortly, and checking the Notice section in the Nodit Developer Portal (https://developer.nodit.io).`
              };
    
              if (statusMessages[response.status]) {
                return createErrorResponse(statusMessages[response.status], toolName);
              }
    
              let errorDetails = `Raw error response: ${responseBodyText}`;
              try {
                const errorJson = JSON.parse(responseBodyText);
                errorDetails = `Error Details (JSON):\n${JSON.stringify(errorJson, null, 2)}`;
              } catch (e) { /* ignore parsing error, use raw text */ }
              return createErrorResponse(`API Error (Status ${response.status}). ${errorDetails}`, toolName);
            }
    
            try {
              JSON.parse(responseBodyText);
              log(`Tool (${toolName}): API Success (${response.status}) for ${operationId}`);
              return { content: [{ type: "text", text: responseBodyText }] };
            } catch (parseError) {
              return createErrorResponse(`API returned OK status but body was not valid JSON. Raw response: ${responseBodyText}`, toolName);
            }
    
          } catch (error) {
            let message = (error as Error).message;
            if (error instanceof Error && error.name === 'AbortError') {
              message = `The request took longer than expected and has been terminated. This may be due to high server load or because the requested data is taking longer to process. Please try again later.`;
            }
            return createErrorResponse(`Network/fetch error calling API: ${message}`, toolName);
          } finally {
            cleanup();
          }
        }
      );
    }
  • Imports and calls registerCallNoditApiTool within the registerAllTools function to include it in the overall toolset.
    import { registerCallNoditApiTool } from "./call-nodit-api.js";
    
    export function registerAllTools(server: McpServer) {
      registerApiCategoriesTools(server);
      registerNodeApiTools(server);
      registerDataApiTools(server);
      registerWebhookApiTools(server);
      registerAptosIndexerTools(server);
      registerGetNoditApiSpecTool(server);
      registerCallNoditApiTool(server);
  • src/index.ts:4-16 (registration)
    Top-level call to registerAllTools in the main server initialization, which indirectly registers the 'call_nodit_api' tool.
    import { registerAllTools } from "./tools/index.js";
    
    async function main() {
      const server = new McpServer({
        name: "nodit-blockchain-context",
        version: "1.0.0",
        capabilities: {
          resources: {},
          tools: {},
        },
      });
    
      registerAllTools(server);
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context: the recommendation to check specs first and the API quota consumption warning. However, it lacks details on error handling, response formats, authentication needs, or rate limits beyond quota. For a tool that makes API calls with 4 required parameters, this leaves gaps in behavioral understanding.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with three sentences that each serve a purpose: stating the tool's function, providing a usage recommendation, and giving a quota warning. It's front-loaded with the core purpose. While efficient, it could be slightly more structured (e.g., bullet points for key points), but it avoids unnecessary verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (API call tool with 4 required parameters, nested objects, no output schema, and no annotations), the description is moderately complete. It covers purpose, a prerequisite step, and a quota warning, but lacks details on response handling, error cases, or how to interpret results. Without an output schema, more guidance on what to expect would be helpful, making it adequate but with clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema fully documents all 4 parameters (chain, network, operationId, requestBody). The description doesn't add any parameter-specific information beyond what's in the schema. According to the rules, when schema coverage is high (>80%), the baseline is 3 even with no param info in the description, which applies here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'calls a specific Nodit Blockchain Context API using its operationId.' It specifies the verb ('calls') and resource ('Nodit Blockchain Context API'), distinguishing it from sibling tools like 'get_nodit_api_spec' which retrieves specifications. However, it doesn't explicitly differentiate from 'call_nodit_aptos_indexer_api' (a similar call tool for a different API type), keeping it from a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for usage: it recommends verifying API specs with 'get_nodit_api_spec' before calling and warns about API quota consumption. This gives practical guidance on when and how to use the tool effectively. However, it doesn't explicitly state when NOT to use it or compare it to alternatives like 'call_nodit_aptos_indexer_api', so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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