#
tokens: 16585/50000 21/21 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── scripts
│   │   ├── update-mcp-version.sh
│   │   ├── validate-build.sh
│   │   ├── validate-no-version-change.sh
│   │   └── validate-version-increment.sh
│   └── workflows
│       ├── on-pr-feature-branch.yml
│       └── on-push-main.yml
├── .gitignore
├── CHANGELOG.md
├── changeset-status.json
├── CONTRIBUTING.md
├── Dockerfile
├── glama.json
├── index.js
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── PRIVACY.md
├── README.md
└── smithery.yaml
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
node_modules
.DS_STORE
notes.txt

```

--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------

```markdown
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
<div align="center">
    <h1 align="center">CoinMarketCap MCP Server</h1>
    <p align=center>
        <a href="https://badge.fury.io/js/@shinzolabs%2Fcoinmarketcap-mcp"><img src="https://badge.fury.io/js/@shinzolabs%2Fcoinmarketcap-mcp.svg" alt="NPM Version"></a>
        <a href="https://github.com/shinzo-labs/coinmarketcap-mcp/stargazers"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.github.com%2Frepos%2Fshinzo-labs%2Fcoinmarketcap-mcp%2Fstargazers&query=%24.length&logo=github&label=stars&color=e3b341" alt="Stars"></a>
        <a href="https://github.com/shinzo-labs/coinmarketcap-mcp/forks"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.github.com%2Frepos%2Fshinzo-labs%2Fcoinmarketcap-mcp%2Fforks&query=%24.length&logo=github&label=forks&color=8957e5" alt="Forks"></a>
        <a href="https://smithery.ai/server/@shinzo-labs/coinmarketcap-mcp"><img src="https://smithery.ai/badge/@shinzo-labs/coinmarketcap-mcp" alt="Smithery Calls"></a>
        <a href="https://www.npmjs.com/package/@shinzolabs/coinmarketcap-mcp"><img src="https://img.shields.io/npm/dm/%40shinzolabs%2Fcoinmarketcap-mcp" alt="NPM Downloads"></a>
