This is page 1 of 2. Use http://codebase.md/nftgo/mcp-nftgo-api?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .dockerignore ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── package.json ├── pnpm-lock.yaml ├── README.md ├── smithery.yaml ├── src │ ├── .gitignore │ ├── index.ts │ ├── nftgo.ts │ └── openapi-spec.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- ``` 1 | dist 2 | node_modules 3 | .git 4 | .idea 5 | package-lock.json 6 | pnpm-lock.yaml 7 | *.log ``` -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- ``` 1 | node_modules 2 | npm-debug.log 3 | dist 4 | Dockerfile 5 | .dockerignore 6 | .git 7 | .gitignore 8 | .idea 9 | .vscode 10 | *.log ``` -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- ``` 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "printWidth": 100, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true, 8 | "arrowParens": "avoid" 9 | } 10 | ``` -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Dependency directories 2 | node_modules/ 3 | jspm_packages/ 4 | 5 | # TypeScript cache 6 | *.tsbuildinfo 7 | 8 | # Compiled output 9 | dist/ 10 | build/ 11 | out/ 12 | 13 | # Logs 14 | logs 15 | *.log 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | lerna-debug.log* 20 | 21 | # Environment variables 22 | .env 23 | .env.test 24 | .env.local 25 | .env.development.local 26 | .env.test.local 27 | .env.production.local 28 | 29 | # IDE files 30 | .idea/ 31 | .vscode/ 32 | *.sublime-project 33 | *.sublime-workspace 34 | .project 35 | .classpath 36 | .c9/ 37 | *.launch 38 | .settings/ 39 | *.swp 40 | *.swo 41 | 42 | # OS files 43 | .DS_Store 44 | Thumbs.db 45 | /tmp/ 46 | .pnpm-store 47 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # NFTGo MCP 2 | 3 | A Model Context Protocol server that provides HTTP request to NFTGo Developer API based on NFTGo [API documentation](https://docs.nftgo.io/reference/introduction). 4 | 5 | Currently only support Ethereum. 6 | 7 | ## Key Features 8 | **1. NFT Collection** 9 | - Retrieve Collection Details: Fetch metadata and statistics for specific NFT collections. 10 | - List Collections: Obtain a list of NFT collections with filtering and sorting options. 11 | 12 | **2. NFT Asset** 13 | - Get NFT Details: Access detailed information about individual NFTs, including metadata and ownership. 14 | - List NFTs: Retrieve lists of NFTs based on various criteria such as collection, owner, or traits. 15 | 16 | **3. Market Data and Analytics** 17 | - Market Trends: Analyze market trends and metrics over time. 18 | - Price History: Access historical pricing data for NFTs and collections. 19 | - Volume and Sales Data: Retrieve data on trading volumes and sales activities. 20 | 21 | **4. User and Wallet Information** 22 | - Wallet Holdings: View NFTs held by specific wallet addresses. 23 | - Transaction History: Access the transaction history associated with wallets or NFTs. 24 | 25 | **5. Search and Filtering Capabilities** 26 | - Advanced Search: Perform searches across NFTs and collections using various filters and parameters. 27 | - Trait-Based Filtering: Filter NFTs based on specific traits or attributes. 28 | 29 | **6. Real-Time Data and Notifications** 30 | - Webhooks: Set up webhooks to receive real-time updates on specific events or changes. 31 | - Live Data Feeds: Access live data streams for market activities and NFT events. 32 | 33 | ## Usage with Claude Desktop 34 | 35 | To use this server with the Claude Desktop app, add the following configuration to the "mcpServers" section of your `claude_desktop_config.json`: 36 | 37 | ### NPX 38 | 39 | ```json 40 | { 41 | "mcpServers": { 42 | "nftgoapi": { 43 | "command": "npx", 44 | "args": ["-y", "@nftgo/mcp-nftgo-api", "NFTGO-API-KEY"] 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | Replace `NFTGO-API-KEY` with your API key. You can create your free `NFTGo-API-KEY` [here](https://nftgo.io/developers). 51 | 52 | ## Building 53 | 54 | ```sh 55 | pnpm install 56 | pnpm build 57 | ``` 58 | 59 | ## License 60 | 61 | This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository. 62 | ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | FROM node:20-bookworm 2 | 3 | WORKDIR /app 4 | 5 | # Copy package files 6 | COPY package*.json ./ 7 | 8 | # Install dependencies 9 | RUN npm install 10 | 11 | # Copy application code 12 | COPY . . 13 | 14 | # Build the application 15 | RUN npm run build 16 | 17 | # Command will be provided by smithery.yaml 18 | CMD ["node", "dist/index.js"] 19 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "outDir": "dist", 7 | "rootDir": "src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "resolveJsonModule": true 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | ``` -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- ```yaml 1 | startCommand: 2 | type: stdio 3 | configSchema: 4 | type: object 5 | properties: 6 | apiKey: 7 | type: string 8 | description: 'NFTGo API Key' 9 | required: ['apiKey'] 10 | commandFunction: | 11 | function getCommand(config) { 12 | return { 13 | command: "node", 14 | args: ["dist/index.js", config.apiKey], 15 | env: { 16 | NFTGO_API_KEY: config.apiKey 17 | } 18 | } 19 | } 20 | 21 | build: 22 | dockerfile: Dockerfile 23 | dockerBuildPath: . 24 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@nftgo/mcp-nftgo-api", 3 | "version": "0.1.2", 4 | "description": "NFTGo API MCP Server", 5 | "license": "MIT", 6 | "author": "NFTGo", 7 | "homepage": "https://nftgo.io", 8 | "type": "module", 9 | "bin": { 10 | "nftgo-api": "dist/index.js" 11 | }, 12 | "files": [ 13 | "dist" 14 | ], 15 | "scripts": { 16 | "build": "tsc && shx chmod +x dist/*.js", 17 | "start": "node dist/index.js", 18 | "watch": "tsc --watch", 19 | "format": "prettier --write .", 20 | "format:check": "prettier --check ." 21 | }, 22 | "dependencies": { 23 | "@modelcontextprotocol/sdk": "^1.9.0", 24 | "zod": "^3.24.3" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^20.0.0", 28 | "prettier": "^3.2.5", 29 | "shx": "^0.3.4", 30 | "typescript": "^5.6.2" 31 | } 32 | } 33 | ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | #!/usr/bin/env node 2 | 3 | import { Server } from '@modelcontextprotocol/sdk/server/index.js'; 4 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 5 | import { 6 | CallToolRequestSchema, 7 | ListResourcesRequestSchema, 8 | ListToolsRequestSchema, 9 | ReadResourceRequestSchema, 10 | } from '@modelcontextprotocol/sdk/types.js'; 11 | import { 12 | callTool, 13 | listResources, 14 | listTools, 15 | readResource, 16 | setNftgoApiKey, 17 | getNftgoApiKey, 18 | } from './nftgo.js'; 19 | 20 | const server = new Server( 21 | { 22 | name: 'nftgo-api', 23 | description: 'This is an assistant for NFT data in ethereum build with NFTGo API V1.1', 24 | version: '0.1.0', 25 | }, 26 | { 27 | capabilities: { 28 | resources: {}, 29 | tools: {}, 30 | }, 31 | } 32 | ); 33 | 34 | const args = process.argv.slice(2); 35 | setNftgoApiKey(args[0]); 36 | 37 | if (args.length === 0 || !getNftgoApiKey()) { 38 | console.error('Please provide a valid NFTGo API key as a command-line argument'); 39 | process.exit(1); 40 | } 41 | 42 | server.setRequestHandler(ListResourcesRequestSchema, listResources); 43 | 44 | server.setRequestHandler(ReadResourceRequestSchema, async request => { 45 | const uri = request.params.uri; 46 | return readResource(uri); 47 | }); 48 | 49 | server.setRequestHandler(ListToolsRequestSchema, listTools); 50 | 51 | server.setRequestHandler(CallToolRequestSchema, callTool); 52 | 53 | async function main() { 54 | const transport = new StdioServerTransport(); 55 | await server.connect(transport); 56 | } 57 | 58 | main(); 59 | ``` -------------------------------------------------------------------------------- /src/nftgo.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { z } from 'zod'; 2 | import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'; 3 | import { openapiSpec as openapiSpecJson } from './openapi-spec.js'; 4 | 5 | let nftgoApiKey: string; 6 | const host = 'https://data-api.nftgo.io'; 7 | 8 | const RequestSchema = z.object({ 9 | type: z.enum(['POST', 'GET', 'PUT', 'DELETE']), 10 | url: z.string(), 11 | headers: z.record(z.string(), z.string()).optional(), 12 | body: z.any().optional(), 13 | }); 14 | 15 | const openapiSpec = openapiSpecJson as ApiSpec; 16 | type ApiSpec = { 17 | paths: { 18 | [key: string]: PathSchema; 19 | }; 20 | components: { 21 | schemas: { 22 | [key: string]: object; 23 | }; 24 | }; 25 | }; 26 | type PathSchema = { 27 | get?: { 28 | summary?: string; 29 | description?: string; 30 | parameters?: Array<{ 31 | name: string; 32 | in: string; 33 | description: string; 34 | required: boolean; 35 | schema: { 36 | type: string; 37 | }; 38 | }>; 39 | requestBody?: { 40 | content?: { 41 | 'application/json'?: { 42 | schema?: { 43 | $ref?: string; 44 | }; 45 | }; 46 | }; 47 | }; 48 | [key: string]: string | object | undefined; 49 | }; 50 | post?: { 51 | summary?: string; 52 | description?: string; 53 | parameters?: Array<{ 54 | name: string; 55 | in: string; 56 | description: string; 57 | required: boolean; 58 | schema: { 59 | type: string; 60 | }; 61 | }>; 62 | requestBody?: { 63 | content?: { 64 | 'application/json'?: { 65 | schema?: { 66 | $ref?: string; 67 | }; 68 | }; 69 | }; 70 | }; 71 | [key: string]: string | object | undefined; 72 | }; 73 | }; 74 | 75 | export function getNftgoApiKey() { 76 | return nftgoApiKey; 77 | } 78 | 79 | export function setNftgoApiKey(key: string) { 80 | nftgoApiKey = key; 81 | } 82 | 83 | export async function makeRequest( 84 | url: string, 85 | type: string, 86 | headers: Record<string, string>, 87 | body: any 88 | ) { 89 | try { 90 | headers['X-API-KEY'] = getNftgoApiKey(); 91 | const response = await fetch(url, { 92 | method: type, 93 | headers, 94 | body: body && (type === 'POST' || type === 'PUT') ? JSON.stringify(body) : undefined, 95 | }); 96 | 97 | if (!response.ok) { 98 | throw new Error(`HTTP error! status: ${response.status}`); 99 | } 100 | 101 | return { 102 | status: response.status, 103 | data: await response.text(), 104 | headers: Object.fromEntries(response.headers), 105 | }; 106 | } catch (error) { 107 | console.error('Error making request:', error); 108 | throw error; 109 | } 110 | } 111 | 112 | export function listResources() { 113 | const resources = []; 114 | for (let [path, schema] of Object.entries(openapiSpec.paths)) { 115 | schema = schema as PathSchema; 116 | const detail = schema.get || schema.post; 117 | resources.push({ 118 | uri: new URL(`${host}${path}`).href, 119 | mimeType: 'application/json', 120 | name: `${detail?.summary || detail?.description} API Doc`, 121 | }); 122 | } 123 | return { 124 | resources: resources, 125 | }; 126 | } 127 | 128 | function getAPIRequestSpec(path: string) { 129 | const schema = openapiSpec.paths[path]; 130 | const detail = schema.get || schema.post; 131 | const spec: any = { 132 | method: schema.get ? 'GET' : 'POST', 133 | }; 134 | if (detail?.parameters) { 135 | spec.parameters = detail.parameters; 136 | } 137 | if (detail?.requestBody) { 138 | const bodyRef = detail.requestBody?.content?.['application/json']?.schema?.['$ref']; 139 | if (bodyRef) { 140 | const bodySchema = 141 | openapiSpec.components?.schemas?.[bodyRef.replace('#/components/schemas/', '')]; 142 | spec.body = bodySchema; 143 | } 144 | } 145 | return JSON.stringify(spec, null, 2); 146 | } 147 | 148 | export function readResource(uri: string) { 149 | const path = decodeURIComponent(uri.replace(host, '')); 150 | return { 151 | contents: [ 152 | { 153 | uri, 154 | mimeType: 'application/json', 155 | text: getAPIRequestSpec(path), 156 | }, 157 | ], 158 | }; 159 | } 160 | 161 | export function listTools() { 162 | return { 163 | tools: [ 164 | { 165 | name: 'request', 166 | description: `Make an HTTP request to NFTGo API(${host}) based on the OpenAPI specifications returned by the api-path-schema tool.`, 167 | inputSchema: { 168 | type: 'object', 169 | properties: { 170 | type: { 171 | type: 'string', 172 | description: 'Type of the request. GET, POST, PUT, DELETE', 173 | }, 174 | url: { 175 | type: 'string', 176 | description: 'Url to make the request to', 177 | }, 178 | headers: { 179 | type: 'object', 180 | description: 'Headers to include in the request', 181 | }, 182 | body: { 183 | type: 'object', 184 | description: 'Body to include in the request', 185 | }, 186 | }, 187 | required: ['type', 'url'], 188 | }, 189 | }, 190 | { 191 | name: 'api-path-schema', 192 | description: `Get detail description and parameters of a NFT API path based on OpenAPI specifications.`, 193 | inputSchema: { 194 | type: 'object', 195 | properties: { 196 | path: { 197 | type: 'string', 198 | description: 199 | 'Path to get the api detail of, format should be like: /eth/v1/nft/name/{keywords} or /eth/v1/nft/{contract}/{tokenId}/metrics', 200 | }, 201 | }, 202 | required: ['path'], 203 | }, 204 | }, 205 | { 206 | name: 'api-documentation', 207 | description: `Retrieve a comprehensive list of all NFT API endpoints with documentation based on OpenAPI specifications.`, 208 | inputSchema: { 209 | type: 'object', 210 | properties: {}, 211 | required: [], 212 | }, 213 | }, 214 | ], 215 | }; 216 | } 217 | 218 | export async function callTool(request: CallToolRequest) { 219 | const { name, arguments: args } = request.params; 220 | 221 | try { 222 | if (name === 'request') { 223 | const { type, url, headers, body } = RequestSchema.parse(args); 224 | const response = await makeRequest(url, type, headers || {}, body || {}); 225 | return { 226 | content: [ 227 | { 228 | type: 'text', 229 | text: JSON.stringify({ 230 | response, 231 | }), 232 | }, 233 | ], 234 | }; 235 | } else if (name === 'api-documentation') { 236 | return { 237 | content: [ 238 | { 239 | type: 'text', 240 | text: JSON.stringify(listResources()), 241 | }, 242 | ], 243 | }; 244 | } else if (name === 'api-path-schema') { 245 | return { 246 | content: [ 247 | { 248 | type: 'text', 249 | text: getAPIRequestSpec(request.params.arguments?.path as string), 250 | }, 251 | ], 252 | }; 253 | } else { 254 | throw new Error(`Unknown tool: ${name}`); 255 | } 256 | } catch (error) { 257 | if (error instanceof z.ZodError) { 258 | throw new Error( 259 | `Invalid arguments: ${error.errors 260 | .map(e => `${e.path.join('.')}: ${e.message}`) 261 | .join(', ')}` 262 | ); 263 | } 264 | throw error; 265 | } 266 | } 267 | ```