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

```
├── .gitignore
├── LICENSE
├── main.py
├── README.md
└── requirements.txt
```

# Files

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

```
# Python
__pycache__/
venv/
.env/
.venv/

# VS Code
.vscode/

# macOS
.DS_Store

# Local env files
.envrc
.env.*
```

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

```markdown
# mcp-medium-accelerator

Questo MCP server permette a LLM come ad esempio Claude Desktop, dando in input una URL tipo "https://medium.com/tag/frontend/archive" di estrapolare tutti i link degli ultimi articoli. Creare un riassunto, anche in italiano degli articoli che interessano all'utente. Salvare il riassunto in una memoria locale da poter interpellare in qualsiasi momento.

---

## Requisiti

Assicurati di avere installato:

- Python ≥ 3.10
- Claude Desktop

---

## Installazione locale

1. Clona il repository:

```bash
git clone https://github.com/crtdaniele/mcp-medium-accelerator
cd mcp-medium-accelerator
```

2. Crea e attiva un ambiente virtuale:

```bash
python -m venv venv
source venv/bin/activate
```

3. Installa le dipendenze:

```bash
pip install -r requirements.txt
```

4. (Facoltativo) Aggiorna il file requirements.txt dopo aver aggiunto nuove librerie:

```bash
pip freeze > requirements.txt
```

## Avvio del server MCP

Per eseguire il server MCP in modalità sviluppo con hot reload:

```bash
mcp dev main.py
```

Per eseguire il server in modalità normale:

```bash
mcp run main.py
```

## Tool

Tool disponibili:

- **extract_article_links:**

Estrae i link degli articoli da un URL di archivio Medium. Restituisce una lista di link agli articoli.

- **extract_article_text:**

Estrae il contenuto di un articolo da un URL di Medium. Restituisce il contenuto dell’articolo. Chiede all’utente se desidera salvare il riassunto tramite save_summary.

- **save_summary:**

Salva un riassunto di un articolo con titolo, URL e tag. Restituisce un messaggio di stato.

- **list_summaries:**

Elenca tutti i riassunti salvati. Restituisce una lista di riassunti.

## Installazione su Claude Desktop

```bash
mcp install main.py
```

Oppure configura manualmente il file settings.json (Claude Desktop > Settings > Advanced):

```bash
{
  "mcpServers": {
    "mcp-medium-accelerator": {
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "run",
        "--with",
        "bs4",
        "--with",
        "httpx",
        "--with",
        "datetime",
        "--with",
        "tinydb",
        "--with",
        "mcp[cli]",
        "mcp",
        "run",
        "/your-local-path/main.py"
      ]
    }
  }
}
```

## Licenza

MIT License.
© 2025 Daniele Carta

# Contribuire

Pull request benvenute!

Segnala bug o richiedi funzionalità aprendo una issue.

```

--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------

```
annotated-types==0.7.0
anyio==4.9.0
beautifulsoup4==4.13.4
bs4==0.0.2
certifi==2025.4.26
charset-normalizer==3.4.2
click==8.2.1
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
httpx-sse==0.4.0
idna==3.10
markdown-it-py==3.0.0
mcp==1.9.2
mdurl==0.1.2
pydantic==2.11.5
pydantic-settings==2.9.1
pydantic_core==2.33.2
Pygments==2.19.1
python-dotenv==1.1.0
python-multipart==0.0.20
requests==2.32.3
rich==14.0.0
shellingham==1.5.4
sniffio==1.3.1
soupsieve==2.7
sse-starlette==2.3.5
starlette==0.47.0
tinydb==4.8.2
typer==0.16.0
typing-inspection==0.4.1
typing_extensions==4.13.2
urllib3==2.4.0
uvicorn==0.34.2
```

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

```python
from mcp.server.fastmcp import FastMCP
import httpx
from bs4 import BeautifulSoup
import os
from tinydb import TinyDB, Query
from datetime import datetime

mcp = FastMCP("mcp-medium-accelerator")

home_dir = os.path.expanduser("~")
data_dir = os.path.join(home_dir, "mcp-medium-accelerator_data")
os.makedirs(data_dir, exist_ok=True)

db_path = os.path.join(data_dir, "summaries.json")
db = TinyDB(db_path)
summaries_table = db.table("summaries")

@mcp.tool(
    description="Extracts article links from a Medium archive URL. Returns a list of article links.",
    name="extract_article_links",
)
def extract_article_links(archive_url: str, limit: int = 10) -> list[str]:
    response = httpx.get(archive_url)
    soup = BeautifulSoup(response.text, "html.parser")
    links = []

    for div in soup.find_all("article", attrs={"data-testid": "post-preview"}):
        for inner_div in div.find_all("div"):
            if inner_div.has_attr("data-href"):
                div_with_data_href = inner_div["data-href"]
                break
        if div_with_data_href:
            href = div_with_data_href
            if href not in links:
                links.append(href)
            if len(links) >= limit:
                break

    return links

@mcp.tool(
    description="Saves a summary of an article with its title, URL, and tags. Returns a status message.",
    name="save_summary",
)
def save_summary(title: str, url: str, summary: str, tags: list[str] = []):
    entry = {
        "title": title,
        "url": url,
        "summary": summary,
        "tags": tags,
        "saved_at": datetime.utcnow().isoformat()
    }

    Article = Query()
    if summaries_table.contains(Article.url == url):
        return {"status": "already_saved"}

    summaries_table.insert(entry)
    return {"status": "ok"}

@mcp.tool(
    description="Lists all saved summaries. Returns a list of summaries.",
    name="list_summaries",
)
def list_summaries():
    return summaries_table.all()

@mcp.tool(
    description="Extracts the text content from a saved article summary. Ask if the user want to save it with save_summary.",
    name="extract_article_text",
)
def extract_article_text(url: str):
    response = httpx.get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    article = soup.find("article")
    return article.get_text() if article else ""
```