evm_getnftcontractsaleprices
Fetch NFT contract sale price data including last, lowest, highest, and average prices over a specified time period to analyze market trends.
Instructions
Fetch sale prices for NFTs in a contract over a specified number of days. Returns the last sale, lowest sale, highest sale, average sale and total trades within the specified period.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chain | No | The chain to query | eth |
| days | No | The number of days to look back to find the lowest price If not provided 7 days will be the default and 365 is the maximum | |
| address | Yes | The address of the NFT collection |
Implementation Reference
- src/utils/execute-api-tool.ts:34-252 (handler)Generic execution handler for all extracted API tools, including evm_getnftcontractsaleprices. Validates tool arguments using Zod schema derived from OpenAPI inputSchema, builds and sends HTTP request to Moralis EVM API endpoint, handles auth with API key, and returns formatted response.export async function executeApiTool( toolName: string, definition: McpToolDefinition, toolArgs: JsonObject, allSecuritySchemes: Record<string, any>, token?: string, ): Promise<CallToolResult> { try { // Validate arguments against the input schema let validatedArgs: JsonObject; try { const zodSchema = getZodSchemaFromJsonSchema( definition.inputSchema, toolName, ); const argsToParse = typeof toolArgs === 'object' && toolArgs !== null ? toolArgs : {}; validatedArgs = zodSchema.parse(argsToParse); } catch (error: unknown) { if (error instanceof ZodError) { const validationErrorMessage = `Invalid arguments for tool '${toolName}': ${error.errors.map((e) => `${e.path.join('.')} (${e.code}): ${e.message}`).join(', ')}`; return { content: [{ type: 'text', text: validationErrorMessage }] }; } else { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `Internal error during validation setup: ${errorMessage}`, }, ], }; } } // Prepare URL, query parameters, headers, and request body let urlPath = definition.pathTemplate; const queryParams: Record<string, any> = {}; const headers: Record<string, string> = { Accept: 'application/json', 'X-Moralis-Platform': 'MCP', }; let requestBodyData: any = undefined; // Apply parameters to the URL path, query, or headers definition.executionParameters.forEach((param) => { const value = validatedArgs[param.name]; if (typeof value !== 'undefined' && value !== null) { if (param.in === 'path') { urlPath = urlPath.replace( `{${param.name}}`, encodeURIComponent(String(value)), ); } else if (param.in === 'query') { queryParams[param.name] = value; } else if (param.in === 'header') { headers[param.name.toLowerCase()] = String(value); } } }); // Ensure all path parameters are resolved if (urlPath.includes('{')) { throw new Error(`Failed to resolve path parameters: ${urlPath}`); } // Construct the full URL const requestUrl = `${definition.baseUrl}${urlPath}`; // Handle request body if needed if ( definition.requestBodyContentType && typeof validatedArgs['requestBody'] !== 'undefined' ) { requestBodyData = validatedArgs['requestBody']; headers['content-type'] = definition.requestBodyContentType; } // Apply security requirements if available // Security requirements use OR between array items and AND within each object const appliedSecurity = definition.securityRequirements?.find((req) => { // Try each security requirement (combined with OR) return Object.entries(req).every(([schemeName, scopesArray]) => { const scheme = allSecuritySchemes[schemeName]; if (!scheme) return false; // API Key security (header, query, cookie) if (scheme.type === 'apiKey') { return !!token || !!Config.MORALIS_API_KEY; } return false; }); }); // If we found matching security scheme(s), apply them if (appliedSecurity) { // Apply each security scheme from this requirement (combined with AND) for (const [schemeName, scopesArray] of Object.entries(appliedSecurity)) { const scheme = allSecuritySchemes[schemeName]; // API Key security if (scheme?.type === 'apiKey') { const apiKey = token || Config.MORALIS_API_KEY; if (apiKey) { if (scheme.in === 'header') { headers[scheme.name.toLowerCase()] = apiKey; console.error( `Applied API key '${schemeName}' in header '${scheme.name}'`, ); } } } } } // Log warning if security is required but not available else if (definition.securityRequirements?.length > 0) { // First generate a more readable representation of the security requirements const securityRequirementsString = definition.securityRequirements .map((req) => { const parts = Object.entries(req) .map(([name, scopesArray]) => { const scopes = scopesArray as string[]; if (scopes.length === 0) return name; return `${name} (scopes: ${scopes.join(', ')})`; }) .join(' AND '); return `[${parts}]`; }) .join(' OR '); console.warn( `Tool '${toolName}' requires security: ${securityRequirementsString}, but no suitable credentials found.`, ); } // Prepare the axios request configuration const config: AxiosRequestConfig = { method: definition.method.toUpperCase(), url: requestUrl, params: queryParams, headers: headers, ...(requestBodyData !== undefined && { data: requestBodyData }), }; // Log request info to stderr (doesn't affect MCP output) console.error( `Executing tool "${toolName}": ${config.method} ${config.url}`, ); // Execute the request const response = await axios(config); // Process and format the response let responseText = ''; const contentType = response.headers['content-type']?.toLowerCase() || ''; // Handle JSON responses if ( contentType.includes('application/json') && typeof response.data === 'object' && response.data !== null ) { try { responseText = JSON.stringify(response.data, null, 2); } catch (e) { responseText = '[Stringify Error]'; } } // Handle string responses else if (typeof response.data === 'string') { responseText = response.data; } // Handle other response types else if (response.data !== undefined && response.data !== null) { responseText = String(response.data); } // Handle empty responses else { responseText = `(Status: ${response.status} - No body content)`; } // Return formatted response return { content: [ { type: 'text', text: `API Response (Status: ${response.status}):\n${responseText}`, }, ], }; } catch (error: unknown) { // Handle errors during execution let errorMessage: string; // Format Axios errors specially if (axios.isAxiosError(error)) { errorMessage = formatApiError(error); } // Handle standard errors else if (error instanceof Error) { errorMessage = error.message; } // Handle unexpected error types else { errorMessage = `Unexpected error: ${String(error)}`; } // Log error to stderr console.error( `Error during execution of tool '${toolName}':`, errorMessage, ); // Return error message to client return { content: [{ type: 'text', text: errorMessage }] }; } }
- src/server.ts:97-120 (registration)MCP CallToolRequestSchema request handler that looks up the tool definition by name (e.g. 'evm_getnftcontractsaleprices') from the map populated from OpenAPI specs and dispatches to the generic executeApiTool handler.CallToolRequestSchema, async (request: CallToolRequest, c): Promise<CallToolResult> => { const { name: toolName, arguments: toolArgs } = request.params; const toolDefinition = toolDefinitionMap[toolName]; if (!toolDefinition) { console.error(`Error: Unknown tool requested: ${toolName}`); return { content: [ { type: 'text', text: `Error: Unknown tool requested: ${toolName}`, }, ], }; } return executeApiTool( toolName, toolDefinition, toolArgs ?? {}, securitySchemes, c.authInfo?.token, ); }, );
- src/parser/extract-tools.ts:17-93 (schema)Extracts tool definitions from Moralis EVM OpenAPI spec, adds 'evm_' prefix to operation names to create tools like 'evm_getnftcontractsaleprices', generates JSON Schema inputSchema from operation parameters and requestBody for MCP tool registration and validation.export function extractToolsFromApi( api: OpenAPIV3DocumentX, prefix?: string ): McpToolDefinition[] { const tools: McpToolDefinition[] = []; const usedNames = new Set<string>(); const globalSecurity = api.security || []; if (!api.paths) return tools; for (const [path, pathItem] of Object.entries(api.paths)) { if (!pathItem) continue; for (const method of Object.values(OpenAPIV3.HttpMethods)) { const operation = pathItem[method]; if (!operation) continue; // Generate a unique name for the tool let baseName = operation.operationId || generateOperationId(method, path); if (!baseName) continue; // Sanitize the name to be MCP-compatible (only a-z, 0-9, _, -) baseName = baseName .replace(/\./g, '_') .replace(/[^a-z0-9_-]/gi, '_') .toLowerCase(); let finalToolName = baseName; let counter = 1; while (usedNames.has(finalToolName)) { finalToolName = `${baseName}_${counter++}`; } usedNames.add(finalToolName); // Get or create a description const description = operation.description || operation.summary || `Executes ${method.toUpperCase()} ${path}`; const prompt = operation['x-mcp-prompt']; // Generate input schema and extract parameters const { inputSchema, parameters, requestBodyContentType } = generateInputSchemaAndDetails(operation); // Extract parameter details for execution const executionParameters = parameters.map((p) => ({ name: p.name, in: p.in, })); // Determine security requirements const securityRequirements = operation.security === null ? globalSecurity : operation.security || globalSecurity; // Create the tool definition tools.push({ name: prefix + finalToolName, description, inputSchema, method, pathTemplate: path, parameters, executionParameters, requestBodyContentType, securityRequirements, operationId: baseName, prompt, }); } } return tools; }
- src/server.ts:85-94 (registration)MCP ListToolsRequestSchema handler that returns the list of all extracted tools including evm_getnftcontractsaleprices with their name, description, and inputSchema.server.setRequestHandler(ListToolsRequestSchema, async () => { const toolsForClient: Tool[] = Object.values(toolDefinitionMap).map( (def) => ({ name: def.name, description: def.description, inputSchema: def.inputSchema, }), ); return { tools: toolsForClient }; });
- src/config.ts:15-22 (helper)EVM configuration defining the 'evm_' prefix for tool names and URLs to the Moralis EVM API base and OpenAPI spec from which evm_getnftcontractsaleprices tool is dynamically extracted.evm: { prefix: 'evm_', baseUrl: process.env.API_BASE_URL || 'https://deep-index.moralis.io/api/v2.2', specUrl: process.env.API_SPEC_URL || 'https://deep-index.moralis.io/api-docs-2.2/v2.2/swagger.json', },