apify-utils
# apify-utils-mcp
Four small utilities behind one MCP server: **PDF text, tables and OCR**, **YouTube
thumbnail URLs and files**, **sports scores and standings**, and **ATP/WTA tennis**.
Each tool is a thin client over a public [Apify](https://apify.com) Actor. Runs are
billed by Apify to **your own** account — see [Pricing](#pricing). This server never
proxies a call, never sees your token or your results, and never pays for a run on your
behalf.
## Install
Requires [uv](https://docs.astral.sh/uv/) and a free
[Apify](https://console.apify.com/settings/integrations) token.
```json
{
"mcpServers": {
"apify-utils": {
"command": "uvx",
"args": ["--from", "git+https://github.com/moonie0201/apify-utils-mcp", "apify-utils-mcp"],
"env": { "APIFY_TOKEN": "apify_api_..." }
}
}
}
```
| Client | Where that JSON goes |
|---|---|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) · `%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
| Cursor | `~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project |
| Claude Code | `claude mcp add apify-utils --env APIFY_TOKEN=apify_api_... -- uvx --from git+https://github.com/moonie0201/apify-utils-mcp apify-utils-mcp` |
## Tools
| Tool | What it does | Spend cap | Cost |
|---|---|---|---|
| `extract_pdf(urls, max_pages, output_mode?, page_range?, max_pages_per_pdf?, include_markdown?, extract_tables?, ocr?, max_ocr_pages_per_pdf?, chunk_size?, chunk_overlap?)` | Text, markdown and ruled tables per page, per document, or as RAG chunks with page and character offsets. Optional Tesseract OCR for pages with no text layer. | `max_pages` | $0.0003/page, $0.003/OCR page |
| `youtube_thumbnails(videos, max_videos, sizes?, image_format?, include_metadata?, save_images?)` | Thumbnail URLs (jpg and webp) for video URLs or bare IDs, with per-size availability, bytes and etag from a HEAD probe, plus title and channel. Optionally saves the files to your own key-value store. | `max_videos` | $0.005/video |
| `sports_scores(leagues, max_items, mode?, date_from?, date_to?, teams?, status?, season?, season_type?, event_ids?, include_odds?, timezone?)` | Scores, schedules, standings, team directories and game summaries across 33 leagues. | `max_items` | $0.002/game · $0.001/standings or team row · $0.004/summary |
| `tennis_scores(max_items, tours?, date_from?, date_to?, player?, status?, timezone?)` | ATP and WTA matches with per-set scores, round, seeds, country, court and result text. | `max_items` | $0.002/match |
| `list_supported_utilities()` | Every tool, the Actor behind it, its exact per-event price and its spend-cap argument. | — | free, no network |
Filters (`page_range`, `teams`, `status`, `max_pages_per_pdf`) are applied **before**
billing, so a narrow query is cheap rather than a big response you post-filter. The
spend-cap argument is required on every paid tool — there is no way to start an
unbounded run from here.
## Pricing
No Actor here charges a start fee, so a run that fails or finds nothing costs nothing.
| Tool | Event | Price | Free, always |
|---|---|---|---|
| `extract_pdf` | page | $0.0003 | document summary rows, error rows, pages outside `page_range`/`max_pages_per_pdf`, refused downloads |
| `extract_pdf` | ocr-page | $0.003 | image-only pages when `ocr` is off (delivered empty with `needsOcr`) |
| `youtube_thumbnails` | video | $0.005 | `not_found`, `invalid_input`, `duplicate`, `removed`, `playlist_not_supported`, `budget_exhausted` |
| `sports_scores`, `tennis_scores` | game | $0.002 | `league_summary` and `error` rows, games dropped by `teams`/`status`, off-season windows, unknown leagues or event ids |
| `sports_scores` | row | $0.001 | — |
| `sports_scores` | summary | $0.004 | — |
Worked examples: a 2-page invoice is **$0.0006**, a 100-page manual **$0.03**, 20 scanned
pages with OCR **$0.06**, 100 videos **$0.50**, today's NBA slate of 8 games **$0.016**,
500 game summaries **$2.00**.
Billing lands on the Apify account behind your own `APIFY_TOKEN`. The spend-cap
argument is not just documentation: the server turns it into the run's
`maxTotalChargeUsd`, so Apify enforces the dollar ceiling itself and stops the run when
it is reached. Because OCR pages draw on the same `max_pages` budget at ten times the
price, `max_pages=1000` means **$0.30** with `ocr` off and **$3.00** with it on, and the
ceiling sent to Apify follows whichever applies. Apify refuses a ceiling below an
Actor's own minimum ($0.05 for `extract_pdf`, $0.10 for the rest), so very small runs ask
for that floor — a ceiling, not a charge: a two-page PDF still costs $0.0006.
The run is also started with `timeout=290`, inside Apify's 300 s synchronous window, so a
run can never outlive the call and keep charging in the background.
## Sports data: read this first
> **Unofficial.** Not affiliated with, endorsed by or sponsored by ESPN, The Walt Disney
> Company or any league. Data comes from the unauthenticated JSON feeds that espn.com
> itself uses; they are undocumented, **Disney's Terms of Use for espn.com restrict
> automated access**, and ESPN **may change or block access without notice** — if that
> happens the Actor fails the run and charges nothing, and we stop within **48 h** of any
> notice from ESPN or Disney. Scores, schedules and standings are facts; ESPN's articles,
> video and images are ESPN's copyrighted content and are not included. Odds are one
> sportsbook's pregame line shown on ESPN, for information only — **not for wagering**.
> Team and league names and logos are trademarks of their owners.
> **Removal requests:** [TAKEDOWN.md](https://github.com/moonie0201/apify-utils/blob/main/TAKEDOWN.md) — honoured in 48 h.
`include_odds` defaults to **off** here, unlike the Actor. Turn it on only if you want the
pregame line copied into the row. `timezone` defaults to `UTC` and only affects the
`dateLocal` field; feed dates are UTC either way.
**Availability.** All four Actors are public: `acotr_moonie/pdf-text-extractor`,
`acotr_moonie/youtube-thumbnail-downloader`, `acotr_moonie/espn-sports-scraper` and
`acotr_moonie/tennis-scores-scraper` (the two sports listings since 2026-08-28). Every
tool was verified end to end against its live Actor.
`leagues` accepts: `nfl`, `ncaaf`, `nba`, `wnba`, `ncaab`, `ncaaw`, `mlb`, `nhl`, `mls`,
`nwsl`, `epl`, `championship`, `fa-cup`, `la-liga`, `serie-a`, `bundesliga`, `ligue-1`,
`eredivisie`, `primeira-liga`, `liga-mx`, `saudi-pro-league`, `wsl`, `ucl`, `uel`, `uecl`,
`nations-league`, `world-cup`, `copa-libertadores`, `atp`, `wta`, `pga`, `f1`, `ufc` — or
any raw ESPN path such as `soccer/bra.1`, which comes back as a free error row if unknown.
## YouTube
Nominative reference only: this reads the public `i.ytimg.com` thumbnail CDN and YouTube's
public oEmbed endpoint. Not affiliated with, endorsed by or sponsored by YouTube or
Google; the YouTube name and logo are trademarks of Google LLC. Thumbnails are the
copyright of the uploader or their licensors — the tool retrieves them, it does not grant
you a licence to republish them.
## What it will not do
- **Nothing is inferred.** Where a value cannot be determined the field is `null`.
- `extract_pdf` reads PDFs you point it at with a plain GET, no proxy and no login. You
are responsible for the hosting site's terms and robots rules. PDFs whose owner disabled
text copying are always skipped, with a free error row and no override.
- Markdown headings and paragraphs are heuristics over font size and line gaps, not a
layout model. Table extraction finds tables drawn with ruling lines; borderless tables
stay in the text.
- `youtube_thumbnails` does not expand playlist or channel URLs, and does not download
video.
- The sports tools carry no news, articles, rosters, injuries, athlete bios, play-by-play
or video, and no ITF or Challenger tennis.
## Development
```bash
uv venv && uv pip install -e ".[dev]"
python -m pytest tests -q # offline: httpx is monkeypatched, nothing is billed
ruff check . && ruff format --check .
python -m apify_utils_mcp --list-tools
```
## Links
- Actor source, MIT: <https://github.com/moonie0201/apify-utils>
- Privacy policy: <https://github.com/moonie0201/apify-utils/blob/main/PRIVACY.md>
- Takedown policy: <https://github.com/moonie0201/apify-utils/blob/main/TAKEDOWN.md>
## Disclosure
I built this, and the four Actors behind it are paid. The adapter code is open and MIT
licensed.
TDQS
Scored across 5 tools
Most tools are distinct, but sports_scores and tennis_scores both consume the same ESPN feed and overlap on tennis coverage, creating real selection ambiguity. The descriptions clarify the difference, but an agent could reasonably choose either tool for a tennis query.
Names are all snake_case and readable, but conventions are mixed: extract_pdf and list_supported_utilities are verb_noun, while youtube_thumbnails, sports_scores, and tennis_scores are noun phrases. There is no consistent verb_noun pattern across the set.
Five tools is a reasonable size for a utility aggregator, and most tools cover substantial functionality. The sports/tennis duplication makes one tool feel slightly redundant, but the count is not bloated or thin.
Relative to the advertised utilities, the tool surface is well covered: PDF extraction has multiple output modes and OCR, YouTube thumbnails cover sizes and storage, and sports data includes scoreboards, schedules, standings, teams, and summaries. list_supported_utilities prevents dead ends by exposing league keys, pricing, and reachability.