</div>

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server implementation for the [CoinMarketCap](https://coinmarketcap.com/) API, providing a standardized interface for accessing cryptocurrency market data, exchange information, and other blockchain-related metrics.

<p align="center"><img height="512" src=https://github.com/user-attachments/assets/4b2d5f1c-2868-4b9b-8be3-b8ad02b4d331></p>

## Features

- Complete coverage of the CoinMarketCap API
- Fetch data on the latest crypto trends, market movements, and global market metrics
- Access to detailed OHLCV data with Standard subscription or higher
- Type-safe parameter validation with [Zod](https://zod.dev/)

## Prerequisites

If you don't have an API key, first sign up to receive a free `Basic` key [here](https://pro.coinmarketcap.com/signup/?plan=0).

## Client Configuration

There are several options to configure your MCP client with the server. For hosted/remote server setup, use Smithery's CLI with a [Smithery API Key](https://smithery.ai/docs/registry#registry-api). For local installation, use `npx` or build from source. Each of these options is explained below.

### Smithery Remote Server (Recommended)

To add a remote server to your MCP client `config.json`, run the following command from [Smithery CLI](https://github.com/smithery-ai/cli?tab=readme-ov-file#smithery-cli--):

```bash
npx -y @smithery/cli install @shinzo-labs/coinmarketcap-mcp
```

Enter your `COINMARKETCAP_API_KEY` and `SUBSCRIPTION_LEVEL` (see options below) when prompted.

### Smithery SDK

If you are developing your own agent application, you can use the boilerplate code [here](https://smithery.ai/server/@shinzo-labs/coinmarketcap-mcp/api).

### NPX Local Install

To install the server locally with `npx`, add the following to your MCP client `config.json`:
```javascript
{
  "mcpServers": {
    "coinmarketcap": {
      "command": "npx",
      "args": [
        "@shinzolabs/coinmarketcap-mcp"
      ],
      "env": {
        "COINMARKETCAP_API_KEY": "your-key-here",
        "SUBSCRIPTION_LEVEL": "Basic" // See options below
      }
    }
  }
}
```

### Build from Source

1. Download the repo:
```bash
git clone https://github.com/shinzo-labs/coinmarketcap-mcp.git
```

2. Install packages (inside cloned repo):
```bash
pnpm i
```

3. Add the following to your MCP client `config.json`:
```javascript
{
  "mcpServers": {
    "coinmarketcap": {
      "command": "node",
      "args": [
        "/path/to/coinmarketcap-mcp/index.js"
      ],
      "env": {
        "COINMARKETCAP_API_KEY": "your-key-here",
        "SUBSCRIPTION_LEVEL": "Basic" // See options below
      }
    }
  }
}
```

## Config Variables

| Variable                | Description                                                                 | Required? | Default |
|-------------------------|-----------------------------------------------------------------------------|-----------|---------|
| `COINMARKETCAP_API_KEY` | API Key from CoinMarketCap.com                                              | Yes       |         |
| `SUBSCRIPTION_LEVEL`    | `Basic`, `Hobbyist`, `Startup`, `Standard`, `Professional`, or `Enterprise` | No        | `Basic` |
| `PORT`                  | Port for Streamable HTTP transport method                                   | No        | `3000`  |
| `TELEMETRY_ENABLED`     | Enable telemetry                                                            | No        | `true`  |

## Supported Tools

### Subscription Level: Basic (and above)

#### Cryptocurrency
- `cryptoCurrencyMap`: Get mapping of all cryptocurrencies
- `getCryptoMetadata`: Get metadata for one or more cryptocurrencies
- `allCryptocurrencyListings`: Get latest market quote for 1-5000 cryptocurrencies
- `cryptoQuotesLatest`: Get latest market quote for 1 or more cryptocurrencies
- `cryptoCategories`: Get list of all cryptocurrency categories
- `cryptoCategory`: Get metadata about a cryptocurrency category

#### Exchange
- `exchangeMap`: Get mapping of all exchanges
- `exchangeInfo`: Get metadata for one or more exchanges
- `exchangeAssets`: Get list of all assets available on an exchange

#### DEX
- `dexInfo`: Get metadata for one or more decentralised exchanges
- `dexListingsLatest`: Get latest market data for all DEXes
- `dexNetworksList`: Get list of all networks with unique IDs
- `dexSpotPairsLatest`: Get latest market data for all active DEX spot pairs
- `dexPairsQuotesLatest`: Get latest market quotes for spot pairs
- `dexPairsOhlcvLatest`: Get latest OHLCV data for spot pairs
- `dexPairsOhlcvHistorical`: Get historical OHLCV data for spot pairs
- `dexPairsTradeLatest`: Get latest trades for spot pairs

#### Global Metrics
- `globalMetricsLatest`: Get latest global cryptocurrency metrics

#### Index
- `cmc100IndexLatest`: Get latest CoinMarketCap 100 Index value and constituents
- `cmc100IndexHistorical`: Get historical CoinMarketCap 100 Index values

#### Tools
- `priceConversion`: Convert an amount of one cryptocurrency or fiat currency into another
- `getPostmanCollection`: Get Postman collection for the API

#### Other
- `fiatMap`: Get mapping of all fiat currencies
- `keyInfo`: Get API key usage and status
- `fearAndGreedLatest`: Get latest Fear & Greed Index
- `fearAndGreedHistorical`: Get historical Fear & Greed Index values

### Subscription Level: Hobbyist (and above)

#### Cryptocurrency
- `cryptoAirdrops`: Get list of all cryptocurrency airdrops
- `cryptoAirdrop`: Get metadata about a specific airdrop
- `historicalCryptocurrencyListings`: Get historical market quotes for any cryptocurrency
- `cryptoQuotesHistorical`: Get historical market quotes for any cryptocurrency
- `cryptoQuotesHistoricalV3`: Get historical market quotes with advanced time-based intervals

#### Exchange
- `exchangeQuotesHistorical`: Get historical quotes for any exchange

#### Global Metrics
- `globalMetricsHistorical`: Get historical global cryptocurrency metrics

### Subscription Level: Startup (and above)

#### Cryptocurrency
- `newCryptocurrencyListings`: Get list of most recently added cryptocurrencies
- `cryptoTrendingGainersLosers`: Get biggest gainers and losers in a given time period
- `cryptoTrendingLatest`: Get top cryptocurrencies by search volume
- `cryptoTrendingMostVisited`: Get most visited cryptocurrencies
- `cryptoOhlcvLatest`: Get latest OHLCV market data for any cryptocurrency
- `cryptoOhlcvHistorical`: Get historical OHLCV market data for any cryptocurrency
- `cryptoPricePerformanceStatsLatest`: Get price performance statistics for any cryptocurrency

### Subscription Level: Standard (and above)

#### Cryptocurrency
- `cryptoMarketPairsLatest`: Get latest market pairs for any cryptocurrency

#### Exchange
- `exchangeListingsLatest`: Get latest market data for all exchanges
- `exchangeMarketPairsLatest`: Get latest market pairs for any exchange
- `exchangeQuotesLatest`: Get latest market quotes for one or more exchanges

#### Content
- `contentLatest`: Get latest cryptocurrency news and content
- `contentPostsTop`: Get top cryptocurrency posts
- `contentPostsLatest`: Get latest cryptocurrency posts
- `contentPostsComments`: Get comments for a specific post

#### Community
- `communityTrendingTopic`: Get trending topics in the cryptocurrency community
- `communityTrendingToken`: Get trending tokens in the cryptocurrency community

### Subscription Level: Enterprise (and above)

#### Blockchain
- `blockchainStatisticsLatest`: Get latest statistics for one or more blockchains

## Contributing

Contributions are welcomed and encouraged! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on issues, contributions, and contact information.

## Data Collection and Privacy

Shinzo Labs collects limited anonymous telemetry from this server to help improve our products and services. No personally identifiable information is collected as part of this process. Please review the [Privacy Policy](./PRIVACY.md) for more details on the types of data collected and how to opt-out of this telemetry.

## License

MIT

```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
# Contributing to CoinMarketCap MCP

## Overview

Contributions to this codebase are welcomed and appreciated. We encourage novice and professional developers alike to help improve the quality of our software, which is offered as a benefit to the open source community.

## Guidelines

### Issues

If you would like to raise any issues, please do so in the [Issues](https://github.com/shinzo-labs/coinmarketcap-mcp/issues) section and a core contributor will respond in a timely manner. Issue threads may be closed if there are no additional comments added within 7 days of the last update on the thread.

### Code Contributions

If you would like to contribute code to the codebase, please contact [email protected] to discuss what feature you would like to add, or what features/bugs you may be able to own from the queue. The steps to then contribute would be:
1. Create a fork version of the repo.
2. Open a branch with a name prefixed with `feat/`, `fix/`, or `chore/`.
3. Implement the desired changes.
4. Run `npx @changesets/cli` to add a `changeset` for each distinct change in your feature. Read the [changeset README](.changeset/README.md) for more info.
4. Open a Pull Request from your forked repo back to the main repo. Tag one of the core contributors as a reviewer.
5. Once the core contributor has reviewed the code and all comments have been resolved, the PR will be approved and merged into the `main` branch.
6. Merged changes will be added to a versioned package release on a regular schedule.

## Contact

If you have any questions or comments about the guidelines here or anything else about the software, please contact [email protected] or open an issue.

```

--------------------------------------------------------------------------------
/glama.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://glama.ai/mcp/schemas/server.json",
  "maintainers": [
    "austinborn"
  ]
}

```

--------------------------------------------------------------------------------
/.github/scripts/validate-build.sh:
--------------------------------------------------------------------------------

```bash
#! /bin/sh

pnpm i --frozen-lockfile
if [ $? -ne 0 ]; then
  echo 'Error: Lockfile is not up to date. Please run `pnpm install` and commit the updated lockfile.'
  exit 1
fi

```

--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": []
}

```

--------------------------------------------------------------------------------
/changeset-status.json:
--------------------------------------------------------------------------------

```json
{
  "changesets": [
    {
      "releases": [
        {
          "name": "@shinzolabs/coinmarketcap-mcp",
          "type": "minor"
        }
      ],
      "summary": "Update and pin dependencies",
      "id": "five-planets-do"
    }
  ],
  "releases": [
    {
      "name": "@shinzolabs/coinmarketcap-mcp",
      "type": "minor",
      "oldVersion": "1.3.7",
      "changesets": [
        "five-planets-do"
      ],
      "newVersion": "1.4.0"
    }
  ]
}
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM node:lts-alpine

# Create app directory in container
WORKDIR /app

# Copy package.json and package-lock.json if available
COPY package*.json ./

# Install dependencies (ignoring scripts to skip any prepare hooks)
RUN npm install --ignore-scripts

# Copy the rest of the application code
COPY . .

# Expose port if needed (not required for MCP using stdio)

# Command to run the application
CMD ["npm", "start"]

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------

```markdown
---
name: Feature request
about: Suggest a feature for this codebase
title: ''
labels: ''
assignees: austinborn

---

# Overview
<!--- Give a brief overview of the feature you would like to see in the codebase. -->

# Purpose
<!--- Please explain why the feature would be a meaningful addition and isn't captured by other features already. -->

# Example Use Case(s)
<!--- Provide at least one scenario where this feature may add value in practice. -->

# Requirements
- [ ] The feature is not covered in any other open issues
- [ ] The feature pertains primarily to this codebase

```

--------------------------------------------------------------------------------
/.github/scripts/update-mcp-version.sh:
--------------------------------------------------------------------------------

```bash
#! /bin/sh

version=$(jq -r .version package.json)

git fetch origin changeset-release/main:changeset-release/main
git checkout changeset-release/main

sed -i "s/version: \"[0-9]\+\.[0-9]\+\.[0-9]\+\"/version: \"$version\"/g" $PATH_TO_FILE
if git diff --quiet $PATH_TO_FILE; then
  echo "No changes to MCP version, skipping commit"
else
  git config --global user.email "github-actions[bot]@users.noreply.github.com"
  git config --global user.name "github-actions[bot]"
  git add $PATH_TO_FILE
  git commit -m "Update MCP version to $version"
  git push origin changeset-release/main
fi

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml

startCommand:
  type: http
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    type: object
    required:
      - COINMARKETCAP_API_KEY
    properties:
      COINMARKETCAP_API_KEY:
        type: string
        description: "COINMARKETCAP_API_KEY - Your API key"
      SUBSCRIPTION_LEVEL:
        type: string
        default: Basic
        description: "SUBSCRIPTION_LEVEL - One of: Basic, Hobbyist, Startup, Standard,
          Professional, or Enterprise (defaults to Basic)"
  exampleConfig:
    COINMARKETCAP_API_KEY: test-key-123
    SUBSCRIPTION_LEVEL: Basic

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@shinzolabs/coinmarketcap-mcp",
  "version": "1.4.5",
  "description": "A complete MCP for the CoinMarketCap API",
  "type": "module",
  "main": "index.js",
  "bin": {
    "coinmarketcap-mcp": "./index.js"
  },
  "scripts": {
    "start": "node ./index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "1.16.0",
    "@shinzolabs/instrumentation-mcp": "^1.0.8",
    "@smithery/sdk": "1.4.3",
    "zod": "3.22.4"
  },
  "devDependencies": {
    "@changesets/cli": "2.29.4"
  },
  "keywords": [
    "crypto",
    "mcp",
    "coinmarketcap"
  ],
  "author": "Austin Born ([email protected])",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/shinzo-labs/coinmarketcap-mcp"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------

```markdown
---
name: Bug report
about: Write a report about a problem with functionality
title: ''
labels: ''
assignees: austinborn

---

# Overview
<!--- Give a brief overview of the bug or issue. -->

# Steps to Reproduce
<!--- Explain how testers can reproduce the issue. Be as specific as reasonably possible about your local environment and relevant setup configuration. -->

# Expected Behavior
<!--- Did you expect a particular functionality different from what was observed? -->

# Additional Context
<!--- Please provide any other details that can aid in the resolution of the issue, including any screenshots, sample code snippets, or sequences of commands to test. -->

# Requirements
- [ ] The bug is not covered in any other open issues
- [ ] The bug pertains primarily to this codebase
- [ ] The submitter has taken reasonable steps to confirm whether the bug is a result of the code itself and not user error.

```

--------------------------------------------------------------------------------
/.github/scripts/validate-version-increment.sh:
--------------------------------------------------------------------------------

```bash
#! /bin/sh

BASE_BRANCH=${GITHUB_BASE_REF:-main}
git fetch origin $BASE_BRANCH

pkg_version=$(jq -r .version package.json)
base_version=$(git show origin/$BASE_BRANCH:package.json | jq -r .version)

IFS='.' read -r pkg_major pkg_minor pkg_patch <<< "$pkg_version"
IFS='.' read -r base_major base_minor base_patch <<< "$base_version"

inc_count=0

if [ "$pkg_major" -eq $((base_major + 1)) ] && [ "$pkg_minor" -eq 0 ] && [ "$pkg_patch" -eq 0 ]; then
  inc_count=1
elif [ "$pkg_major" -eq "$base_major" ] && [ "$pkg_minor" -eq $((base_minor + 1)) ] && [ "$pkg_patch" -eq 0 ]; then
  inc_count=1
elif [ "$pkg_major" -eq "$base_major" ] && [ "$pkg_minor" -eq "$base_minor" ] && [ "$pkg_patch" -eq $((base_patch + 1)) ]; then
  inc_count=1
fi

if [ "$inc_count" -ne 1 ]; then
  echo "Error: Version must increment one of major, minor, or patch by 1 (and reset lower segments if major/minor is incremented)."
  echo "Base branch version: $base_version"
  echo "PR version: $pkg_version"
  exit 1
fi

```

--------------------------------------------------------------------------------
/.github/scripts/validate-no-version-change.sh:
--------------------------------------------------------------------------------

```bash
#! /bin/sh

BASE_BRANCH=${GITHUB_BASE_REF:-main}
git fetch origin $BASE_BRANCH

pkg_version=$(jq -r .version package.json)
base_version=$(git show origin/$BASE_BRANCH:package.json | jq -r .version)

# Check if version has changed from base branch
if [ "$pkg_version" != "$base_version" ]; then
  echo "Error: Version should not change from base branch."
  echo "Base branch version: $base_version"
  echo "PR version: $pkg_version"
  exit 1
fi

# Check if package version matches the version in FILE_PATH
if [ -n "$FILE_PATH" ] && [ -f "$FILE_PATH" ]; then
  file_content=$(cat "$FILE_PATH")
  file_version=$(echo "$file_content" | grep -o 'version: "[0-9]\+\.[0-9]\+\.[0-9]\+"' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+')
  
  if [ "$pkg_version" != "$file_version" ]; then
    echo "Error: Version in package.json ($pkg_version) does not match version in $FILE_PATH ($file_version)"
    exit 1
  fi
else
  echo "Warning: FILE_PATH not set or file does not exist. Skipping file version check."
  exit 1
fi

echo "Version validation passed: $pkg_version"

```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# @shinzolabs/coinmarketcap-mcp

## 1.4.5

### Patch Changes

- 2d635ac: README typo

## 1.4.4

### Patch Changes

- 3b5383f: Patch telemetryEnabled config

## 1.4.3

### Patch Changes

- 26f8fa4: Add Privacy Policy and TELEMETRY_ENABLED config option

## 1.4.2

### Patch Changes

- 3e4839e: Upgrade to [email protected]

## 1.4.1

### Patch Changes

- ceffe92: Instrument tool calls

## 1.4.0

### Minor Changes

- 4bf1fa5: Update and pin dependencies

## 1.3.7

### Patch Changes

- e01aed7: Update gh workflows to manage release PRs better

## 1.3.6

### Patch Changes

- c11e225: Publish NPM package with pnpm

## 1.3.5

### Patch Changes

- 582e015: Add correct permissions for NPM token

## 1.3.4

### Patch Changes

- b09d521: Use 'npm ci' for npm publish script

## 1.3.3

### Patch Changes

- f4046d2: Make npm-publish.sh executable

## 1.3.2

### Patch Changes

- 76a5912: Update NPM publish script

## 1.3.1

### Patch Changes

- 39d5c10: Add @changesets/cli as dev dep for Release PR creation
- 14a8d0d: Debug release workflow
- 815f99e: Patch create-release-pr
- 2eee98f: Add github workflows for checks and publishing
- 86e6d2f: Ensure MCP version can be updated in same release PRs

```

--------------------------------------------------------------------------------
/.github/workflows/on-pr-feature-branch.yml:
--------------------------------------------------------------------------------

```yaml
name: On PR for Feature Branch

on:
  pull_request:
    branches-ignore:
      - changeset-release/main

env:
  UPSTREAM_BRANCH: origin/${{ github.base_ref }}

concurrency:
  group: pr-${{ github.event.pull_request.number }}-checks
  cancel-in-progress: true

jobs:
  check-changeset:
    name: Check for changeset
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Fetch base branch
        run: git fetch origin main:refs/remotes/origin/main
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      - name: Check for changeset
        run: npx @changesets/cli status --since=origin/main

  validate-no-version-change:
    name: Validate the version has not changed
    runs-on: ['ubuntu-latest']
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Run validation script
        run: .github/scripts/validate-no-version-change.sh
        env:
          FILE_PATH: index.js

  validate-build:
    name: Validate build
    runs-on: ['ubuntu-latest']
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Run validation script
        run: .github/scripts/validate-build.sh

```

--------------------------------------------------------------------------------
/.github/workflows/on-push-main.yml:
--------------------------------------------------------------------------------

```yaml
name: On Push to Main Branch

on:
  push:
    branches:
      - main

concurrency:
  group: deployment
  cancel-in-progress: true

jobs:
  check-version:
    runs-on: ubuntu-latest
    needs: check-changesets
    outputs:
      version_changed: ${{ steps.version-check.outputs.changed }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Check if version changed
        id: version-check
        run: |
          current_version=$(jq -r .version package.json)
          base_version=$(git show HEAD^:package.json | jq -r .version)
          if [ "$current_version" != "$base_version" ]; then
            echo "changed=true" >> $GITHUB_OUTPUT
          else
            echo "changed=false" >> $GITHUB_OUTPUT
          fi

  check-changesets:
    runs-on: ubuntu-latest
    outputs:
      hasChangesets: ${{ steps.changeset-check.outputs.hasChangesets }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Check for changesets
        id: changeset-check
        run: |
          if [ -n "$(ls .changeset/*.md 2>/dev/null | grep -v 'README.md' | grep -v 'config.json')" ]; then
            echo "hasChangesets=true" >> $GITHUB_OUTPUT
          else
            echo "hasChangesets=false" >> $GITHUB_OUTPUT
          fi

  create-release-pr:
    name: Create or update Release PR
    runs-on: ubuntu-latest
    needs: [check-version, check-changesets]
    if: needs.check-changesets.outputs.hasChangesets == 'true' && needs.check-version.outputs.version_changed == 'false'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      - name: Get next version from changesets
        id: get-next-version
        run: |
          npx changeset status --output changeset-status.json
          version=$(jq -r '.releases[0].newVersion' changeset-status.json)
          echo "next_version=$version" >> $GITHUB_OUTPUT
      - name: Create or update Release PR
        uses: changesets/action@v1
        with:
          publish: false
          title: "Release v${{ steps.get-next-version.outputs.next_version }}"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Update MCP version
        run: .github/scripts/update-mcp-version.sh
        env:
          PATH_TO_FILE: 'index.js'

  gh-release:
    name: GH Release
    runs-on: ubuntu-latest
    needs: check-version
    if: needs.check-version.outputs.version_changed == 'true'
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v4
      - name: Get release version
        id: get-version
        run: |
          echo "result=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
      - name: Get release body
        run: |
          # Get the PR body to use in the GH release body
          gh pr list --search "$(git rev-parse HEAD)" --state merged --json number,body --jq '"This release was merged in PR #" + (.[0].number | tostring) + "\n\n" + (.[0].body | split("# Releases")[1:] | join("# Releases"))' > pr_body.tmp
      - name: Create release
        uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
        with:
          tag_name: v${{ steps.get-version.outputs.result }}
          name: Release v${{ steps.get-version.outputs.result }}
          body_path: pr_body.tmp

  npm-publish:
    name: Publish to npm registry
    runs-on: ubuntu-latest
    needs: check-version
    if: needs.check-version.outputs.version_changed == 'true'
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Setup Node.js and npm auth
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          registry-url: 'https://registry.npmjs.org'
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      - name: Publish
        run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

```

--------------------------------------------------------------------------------
/PRIVACY.md:
--------------------------------------------------------------------------------

```markdown
# Privacy Policy

Effective Date: July 24, 2025

This privacy policy explains how Shinzo Labs collects telemetry data using the [Shinzo platform](https://github.com/shinzo-labs/shinzo-ts) to improve its products and services.

## Data Collection

Shinzo Labs collects limited telemetry data using instrumentation from the [Shinzo platform](https://github.com/shinzo-labs/shinzo-ts) to help us understand usage patterns, detect errors, and improve the tool. Our telemetry pipeline includes steps to anonymize data and remove any personally identifiable information.

### What We Collect

To better understand the performance of our product in different environments, we collect trace data on operations with limited attributes, and metrics for operation counts. This includes, but is not limited to, the following data:
- **Span Name**: Name of the operation called
- **Span ID**: Random identifier for the span (ex. `tools/call send_message`)
- **Timestamp**: The time in which the operation started
- **Duration**: The total duration of the operation
- **Service Name**: Name of the server called
- **Service Version**: Specific version of the server
- **Service Instance ID**: Random identifier for this process instance
- **Status Code**: Error code (if any)
- **Host Arch**: Hardware architecture (ex. `arm64`, `amd64`)
- **Method Name**: Capability-specific name (ex. `tools/call`)
- **Request ID**: Random identifier for this specific operation or request
- **Session ID**: Random identifier for the overall running session, so correlated requests in a single session
- **Tool Name**: Name of the tool called (if any, ex. `send_message`)
- **OS Type**: `darwin`, `linux`, `windows`, or `other`
- **OS Version**: Specific OS version
- **Telemetry SDK Language**: ex. `nodejs`
- **Telemetry SDK Name**: ex. `opentelemetry`
- **Telemetry SDK Version**: Specific telemetry SDK version
- **Error Code**: Whether an operation was successful or not
- **Error Message**: Short description of the error message

### What We DO NOT Collect

We explicitly DO NOT collect:
- **Personal information (PII)**: Any personally identifiable information
- **IP addresses and Ports**: Your network information
- **Usernames**: System or account usernames
- **Arguments for operations**: Any of the data entered by the user as arguments or parameters for operations

## Data Usage

The collected data is used for:

- Understanding how the product is used
- Identifying common errors or issues
- Measuring feature adoption and performance
- Guiding development priorities
- Improving overall user experience

## Privacy Protection

We take your privacy seriously:

- All IDs are randomly-generated UUIDs, not derived from your machine hardware ID
- All data is sent securely via HTTPS to collectors controlled by Shinzo Labs or affiliate third parties (current infrastructure may be shared upon request)
- Data is only used in aggregate form for statistical analysis
- We implement robust sanitization of all data to ensure any potential PII is never included in telemetry
- We maintain data minimization principles - only collecting essential data for explicit purposes
- All telemetry is processed in a way that does not connect it to specific individuals

## Data Retention

Telemetry data is retained for a period of 12 months, after which it is automatically deleted from Shinzo Labs' collector infrastructure

## User Control

Telemetry is enabled by default, but you can disable it at any time by setting `TELEMETRY_ENABLED: "false"` in the environment config. In doing so, no telemetry data will otherwise be shared with Shinzo Labs without the user's explicit permission.

## Legal Basis

We collect this data based on our legitimate interest (GDPR Article 6(1)(f)) to improve our software. Since we use randomly-generated UUIDs rather than personal identifiers, the privacy impact is minimal while allowing us to gather important usage data.

## Changes to This Policy

We may update this privacy policy from time to time. Any changes will be posted in this document and in release notes.

## Contact

If you have any questions about this privacy policy or our data practices, please contact [Shinzo Labs](mailto:[email protected]).

## Acknowledgements

This policy is adapted from the [Privacy Policy for DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/PRIVACY.md), although Shinzo Labs has no affiliation with the team behind DesktopCommanderMCP.

```

--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { createStatefulServer } from "@smithery/sdk/server/stateful.js"
import { instrumentServer } from "@shinzolabs/instrumentation-mcp"
import { z } from "zod"

// Subscription plan levels in order of increasing access
const SUBSCRIPTION_LEVELS = {
  "Basic": 0,
  "Hobbyist": 1,
  "Startup": 2,
  "Standard": 3,
  "Professional": 4,
  "Enterprise": 5
}

// Enhanced response formatter with status code support
function formatErrorResponse(message, status = 403) {
  return {
    content: [{
      type: "text",
      text: JSON.stringify({ error: message, status })
    }]
  }
}

// Helper function to format successful API responses
function formatResponse(data) {
  return {
    content: [{
      type: "text",
      text: JSON.stringify(data)
    }]
  }
}

// Enhanced API request wrapper with error handling
async function makeApiRequestWithErrorHandling(apiKey, endpoint, params = {}) {
  try {
    const data = await makeApiRequest(apiKey, endpoint, params)
    return formatResponse(data)
  } catch (error) {
    return formatErrorResponse(`Error fetching data from CoinMarketCap: ${error.message}`, 500)
  }
}

// Helper function for making API requests to CoinMarketCap
async function makeApiRequest(apiKey, endpoint, params = {}) {
  const queryParams = new URLSearchParams()
  Object.entries(params).forEach(([key, value]) => {
    if (value !== undefined) {
      queryParams.append(key, value.toString())
    }
  })

  const url = `https://pro-api.coinmarketcap.com${endpoint}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`

  const response = await fetch(url, {
    method: 'GET',
    headers: {
      'Accept': 'application/json',
      'X-CMC_PRO_API_KEY': apiKey,
    }
  })

  if (!response.ok) {
    throw new Error(`Error fetching data from CoinMarketCap: ${response.statusText}`)
  }

  return await response.json()
}

// Helper function to check subscription level at runtime
function checkSubscriptionLevel(subscriptionLevel, requiredLevel) {
  const currentLevel = SUBSCRIPTION_LEVELS[subscriptionLevel]
  return currentLevel >= requiredLevel
}

// Wrapper function to handle common endpoint patterns
async function handleEndpoint(apiCall) {
  try {
    return await apiCall()
  } catch (error) {
    return formatErrorResponse(error.message, error.status || 403)
  }
}

function getConfig(config) {
  return {
    apiKey: config?.COINMARKETCAP_API_KEY || process.env.COINMARKETCAP_API_KEY,
    subscriptionLevel: config?.SUBSCRIPTION_LEVEL || process.env.SUBSCRIPTION_LEVEL || 'Basic',
    telemetryEnabled: config?.TELEMETRY_ENABLED || process.env.TELEMETRY_ENABLED || "true"
  }
}

const serverInfo = {
  name: "CoinMarketCap-MCP",
  version: "1.4.5",
  description: "A complete MCP for the CoinMarketCap API"
}

function createServer({ config }) {
  const server = new McpServer(serverInfo)

  const { apiKey, subscriptionLevel, telemetryEnabled } = getConfig(config)

  if (telemetryEnabled !== "false") {
    const telemetry = instrumentServer(server, {
      serverName: serverInfo.name,
      serverVersion: serverInfo.version,
      exporterEndpoint: "https://api.otel.shinzo.tech/v1",
    })
  }

  /*
  * BASIC SUBSCRIPTION ENDPOINTS
  */
  if (checkSubscriptionLevel(subscriptionLevel, SUBSCRIPTION_LEVELS.Basic)) {
    // /cryptocurrency/categories
    server.tool("cryptoCategories",
      "Returns information about all coin categories available on CoinMarketCap.",
      {
        start: z.number().optional(),
        limit: z.number().optional(),
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/categories', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/category
    server.tool("cryptoCategory",
      "Returns information about a single coin category on CoinMarketCap.",
      {
        id: z.string(),
        start: z.number().optional(),
        limit: z.number().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/category', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/map
    server.tool("cryptoCurrencyMap",
      "Returns a mapping of all cryptocurrencies to unique CoinMarketCap IDs.",
      {
        listing_status: z.string().optional(),
        start: z.number().optional(),
        limit: z.number().optional(),
        sort: z.string().optional(),
        symbol: z.string().optional(),
        aux: z.string().optional()
      },
      async ({ listing_status = 'active', start = 1, limit = 100, sort = 'id', symbol, aux }) => {
        return handleEndpoint(async () => {
          return await makeApiRequestWithErrorHandling(apiKey, '/v1/cryptocurrency/map', {
            listing_status,
            start,
            limit,
            sort,
            symbol,
            aux
          })
        })
      }
    )

    // /cryptocurrency/info
    server.tool("getCryptoMetadata",
      "Returns all static metadata for one or more cryptocurrencies including logo, description, and website URLs.",
      {
        symbol: z.string().optional(),
        id: z.string().optional(),
        slug: z.string().optional(),
        address: z.string().optional(),
        aux: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async ({ symbol, id, slug, address, aux, skip_invalid }) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/info', {
            symbol,
            id,
            slug,
            address,
            aux,
            skip_invalid
          })
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/listings/latest
    server.tool("allCryptocurrencyListings",
      "Returns a paginated list of all active cryptocurrencies with latest market data.",
      {
        start: z.number().optional(),
        limit: z.number().min(1).max(5000).optional(),
        price_min: z.number().optional(),
        price_max: z.number().optional(),
        market_cap_min: z.number().optional(),
        market_cap_max: z.number().optional(),
        volume_24h_min: z.number().optional(),
        volume_24h_max: z.number().optional(),
        circulating_supply_min: z.number().optional(),
        circulating_supply_max: z.number().optional(),
        percent_change_24h_min: z.number().optional(),
        percent_change_24h_max: z.number().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        sort: z.enum(['market_cap', 'name', 'symbol', 'date_added', 'price', 'circulating_supply', 'total_supply', 'max_supply', 'num_market_pairs', 'volume_24h', 'percent_change_1h', 'percent_change_24h', 'percent_change_7d']).optional(),
        sort_dir: z.enum(['asc', 'desc']).optional(),
        cryptocurrency_type: z.string().optional(),
        tag: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/listings/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/quotes/latest
    server.tool("cryptoQuotesLatest",
      "Returns the latest market quote for one or more cryptocurrencies.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/quotes/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/listings/info
    server.tool("dexInfo",
      "Returns all static metadata for one or more decentralised exchanges.",
      {
        id: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/listings/info', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/listings/quotes
    server.tool("dexListingsLatest",
      "Returns a paginated list of all decentralised cryptocurrency exchanges including the latest aggregate market data.",
      {
        start: z.string().optional(),
        limit: z.string().optional(),
        sort: z.enum(['name', 'volume_24h', 'market_share', 'num_markets']).optional(),
        sort_dir: z.enum(['desc', 'asc']).optional(),
        type: z.enum(['all', 'orderbook', 'swap', 'aggregator']).optional(),
        aux: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/listings/quotes', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/networks/list
    server.tool("dexNetworksList",
      "Returns a list of all networks to unique CoinMarketCap ids.",
      {
        start: z.string().optional(),
        limit: z.string().optional(),
        sort: z.enum(['id', 'name']).optional(),
        sort_dir: z.enum(['desc', 'asc']).optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/networks/list', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/spot-pairs/latest
    server.tool("dexSpotPairsLatest",
      "Returns a paginated list of all active dex spot pairs with latest market data.",
      {
        network_id: z.string().optional(),
        network_slug: z.string().optional(),
        dex_id: z.string().optional(),
        dex_slug: z.string().optional(),
        base_asset_id: z.string().optional(),
        base_asset_symbol: z.string().optional(),
        base_asset_contract_address: z.string().optional(),
        base_asset_ucid: z.string().optional(),
        quote_asset_id: z.string().optional(),
        quote_asset_symbol: z.string().optional(),
        quote_asset_contract_address: z.string().optional(),
        quote_asset_ucid: z.string().optional(),
        scroll_id: z.string().optional(),
        limit: z.string().optional(),
        liquidity_min: z.string().optional(),
        liquidity_max: z.string().optional(),
        volume_24h_min: z.string().optional(),
        volume_24h_max: z.string().optional(),
        no_of_transactions_24h_min: z.string().optional(),
        no_of_transactions_24h_max: z.string().optional(),
        percent_change_24h_min: z.string().optional(),
        percent_change_24h_max: z.string().optional(),
        sort: z.enum(['name', 'date_added', 'price', 'volume_24h', 'percent_change_1h', 'percent_change_24h', 'liquidity', 'fully_diluted_value', 'no_of_transactions_24h']).optional(),
        sort_dir: z.enum(['desc', 'asc']).optional(),
        aux: z.string().optional(),
        reverse_order: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/spot-pairs/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/pairs/quotes/latest
    server.tool("dexPairsQuotesLatest",
      "Returns the latest market quote for 1 or more spot pairs.",
      {
        contract_address: z.string().optional(),
        network_id: z.string().optional(),
        network_slug: z.string().optional(),
        aux: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.string().optional(),
        reverse_order: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/pairs/quotes/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/pairs/ohlcv/latest
    server.tool("dexPairsOhlcvLatest",
      "Returns the latest OHLCV market values for one or more spot pairs for the current UTC day.",
      {
        contract_address: z.string().optional(),
        network_id: z.string().optional(),
        network_slug: z.string().optional(),
        aux: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.string().optional(),
        reverse_order: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/pairs/ohlcv/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/pairs/ohlcv/historical
    server.tool("dexPairsOhlcvHistorical",
      "Returns historical OHLCV data along with market cap for any spot pairs using time interval parameters.",
      {
        contract_address: z.string().optional(),
        network_id: z.string().optional(),
        network_slug: z.string().optional(),
        time_period: z.enum(['daily', 'hourly', '1m', '5m', '15m', '30m', '4h', '8h', '12h', 'weekly', 'monthly']).optional(),
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.string().optional(),
        interval: z.enum(['1m', '5m', '15m', '30m', '1h', '4h', '8h', '12h', 'daily', 'weekly', 'monthly']).optional(),
        aux: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.string().optional(),
        reverse_order: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/pairs/ohlcv/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /v4/dex/pairs/trade/latest
    server.tool("dexPairsTradeLatest",
      "Returns up to the latest 100 trades for 1 spot pair.",
      {
        contract_address: z.string().optional(),
        network_id: z.string().optional(),
        network_slug: z.string().optional(),
        aux: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.string().optional(),
        reverse_order: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v4/dex/pairs/trade/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/assets
    server.tool("exchangeAssets",
      "Returns the assets/token holdings of an exchange.",
      {
        id: z.string().optional(),
        slug: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/assets', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/info
    server.tool("exchangeInfo",
      "Returns metadata for one or more exchanges.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/info', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/map
    server.tool("exchangeMap",
      "Returns a mapping of all exchanges to unique CoinMarketCap IDs.",
      {
        listing_status: z.string().optional(),
        slug: z.string().optional(),
        start: z.number().optional(),
        limit: z.number().optional(),
        sort: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/map', params)
          return formatResponse(data)
        })
      }
    )

    // /global-metrics/quotes/latest
    server.tool("globalMetricsLatest",
      "Returns the latest global cryptocurrency market metrics.",
      {
        convert: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/global-metrics/quotes/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /index/quotes/historical
    server.tool("cmc100IndexHistorical",
      "Returns an interval of historic CoinMarketCap 100 Index values based on the interval parameter.",
      {
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.string().optional(),
        interval: z.enum(['5m', '15m', 'daily']).optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v3/index/cmc100-historical', params)
          return formatResponse(data)
        })
      }
    )

    // /index/quotes/latest
    server.tool("cmc100IndexLatest",
      "Returns the lastest CoinMarketCap 100 Index value, constituents, and constituent weights.",
      {},
      async () => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v3/index/cmc100-latest')
          return formatResponse(data)
        })
      }
    )

    // /fear-and-greed/latest
    server.tool("fearAndGreedLatest",
      "Returns the latest CMC Crypto Fear and Greed Index value.",
      {},
      async () => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v3/fear-and-greed/latest')
          return formatResponse(data)
        })
      }
    )

    // /fear-and-greed/historical
    server.tool("fearAndGreedHistorical",
      "Returns historical CMC Crypto Fear and Greed Index values.",
      {
        start: z.number().min(1).optional(),
        limit: z.number().min(1).max(500).optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v3/fear-and-greed/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /fiat/map
    server.tool("fiatMap",
      "Returns a mapping of all supported fiat currencies to unique CoinMarketCap IDs.",
      {
        start: z.number().optional(),
        limit: z.number().optional(),
        sort: z.string().optional(),
        include_metals: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/fiat/map', params)
          return formatResponse(data)
        })
      }
    )

    // /tools/postman
    server.tool("getPostmanCollection",
      "Returns a Postman collection for the CoinMarketCap API.",
      {},
      async () => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/tools/postman')
          return formatResponse(data)
        })
      }
    )

    // /tools/price-conversion
    server.tool("priceConversion",
      "Convert an amount of one cryptocurrency or fiat currency into one or more different currencies.",
      {
        amount: z.number(),
        id: z.string().optional(),
        symbol: z.string().optional(),
        time: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/tools/price-conversion', params)
          return formatResponse(data)
        })
      }
    )

    // /key/info
    server.tool("keyInfo",
      "Returns API key details and usage stats.",
      {},
      async () => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/key/info')
          return formatResponse(data)
        })
      }
    )
  }

  /*
  * HOBBYIST SUBSCRIPTION ENDPOINTS
  */
  if (checkSubscriptionLevel(subscriptionLevel, SUBSCRIPTION_LEVELS.Hobbyist)) {
    // /cryptocurrency/airdrop
    server.tool("cryptoAirdrop",
      "Returns information about a single airdrop on CoinMarketCap.",
      {
        id: z.string()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/airdrop', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/airdrops
    server.tool("cryptoAirdrops",
      "Returns a list of past, present, or future airdrops on CoinMarketCap.",
      {
        start: z.number().optional(),
        limit: z.number().optional(),
        status: z.string().optional(),
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/airdrops', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/listings/historical
    server.tool("historicalCryptocurrencyListings",
      "Returns a ranked and sorted list of all cryptocurrencies for a historical point in time.",
      {
        timestamp: z.string().or(z.number()).optional(),
        start: z.number().optional(),
        limit: z.number().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        sort: z.string().optional(),
        sort_dir: z.string().optional(),
        cryptocurrency_type: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/listings/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/quotes/historical
    server.tool("cryptoQuotesHistorical",
      "Returns an interval of historical market quotes for any cryptocurrency.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.number().optional(),
        interval: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/quotes/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/quotes/historical/v3
    server.tool("cryptoQuotesHistoricalV3",
      "Returns an interval of historic market quotes for any cryptocurrency based on time and interval parameters.",
      {
        id: z.string().optional(),
        symbol: z.string().optional(),
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.number().min(1).max(10000).optional(),
        interval: z.enum([
          '5m', '10m', '15m', '30m', '45m',
          '1h', '2h', '3h', '4h', '6h', '12h', '24h',
          '1d', '2d', '3d', '7d', '14d', '15d', '30d', '60d', '90d', '365d',
          'hourly', 'daily', 'weekly', 'monthly', 'yearly'
        ]).optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v3/cryptocurrency/quotes/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/quotes/historical
    server.tool("exchangeQuotesHistorical",
      "Returns an interval of historic quotes for any exchange based on time and interval parameters.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.number().min(1).max(10000).optional(),
        interval: z.enum([
          '5m', '10m', '15m', '30m', '45m',
          '1h', '2h', '3h', '4h', '6h', '12h', '24h',
          '1d', '2d', '3d', '7d', '14d', '15d', '30d', '60d', '90d', '365d',
          'hourly', 'daily', 'weekly', 'monthly', 'yearly'
        ]).optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/quotes/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /global-metrics/quotes/historical
    server.tool("globalMetricsHistorical",
      "Returns historical global cryptocurrency market metrics.",
      {
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.number().optional(),
        interval: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/global-metrics/quotes/historical', params)
          return formatResponse(data)
        })
      }
    )
  }

  /*
  * STARTUP SUBSCRIPTION ENDPOINTS
  */
  if (checkSubscriptionLevel(subscriptionLevel, SUBSCRIPTION_LEVELS.Startup)) {
    // /cryptocurrency/listings/new
    server.tool("newCryptocurrencyListings",
      "Returns a paginated list of most recently added cryptocurrencies.",
      {
        start: z.number().min(1).optional(),
        limit: z.number().min(1).max(5000).optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        sort_dir: z.enum(['asc', 'desc']).optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/listings/new', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/trending/gainers-losers
    server.tool("cryptoTrendingGainersLosers",
      "Returns the biggest gainers and losers in a given time period.",
      {
        time_period: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/trending/gainers-losers', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/trending/latest
    server.tool("cryptoTrendingLatest",
      "Returns the top cryptocurrencies by search volume in a given time period.",
      {
        time_period: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/trending/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/trending/most-visited
    server.tool("cryptoTrendingMostVisited",
      "Returns the most visited cryptocurrencies on CoinMarketCap in a given time period.",
      {
        time_period: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/cryptocurrency/trending/most-visited', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/ohlcv/historical
    server.tool("cryptoOhlcvHistorical",
      "Returns historical OHLCV market values for one or more cryptocurrencies.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        time_period: z.string().optional(),
        time_start: z.string().optional(),
        time_end: z.string().optional(),
        count: z.number().optional(),
        interval: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/ohlcv/historical', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/ohlcv/latest
    server.tool("cryptoOhlcvLatest",
      "Returns the latest OHLCV (Open, High, Low, Close, Volume) market values for one or more cryptocurrencies.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        skip_invalid: z.boolean().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/ohlcv/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /cryptocurrency/price-performance-stats/latest
    server.tool("cryptoPricePerformanceStatsLatest",
      "Returns price performance statistics for one or more cryptocurrencies including ROI and ATH stats.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        time_period: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/price-performance-stats/latest', params)
          return formatResponse(data)
        })
      }
    )
  }

  /*
  * STANDARD SUBSCRIPTION ENDPOINTS
  */
  if (checkSubscriptionLevel(subscriptionLevel, SUBSCRIPTION_LEVELS.Standard)) {
    // /cryptocurrency/market-pairs/latest
    server.tool("cryptoMarketPairsLatest",
      "Returns all market pairs for the specified cryptocurrency with associated stats.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        start: z.number().optional(),
        limit: z.number().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        matched_id: z.string().optional(),
        matched_symbol: z.string().optional(),
        category: z.string().optional(),
        fee_type: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v2/cryptocurrency/market-pairs/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/listings/latest
    server.tool("exchangeListingsLatest",
      "Returns a paginated list of all exchanges with latest market data.",
      {
        start: z.number().optional(),
        limit: z.number().optional(),
        sort: z.string().optional(),
        sort_dir: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/listings/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/market-pairs/latest
    server.tool("exchangeMarketPairsLatest",
      "Returns all market pairs for the specified exchange with associated stats.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        start: z.number().optional(),
        limit: z.number().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/market-pairs/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /exchange/quotes/latest
    server.tool("exchangeQuotesLatest",
      "Returns the latest market quotes for one or more exchanges.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        convert: z.string().optional(),
        convert_id: z.string().optional(),
        aux: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/exchange/quotes/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /content/latest
    server.tool("contentLatest",
      "Returns latest cryptocurrency news and Alexandria articles.",
      {
        start: z.number().optional(),
        limit: z.number().optional(),
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional(),
        news_type: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/content/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /content/posts/top
    server.tool("contentPostsTop",
      "Returns top cryptocurrency posts.",
      {
        start: z.number().optional(),
        limit: z.number().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/content/posts/top', params)
          return formatResponse(data)
        })
      }
    )

    // /content/posts/latest
    server.tool("contentPostsLatest",
      "Returns latest cryptocurrency posts.",
      {
        start: z.number().optional(),
        limit: z.number().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/content/posts/latest', params)
          return formatResponse(data)
        })
      }
    )

    // /content/posts/comments
    server.tool("contentPostsComments",
      "Returns comments for a specific post.",
      {
        id: z.string(),
        start: z.number().optional(),
        limit: z.number().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/content/posts/comments', params)
          return formatResponse(data)
        })
      }
    )

    // /community/trending/topic
    server.tool("communityTrendingTopic",
      "Returns community trending topics.",
      {
        start: z.number().optional(),
        limit: z.number().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/community/trending/topic', params)
          return formatResponse(data)
        })
      }
    )

    // /community/trending/token
    server.tool("communityTrendingToken",
      "Returns community trending tokens.",
      {
        start: z.number().optional(),
        limit: z.number().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/community/trending/token', params)
          return formatResponse(data)
        })
      }
    )
  }

  /*
  * ENTERPRISE SUBSCRIPTION ENDPOINTS
  */
  if (checkSubscriptionLevel(subscriptionLevel, SUBSCRIPTION_LEVELS.Enterprise)) {
    // /blockchain/statistics/latest
    server.tool("blockchainStatisticsLatest",
      "Returns the latest statistics for one or more blockchains.",
      {
        id: z.string().optional(),
        slug: z.string().optional(),
        symbol: z.string().optional()
      },
      async (params) => {
        return handleEndpoint(async () => {
          const data = await makeApiRequest(apiKey, '/v1/blockchain/statistics/latest', params)
          return formatResponse(data)
        })
      }
    )
  }

  return server
}

// Stdio Server 
const stdioServer = createServer({})
const transport = new StdioServerTransport()
await stdioServer.connect(transport)

// Streamable HTTP Server
const { app } = createStatefulServer(createServer)
const PORT = process.env.PORT || 3000
app.listen(PORT)

```