Skip to main content
Glama
CryptoQuantOfficial

CryptoQuant MCP Server

Official

initialize

Initialize a CryptoQuant session to obtain available assets, metric categories, and plan. Required before using any other tools.

Instructions

Initialize CryptoQuant session. MUST be called first before any other CryptoQuant tools. Returns available assets (btc, eth, etc.), metric categories per asset (e.g., market-indicator, network-indicator), and your plan. Use the returned asset_categories to know which discover_endpoints() calls are valid.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
api_keyNoAPI key (optional if CRYPTOQUANT_API_KEY env var is set)

Implementation Reference

  • Registration of the 'initialize' tool on the MCP server via server.tool(). This is where the tool name, description, schema, and handler are bound together.
    export function registerAuthTools(server: McpServer): void {
      server.tool(
        "initialize",
        "Initialize CryptoQuant session. MUST be called first before any other CryptoQuant tools. Returns available assets (btc, eth, etc.), metric categories per asset (e.g., market-indicator, network-indicator), and your plan. Use the returned asset_categories to know which discover_endpoints() calls are valid.",
        initializeSchema,
        async (params: InitializeParams) => {
          logger.debug("[initialize] starting initialization");
          const apiKey = resolveApiKey(params.api_key);
    
          if (!apiKey) {
            logger.debug("[initialize] no API key available");
            return jsonResponse({
              status: "api_key_required",
              message: "API key not found. Please configure using one of these methods:",
              setup_options: [
                {
                  method: "Environment Variable (Recommended)",
                  instruction: "Add to your MCP config (~/.claude/mcp.json):",
                  example: {
                    mcpServers: {
                      cryptoquant: {
                        command: "npx",
                        args: ["-y", "@cryptoquant_official/mcp"],
                        env: { CRYPTOQUANT_API_KEY: "your-api-key" },
                      },
                    },
                  },
                },
                {
                  method: "Direct Parameter",
                  instruction: "Call initialize with api_key parameter:",
                  example: "initialize(api_key='your-api-key')",
                },
              ],
              get_api_key: "https://cryptoquant.com/settings/api",
            });
          }
    
          const apiUrl = getApiUrl();
          logger.debug("[initialize] using API URL:", apiUrl);
    
          const result = await initializePermissions(apiKey, apiUrl);
          logger.debug("[initialize] permissions result:", {
            success: result.success,
            from_cache: result.from_cache,
            cache_status: result.cache_status,
            error: result.error,
          });
    
          if (!result.success) {
            return jsonResponse({
              success: false,
              error: result.error,
              help: {
                check_key: "Check your API key at https://cryptoquant.com/settings/api",
                retry: "Or call initialize(api_key='your-api-key') with a valid key",
              },
            });
          }
    
          // Save or update credentials based on key source
          if (params.api_key) {
            // Key from parameter: save it (enables key replacement)
            saveApiKey(params.api_key);
          } else if (!process.env.CRYPTOQUANT_API_KEY) {
            // Key from stored credentials: just update validated_at
            updateValidatedAt();
          }
    
          const discovery = getDiscoverySummary();
          const assetCategories = getAssetCategoryMap();
          const permState = getPermissionState();
          const planState = getPlanLimitsState();
          const cachedDiscovery = getCachedDiscovery();
    
          const totalEndpoints = discovery?.total_endpoints || cachedDiscovery?.summary.total_endpoints || 0;
          const accessibleSummary = getAccessibleEndpointsSummary(totalEndpoints);
    
          // Build session info with cache status
          const sessionInfo = {
            plan: permState.plan,
            cache_status: result.cache_status,
            ...(planState.apiRateLimit && {
              rate_limit: `${planState.apiRateLimit.token}/${planState.apiRateLimit.window}`,
            }),
          };
    
          // Build scope info (compact summary)
          const scopeInfo: Record<string, unknown> = {
            total_endpoints: discovery?.total_endpoints || cachedDiscovery?.summary.total_endpoints || 0,
            accessible: accessibleSummary.count,
            note: "Use discover_endpoints(asset, category) for details",
          };
    
          // Add asset breakdown from cache summary if available
          if (cachedDiscovery?.summary.assets) {
            const assets: Record<string, { endpoints: number; categories: number }> = {};
            for (const assetInfo of cachedDiscovery.summary.assets) {
              assets[assetInfo.name] = {
                endpoints: assetInfo.endpoint_count,
                categories: assetInfo.categories.length,
              };
            }
            scopeInfo.assets = assets;
          } else if (discovery) {
            // Fallback to discovery data
            const assets: Record<string, { endpoints: number; categories: number }> = {};
            for (const assetInfo of discovery.assets) {
              assets[assetInfo.name] = {
                endpoints: assetInfo.count,
                categories: assetCategories?.[assetInfo.name]?.length || 0,
              };
            }
            scopeInfo.assets = assets;
          }
    
          const planInfo = {
            plan: permState.plan,
            plan_limits_loaded: planState.loaded,
            accessible_endpoints: accessibleSummary.count,
            // Only include endpoint details for basic/advanced plans (limited access)
            ...(accessibleSummary.endpoints && { accessible_list: accessibleSummary.endpoints }),
            fetched_at: planState.fetched_at ? new Date(planState.fetched_at).toISOString() : null,
            note: getPlanNote(permState.plan),
          };
    
          return jsonResponse({
            success: true,
            session: sessionInfo,
            scope: scopeInfo,
            discovery: discovery
              ? {
                  total_endpoints: discovery.total_endpoints,
                  assets: discovery.assets.map((a) => a.name),
                  categories: discovery.categories.map((c) => c.name),
                  asset_categories: assetCategories,
                  fetched_at: discovery.fetched_at,
                }
              : null,
            plan_info: planInfo,
            ...(result.discovery_error && { warning: `Discovery partial: ${result.discovery_error}` }),
          });
        }
      );
  • The handler function for the 'initialize' tool. It resolves the API key, calls initializePermissions(), and returns a structured response with session info, discovery scope, and plan details.
    async (params: InitializeParams) => {
      logger.debug("[initialize] starting initialization");
      const apiKey = resolveApiKey(params.api_key);
    
      if (!apiKey) {
        logger.debug("[initialize] no API key available");
        return jsonResponse({
          status: "api_key_required",
          message: "API key not found. Please configure using one of these methods:",
          setup_options: [
            {
              method: "Environment Variable (Recommended)",
              instruction: "Add to your MCP config (~/.claude/mcp.json):",
              example: {
                mcpServers: {
                  cryptoquant: {
                    command: "npx",
                    args: ["-y", "@cryptoquant_official/mcp"],
                    env: { CRYPTOQUANT_API_KEY: "your-api-key" },
                  },
                },
              },
            },
            {
              method: "Direct Parameter",
              instruction: "Call initialize with api_key parameter:",
              example: "initialize(api_key='your-api-key')",
            },
          ],
          get_api_key: "https://cryptoquant.com/settings/api",
        });
      }
    
      const apiUrl = getApiUrl();
      logger.debug("[initialize] using API URL:", apiUrl);
    
      const result = await initializePermissions(apiKey, apiUrl);
      logger.debug("[initialize] permissions result:", {
        success: result.success,
        from_cache: result.from_cache,
        cache_status: result.cache_status,
        error: result.error,
      });
    
      if (!result.success) {
        return jsonResponse({
          success: false,
          error: result.error,
          help: {
            check_key: "Check your API key at https://cryptoquant.com/settings/api",
            retry: "Or call initialize(api_key='your-api-key') with a valid key",
          },
        });
      }
    
      // Save or update credentials based on key source
      if (params.api_key) {
        // Key from parameter: save it (enables key replacement)
        saveApiKey(params.api_key);
      } else if (!process.env.CRYPTOQUANT_API_KEY) {
        // Key from stored credentials: just update validated_at
        updateValidatedAt();
      }
    
      const discovery = getDiscoverySummary();
      const assetCategories = getAssetCategoryMap();
      const permState = getPermissionState();
      const planState = getPlanLimitsState();
      const cachedDiscovery = getCachedDiscovery();
    
      const totalEndpoints = discovery?.total_endpoints || cachedDiscovery?.summary.total_endpoints || 0;
      const accessibleSummary = getAccessibleEndpointsSummary(totalEndpoints);
    
      // Build session info with cache status
      const sessionInfo = {
        plan: permState.plan,
        cache_status: result.cache_status,
        ...(planState.apiRateLimit && {
          rate_limit: `${planState.apiRateLimit.token}/${planState.apiRateLimit.window}`,
        }),
      };
    
      // Build scope info (compact summary)
      const scopeInfo: Record<string, unknown> = {
        total_endpoints: discovery?.total_endpoints || cachedDiscovery?.summary.total_endpoints || 0,
        accessible: accessibleSummary.count,
        note: "Use discover_endpoints(asset, category) for details",
      };
    
      // Add asset breakdown from cache summary if available
      if (cachedDiscovery?.summary.assets) {
        const assets: Record<string, { endpoints: number; categories: number }> = {};
        for (const assetInfo of cachedDiscovery.summary.assets) {
          assets[assetInfo.name] = {
            endpoints: assetInfo.endpoint_count,
            categories: assetInfo.categories.length,
          };
        }
        scopeInfo.assets = assets;
      } else if (discovery) {
        // Fallback to discovery data
        const assets: Record<string, { endpoints: number; categories: number }> = {};
        for (const assetInfo of discovery.assets) {
          assets[assetInfo.name] = {
            endpoints: assetInfo.count,
            categories: assetCategories?.[assetInfo.name]?.length || 0,
          };
        }
        scopeInfo.assets = assets;
      }
    
      const planInfo = {
        plan: permState.plan,
        plan_limits_loaded: planState.loaded,
        accessible_endpoints: accessibleSummary.count,
        // Only include endpoint details for basic/advanced plans (limited access)
        ...(accessibleSummary.endpoints && { accessible_list: accessibleSummary.endpoints }),
        fetched_at: planState.fetched_at ? new Date(planState.fetched_at).toISOString() : null,
        note: getPlanNote(permState.plan),
      };
    
      return jsonResponse({
        success: true,
        session: sessionInfo,
        scope: scopeInfo,
        discovery: discovery
          ? {
              total_endpoints: discovery.total_endpoints,
              assets: discovery.assets.map((a) => a.name),
              categories: discovery.categories.map((c) => c.name),
              asset_categories: assetCategories,
              fetched_at: discovery.fetched_at,
            }
          : null,
        plan_info: planInfo,
        ...(result.discovery_error && { warning: `Discovery partial: ${result.discovery_error}` }),
      });
    }
  • Zod schema for the 'initialize' tool. Defines the single optional parameter 'api_key'.
    const initializeSchema = {
      api_key: z.string().optional().describe("API key (optional if CRYPTOQUANT_API_KEY env var is set)"),
    };
  • Helper function that resolves the API key from parameter, environment variable CRYPTOQUANT_API_KEY, or stored credentials, in priority order.
    function resolveApiKey(paramKey?: string): string | undefined {
      if (paramKey) {
        logger.debug("[resolveApiKey] using key from parameter");
        return paramKey;
      }
    
      const envKey = process.env.CRYPTOQUANT_API_KEY;
      const isEnvKeyValid = envKey && envKey.trim() && !envKey.startsWith("${");
      if (isEnvKeyValid) {
        logger.debug("[resolveApiKey] using key from environment variable");
        return envKey;
      }
    
      const storedKey = getStoredApiKey();
      if (storedKey) {
        logger.debug("[resolveApiKey] using key from stored credentials");
        return storedKey;
      }
    
      logger.debug("[resolveApiKey] no API key found");
      return undefined;
    }
  • Core initialization logic: validates cache, fetches discovery endpoints and plan limits from CryptoQuant API, writes cache, and returns the initialization result.
    export async function initializePermissions(
      apiKey: string,
      apiUrl: string,
    ): Promise<InitializeResult> {
      logger.debug("[initializePermissions] starting with API URL:", apiUrl);
      const apiKeyPrefix = apiKey.slice(0, 8);
    
      // 1. Try cache first for /my/discovery data
      const cache = readCache(apiUrl);
      logger.debug("[initializePermissions] cache status:", cache ? "found" : "not found");
    
      if (cache && isCacheValid(cache, apiUrl, apiKeyPrefix)) {
        logger.debug("[initializePermissions] using valid cache, plan:", cache.metadata.plan);
        // Load plan limits from cache
        loadPlanLimitsFromCache(
          cache.parsed.limits,
          cache.parsed.statics,
          cache.parsed.apiRateLimit,
          cache.metadata.plan,
        );
    
        // Still need to fetch /discovery/endpoints for parameter options
        const discoveryResult = await fetchDiscoveryEndpoints(apiKey, apiUrl);
        if (!discoveryResult.success) {
          // Discovery failed but we have cache - continue with warning
          logger.warn(`Discovery fetch failed, using cache: ${discoveryResult.error}`);
        }
    
        const cacheStatus = getCacheStatus(cache, true);
        cachedDiscovery = cache;
    
        permissionState = {
          authenticated: true,
          api_key: apiKey,
          cached_at: Date.now(),
          plan: cache.metadata.plan,
          plan_limits_loaded: true,
          from_cache: true,
        };
    
        return {
          success: true,
          from_cache: true,
          cache_status: cacheStatus,
          summary: cache.summary,
        };
      }
    
      // 2. Cache miss or invalid - fetch fresh data
      logger.debug("[initializePermissions] cache miss or invalid, fetching fresh data");
    
      try {
        // Fetch /discovery/endpoints (for parameter options)
        logger.debug("[initializePermissions] fetching /discovery/endpoints");
        const discoveryResult = await fetchDiscoveryEndpoints(apiKey, apiUrl);
        if (!discoveryResult.success) {
          logger.debug("[initializePermissions] discovery fetch failed:", discoveryResult.error);
          return {
            success: false,
            error: discoveryResult.error,
            from_cache: false,
            cache_status: "none",
          };
        }
        logger.debug("[initializePermissions] discovery fetch succeeded");
    
        // Fetch /my/discovery/endpoints (for plan limits)
        logger.debug("[initializePermissions] fetching /my/discovery/endpoints");
        const planResult = await fetchPlanLimits(apiKey, apiUrl);
        logger.debug("[initializePermissions] plan limits fetch:", planResult.success ? "success" : "failed");
        const planState = getPlanLimitsState();
    
        if (!planResult.success) {
          logger.warn(`Plan limits fetch failed: ${planResult.error}`);
        }
    
        // 3. Generate summary and write cache
        if (planResult.success && planResult.rawResponse && planResult.parsed) {
          const rawResponse = extractRawResponse(planResult.rawResponse);
          if (rawResponse) {
            const summary = generateSummary(
              planResult.parsed.limits,
              planResult.parsed.statics,
            );
            writeCache(
              apiUrl,
              apiKey,
              rawResponse,
              planResult.parsed,
              summary,
              planResult.plan || "unknown",
            );
    
            // Store for later use
            const newCache = readCache(apiUrl);
            cachedDiscovery = newCache;
    
            permissionState = {
              authenticated: true,
              api_key: apiKey,
              cached_at: Date.now(),
              plan: planState.plan,
              plan_limits_loaded: planState.loaded,
              from_cache: false,
            };
    
            return {
              success: true,
              from_cache: false,
              cache_status: "fresh",
              summary,
            };
          }
        }
    
        // Cache write failed but auth succeeded
        permissionState = {
          authenticated: true,
          api_key: apiKey,
          cached_at: Date.now(),
          plan: planState.plan,
          plan_limits_loaded: planState.loaded,
          from_cache: false,
        };
    
        return { success: true, from_cache: false, cache_status: "none" };
      } catch (error) {
        return {
          success: false,
          error: `Network error: ${error}`,
          from_cache: false,
          cache_status: "none",
        };
      }
    }
Behavior4/5

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

With no annotations, the description carries full burden. It reveals the tool establishes session state and returns critical data for subsequent calls. Could mention behavior on re-call or session lifetime.

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

Conciseness5/5

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

Two sentences, front-loading the critical requirement, with no wasted words. Efficient and impactful.

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

Completeness4/5

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

Explains return values and how to use them, but lacks detail on return structure or format. Given no output schema, slight gap remains. Still fairly complete for an init tool.

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 coverage is 100%, so baseline is 3. The description adds no parameter details; the api_key parameter's description is already in the schema. No additional semantic value from description.

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

Purpose5/5

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

Clearly states the tool initializes a CryptoQuant session and must be called first. Describes what it returns (assets, metric categories, plan) and how it relates to sibling tools like discover_endpoints().

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?

Explicitly states prerequisite ('MUST be called first') and provides a specific use case for the returned data. Lacks explicit exclusions for when not to use, but context is sufficient.

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/CryptoQuantOfficial/cryptoquant-mcp'

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