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

```
├── .gitignore
├── .python-version
├── cryptopanic_mcp_server.egg-info
│   ├── dependency_links.txt
│   ├── PKG-INFO
│   ├── requires.txt
│   ├── SOURCES.txt
│   └── top_level.txt
├── LICENSE
├── main.py
├── pyproject.toml
├── README.md
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------

```
3.13

```

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

```
.env
.venv
__pycache__
*.egg-info/
```

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

```markdown
# cryptopanic-mcp-server

[![Discord](https://img.shields.io/discord/1353556181251133481?cacheSeconds=3600)](https://discord.gg/aRnuu2eJ)
![GitHub License](https://img.shields.io/github/license/kukapay/blockbeats-mcp)

Provide the latest cryptocurrency news to AI agents, powered by [CryptoPanic](https://cryptopanic.com/).

<a href="https://glama.ai/mcp/servers/dp6kztv7yx">
  <img width="380" height="200" src="https://glama.ai/mcp/servers/dp6kztv7yx/badge" alt="cryptopanic-mcp-server MCP server" />
</a>

## Tools

The server implements only one tool: 

```python
get_crypto_news(kind: str = "news", num_pages: int = 1) -> str
```
- `kind`: Content type (news, media)
- `num_pages`: Number of pages to fetch (default: 1, max: 10)

Example Output: 

```
- Bitcoin Breaks $60k Resistance Amid ETF Optimism
- Ethereum Layer 2 Solutions Gain Traction
- New Crypto Regulations Proposed in EU
- ...
```


## Configuration

- CryptoPanic API key & API plan: get one [here](https://cryptopanic.com/developers/api/)
- Add a server entry to your configuration file:

```
"mcpServers": { 
  "cryptopanic-mcp-server": { 
    "command": "uv", 
    "args": [ 
      "--directory", 
      "/your/path/to/cryptopanic-mcp-server", 
      "run", 
      "main.py" 
    ], 
    "env": { 
      "CRYPTOPANIC_API_PLAN": "your_api_plan",
      "CRYPTOPANIC_API_KEY": "your_api_key" 
    } 
  } 
}
```

- Replace `/your/path/to/cryptopanic-mcp-server` with your actual installation path.
- Replace `CRYPTOPANIC_API_PLAN` and `CRYPTOPANIC_API_KEY` with your API plan and key from CryptoPanic. 

## License

MIT License - see `LICENSE` file
```

--------------------------------------------------------------------------------
/cryptopanic_mcp_server.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------

```


```

--------------------------------------------------------------------------------
/cryptopanic_mcp_server.egg-info/top_level.txt:
--------------------------------------------------------------------------------

```
main

```

--------------------------------------------------------------------------------
/cryptopanic_mcp_server.egg-info/requires.txt:
--------------------------------------------------------------------------------

```
dotenv>=0.9.9
mcp[cli]>=1.3.0
requests>=2.32.3

```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[project]
name = "cryptopanic-mcp-server"
version = "0.1.0"
description = "Provide the latest cryptocurrency news for AI agents."
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "dotenv>=0.9.9",
    "mcp[cli]>=1.3.0",
    "requests>=2.32.3",
]

```

--------------------------------------------------------------------------------
/cryptopanic_mcp_server.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------

```
LICENSE
README.md
main.py
pyproject.toml
cryptopanic_mcp_server.egg-info/PKG-INFO
cryptopanic_mcp_server.egg-info/SOURCES.txt
cryptopanic_mcp_server.egg-info/dependency_links.txt
cryptopanic_mcp_server.egg-info/requires.txt
cryptopanic_mcp_server.egg-info/top_level.txt
```

--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------

```python
import requests
from mcp.server.fastmcp import FastMCP
import os
from dotenv import load_dotenv

load_dotenv()
API_KEY = os.getenv("CRYPTOPANIC_API_KEY")
API_PLAN = os.getenv("CRYPTOPANIC_API_PLAN", "developer")

# Validate API_KEY
if not API_KEY:
    raise ValueError("CRYPTOPANIC_API_KEY environment variable is not set")

mcp = FastMCP("crypto news")
        
@mcp.tool()
def get_crypto_news(kind: str = "news", num_pages: int = 1) -> str:
    """
    Fetch the latest cryptocurrency news from CryptoPanic.

    Args:
        kind (str, optional): Type of content to fetch. Valid options are:
            - 'news': Fetch news articles (default).
            - 'media': Fetch media content like videos.
        num_pages (int, optional): Number of pages to fetch (each page contains multiple news items).
            Defaults to 1. Maximum is 10 to avoid API rate limits.

    Returns:
        str: A concatenated string of news titles, each prefixed with a dash (-).

    Raises:
        ValueError: If the API key is not set or if the API request fails.
    """
    news = fetch_crypto_news(kind, num_pages)
    readable = concatenate_news(news)
    return readable

def fetch_crypto_news_page(kind: str = "news", page: int = 1) -> list:
    try:
        url = f"https://cryptopanic.com/api/{API_PLAN}/v2/posts/"
        params = {
            "auth_token": API_KEY,
            "kind": kind,
            "regions": "en",
            "page": page
        }
        response = requests.get(url, params=params)
        return response.json().get("results", [])
    except Exception:
        return []
        
def fetch_crypto_news(kind: str = "news", num_pages: int = 10) -> list:
    all_news = []
    for page in range(1, num_pages + 1):
        news_items = fetch_crypto_news_page(kind, page)
        if not news_items:
            break
        all_news.extend(news_items)
    return all_news        

def concatenate_news(news_items: list) -> str:
    concatenated_text = ""
    for idx, news in enumerate(news_items):  
        title = news.get("title", "No Title")
        concatenated_text += f"- {title}\n"
    return concatenated_text.strip()

if __name__ == "__main__":
    mcp.run(transport="stdio")

```