Server data from the Official MCP Registry
Confidence-scored domain availability checking for AI agents via CanYouGrab.it
Confidence-scored domain availability checking for AI agents via CanYouGrab.it
Remote endpoints: streamable-http: https://api.canyougrab.it/mcp
The MCP server for CanYouGrab.it has reasonable architecture with proper API key authentication via Bearer tokens and JWT validation. However, there are several concerns: the minified frontend code obscures visibility into client-side security practices, missing input validation patterns in critical endpoints, overly broad error handling in worker processes, and excessive file system permissions for a domain lookup service. The server's permissions exceed its stated purpose of checking domain availability. Supply chain analysis found 7 known vulnerabilities in dependencies (0 critical, 3 high severity). Package verification found 1 issue.
4 files analyzed · 15 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Available as Local & Remote
This plugin can run on your machine or connect to a hosted endpoint. during install.
From the project's GitHub README.
Domain availability lookup API with subscription billing, built on FastAPI + DNS (Unbound resolver).
Live services:
https://api.canyougrab.ithttps://portal.canyougrab.ithttps://auth.canyougrab.it (Auth0 custom domain)┌──────────────────────────────────────────────────────────────────┐
│ Developer Portal │
│ (Zudoku/React on portal.canyougrab.it) │
│ Usage Dashboard · API Keys · Pricing · API Reference (OAS) │
└──────────────────┬───────────────────────────────────────────────┘
│ Auth0 JWT (portal) / Bearer API key (API)
▼
┌──────────────────────────────────────────────────────────────────┐
│ FastAPI Backend (v5.0.0) │
│ api.canyougrab.it:8000 │
│ │
│ ┌─────────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ app.py │ │ keys.py │ │billing.py│ │ auth.py │ │
│ │ /check/bulk │ │ /keys │ │/billing │ │ API key + │ │
│ │ /usage │ │ CRUD │ │/stripe │ │ JWT auth │ │
│ │ │ │ rotate │ │ webhook │ │ │ │
│ └──────┬──────┘ └──────────┘ └────┬─────┘ └─────────────┘ │
│ │ │ │
└─────────┼────────────────────────────┼───────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Valkey (Redis) │ │ Stripe API │
│ Job queue + │ │ Subscriptions │
│ Rate limiting │ │ Webhooks │
└────────┬────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌──────────────────────────────┐
│ RQ Worker │──DNS──▶ │ Unbound Resolver │
│ (worker.py) │ │ (dedicated droplet) │
│ ThreadPool(10) │ │ NS queries via VPC │
│ via RQ queue │ └──────────────────────────────┘
└─────────────────┘
┌──────────────────────────────┐
───────────────────▶│ PostgreSQL │
(auth, usage, │ API keys + Usage logs │
billing only) └──────────────────────────────┘
zuplo/
├── backend/ # Python FastAPI backend
│ ├── app.py # Main API: /check/bulk, /usage, /health
│ ├── auth.py # API key auth (SHA-256) + Auth0 JWT auth (RS256)
│ ├── billing.py # Stripe checkout, portal, webhooks, card-on-file, usage details
│ ├── keys.py # API key CRUD: create, list, rotate, revoke (+ Turnstile)
│ ├── antifraud.py # Anti-fraud: Turnstile, device fingerprints, risk scoring
│ ├── email_utils.py # Email normalization + disposable email detection
│ ├── dns_client.py # DNS-based domain availability checking via Unbound
│ ├── queries.py # PostgreSQL queries: usage tracking, auth, billing
│ ├── valkey_client.py # Redis/Valkey job queue client (RQ-backed)
│ ├── rq_tasks.py # RQ task function: process_domain_job()
│ ├── worker.py # RQ worker process (ThreadPoolExecutor per job)
│ ├── migrations/ # SQL migrations
│ │ └── 001_free_tier_antifraud.sql
│ └── requirements.txt # Python dependencies
├── portal/ # Developer portal (Zuplo + Zudoku)
│ ├── config/
│ │ ├── routes.oas.json # OpenAPI 3.1 spec (public API documentation)
│ │ └── policies.json # Zuplo policies (empty — all routing is direct)
│ ├── docs/ # Zudoku documentation portal
│ │ ├── src/
│ │ │ ├── config.ts # API_BASE, Turnstile site key, Stripe PK
│ │ │ ├── UsageDashboard.tsx # Usage + billing dashboard component
│ │ │ ├── PricingPage.tsx # Plan selection + Stripe checkout
│ │ │ ├── PricingPlans.tsx # Pricing card grid component
│ │ │ └── CardSetupPage.tsx # Stripe Elements card-on-file for Free+
│ │ ├── public/ # Static assets (logos, banners, CSS overrides)
│ │ ├── zudoku.config.tsx # Portal config: theme, nav, Auth0, API key mgmt
│ │ └── package.json # Frontend dependencies (React 19, Zudoku)
│ ├── package.json # Workspace root (Zuplo v6, TypeScript v5)
│ └── README.md # Zuplo boilerplate (not project-specific)
├── mcp-server/ # MCP package for ChatGPT, Claude, and remote MCP clients
│ ├── pyproject.toml # MCP package metadata + version
│ ├── server.json # MCP registry/server metadata
│ ├── uv.lock # Locked MCP runtime dependencies
│ └── src/canyougrab_mcp/
│ ├── __init__.py
│ └── server.py # stdio + streamable-http MCP entrypoint
├── .github/workflows/
│ ├── deploy.yml # Production deploy (on tag push v*)
│ └── deploy-dev.yml # Dev deploy (on push to dev branch)
├── .claude/
│ └── launch.json # Local dev server config (port 9200)
└── package.json # Root workspace config
Authorization: Bearer cyg_...)| Method | Path | Description |
|---|---|---|
POST | /api/check/bulk | Check up to 100 domains. Long-polls until results ready (30s max). |
GET | /api/account/usage | Usage summary for the authenticated consumer. |
GET | /api/account/quota-check | Lightweight monthly + per-minute quota check. |
GET | /health | Health check (no auth). |
| Method | Path | Description |
|---|---|---|
POST | /api/keys | Create new API key. |
GET | /api/keys | List user's API keys. |
POST | /api/keys/{id}/rotate | Rotate key (revoke old, create new). |
DELETE | /api/keys/{id} | Revoke (soft-delete) a key. |
POST | /api/billing/checkout | Create Stripe Checkout session. |
POST | /api/billing/portal | Create Stripe Customer Portal session. |
POST | /api/billing/setup-card | Create SetupIntent for Free+ card-on-file. |
POST | /api/billing/confirm-free-plus | Verify card fingerprint and upgrade to Free+. |
GET | /api/billing/card-status | Check if user has a card on file. |
GET | /api/billing/usage/detailed | Per-key usage breakdown for portal dashboard. |
POST | /api/antifraud/turnstile/verify | Verify Cloudflare Turnstile token. |
POST | /api/antifraud/device/register | Register device fingerprint (Fingerprint Pro). |
GET | /api/antifraud/risk | Get risk assessment for authenticated user. |
POST | /api/antifraud/assess-signup | Run multi-signal risk assessment at signup. |
| Method | Path | Description |
|---|---|---|
POST | /api/account/usage/detailed | Multi-consumer usage breakdown. |
POST | /api/stripe/webhook | Stripe webhook receiver (signature-verified). |
Client FastAPI Valkey Worker Unbound
│ │ │ │ │
│ POST /api/check/bulk │ │ │ │
│ { domains: [...] } │ │ │ │
│─────────────────────────▶│ │ │ │
│ │── validate key ──────────│ │ │
│ │── check minute rate ────▶│ INCR ratelimit:id:min │ │
│ │── check monthly quota ──▶│ (PostgreSQL) │ │
│ │── record usage ─────────▶│ (PostgreSQL) │ │
│ │── create_job() ─────────▶│ HSET job:{uuid} │ │
│ │ │ RQ enqueue (with retry) │ │
│ │ │ │ │
│ │ │◀── RQ Worker dequeues ───│ │
│ │ │ │ │
│ │ │──── claim_job() ────────▶│ │
│ │ │ │── ThreadPool(10) │
│ │ │ │── check_domain_dns()
│ │ │ │── NS query ───────▶│
│ │ │ │◀── NOERROR/NXDOMAIN│
│ │ │◀── complete_job() ───────│ │
│ │ │ │ │
│ │◀─ poll get_job_status() ─│ │ │
│ │ (0.3s interval, 30s │ │ │
│ │ max timeout) │ │ │
│◀─────────────────────────│ │ │ │
│ { results: [...] } │ │ │ │
User → Pricing Page → Select Plan → Auth0 login
→ POST /api/billing/checkout (JWT auth)
→ Find/create Stripe customer (linked by auth0_sub metadata)
→ Stripe Checkout Session created → redirect to Stripe
→ User pays → Stripe fires webhook
→ POST /api/stripe/webhook (HMAC-SHA256 verified)
→ checkout.session.completed → fetch subscription → get price ID
→ Map price to plan → UPDATE api_keys SET plan, lookups_limit
| Plan | Monthly Lookups | Per-Minute Rate Limit | Domains/Request | Price |
|---|---|---|---|---|
| Free | 500 | 30/min | 30 | $0 |
| Free+ | 10,000 | 100/min | 100 | $0 (card on file) |
| Basic | 20,000 | 300/min | 100 | $10/mo |
| Pro | 50,000 | 1,000/min | 100 | $20/mo |
| Business | 300,000 | 3,000/min | 100 | $30/mo |
Authorization: Bearer cyg_<token>api_keys table (plaintext never stored)cyg_XXXXXXXX...) is stored for display in the portalrevoked_at timestamp, not physically deleted)dev-mqe5tavp6dr62e7uauth.canyougrab.ithttps://api.canyougrab.itDomain availability is checked by querying a dedicated Unbound recursive DNS resolver running on a separate DigitalOcean droplet. The worker sends NS record queries over the VPC private network and interprets the response:
| DNS Response | available | Meaning |
|---|---|---|
| NOERROR + NS records | false | Domain is registered and delegated |
| NXDOMAIN | true | Domain not in zone — probably available |
| NoAnswer (NOERROR, no NS) | false | Registered but parked/undelegated |
| SERVFAIL / Timeout | null | Ambiguous — check failed |
The Unbound resolver caches aggressively (7 days for registered domains, 5 minutes for NXDOMAIN) and queries TLD authoritative servers directly, avoiding public resolver rate limits.
PostgreSQL is used for authentication, usage tracking, and billing — not for domain lookups.
| Table | Purpose | Key Columns |
|---|---|---|
api_keys | User API keys | id, user_sub, email_normalized, key_hash, key_prefix, plan, lookups_limit, revoked_at |
usage_log_daily | Daily usage aggregates | consumer, lookups, recorded_at (DATE, unique per consumer+day) |
usage_log_minute | Per-minute usage aggregates | consumer, lookups, minute_start (TIMESTAMP, unique per consumer+minute) |
card_fingerprints | One free account per card | user_sub, stripe_fingerprint (unique pair) |
device_fingerprints | Device-based multi-account detection | user_sub, visitor_id (Fingerprint Pro) |
account_risk | Composite risk scoring | user_sub (unique), risk_score, risk_signals (JSONB) |
| Key Pattern | Type | Purpose | TTL |
|---|---|---|---|
job:{uuid} | Hash | Job status, domains, results | 1 hour |
rq:queue:canyougrab-jobs | List | RQ job queue (managed by RQ) | — |
rq:job:{uuid} | Hash | RQ job metadata (retries, status) | Configurable |
ratelimit:{consumer}:{YYYYMMDDHHmm} | String | Per-minute rate limit counter (INCR) | 60s |
| Environment | Domain | Purpose |
|---|---|---|
| Production | api.canyougrab.it | FastAPI + Worker |
| Dev | dev.canyougrab.it | FastAPI + Worker |
| Portal | portal.canyougrab.it | Zudoku static site |
| Auth | auth.canyougrab.it | Auth0 custom domain |
| Service | Purpose |
|---|---|
| DigitalOcean | Droplets (API servers, Unbound resolver), Managed PostgreSQL, Managed Valkey |
| Cloudflare | DNS, CDN, SSL for canyougrab.it zone, Turnstile bot prevention |
| Auth0 | User authentication, social login (Google + Apple), JWT issuance |
| Stripe | Subscription billing, checkout, customer portal, webhooks |
| GitHub Actions | CI/CD deployment pipelines |
Trigger: Push a git tag matching v* (e.g., v1.0.5)
git tag v1.0.5 && git push origin v1.0.5
Pipeline (.github/workflows/deploy.yml):
v* tag pushDEPLOY_HOST secret) using DEPLOY_SSH_KEYgit fetch + git checkout <tag>)/opt/canyougrab-repo/scripts/deploy-host.shTrigger: Push to the dev branch
git push origin dev
Pipeline (.github/workflows/deploy-dev.yml):
devDEV_DEPLOY_HOST secret) using same DEPLOY_SSH_KEYdev ref on the server (git fetch + git checkout dev)/opt/canyougrab-repo/scripts/deploy-host.shBoth pipelines use a small inline SSH bootstrap to update /opt/canyougrab-repo to the target ref, then call scripts/deploy-host.sh from that checked-out revision. The repo-managed deploy script handles:
pip install -r requirements.txt for backend dependenciesrsync of backend, portal, and MCP source trees into their runtime directoriesmcp-server package/runtime when the host also runs canyougrab-mcp.serviceIf /mcp is served by the same host as the API, backend-only deploys are not enough. The MCP service must be updated and restarted during the same deploy or the OAuth metadata and live MCP behavior can drift apart.
An existing /opt/deploy.sh can still be kept as a manual compatibility shim if desired, but the automated pipeline should treat the repo copy of scripts/deploy-host.sh as the source of truth.
| Branch | Purpose | Deploys To |
|---|---|---|
main | Production releases | Tagged → api.canyougrab.it |
dev | Development/staging | Auto → dev.canyougrab.it |
| Feature branches | In-progress work | No auto-deploy |
# DNS Resolver (Unbound on dedicated droplet)
DNS_RESOLVER_HOSTNAME=unbound.canyougrab.internal # VPC internal hostname (resolved via socket.gethostbyname)
DNS_RESOLVER_PORT=53
DNS_QUERY_TIMEOUT=5.0 # Per-query timeout in seconds
# PostgreSQL (DigitalOcean Managed Database — auth, usage, billing only)
POSTGRES_HOST= # DB cluster hostname
POSTGRES_PORT=5432
POSTGRES_DB=canyougrab
POSTGRES_USER=canyougrab
POSTGRES_PASSWORD= # DB password
POSTGRES_SSLMODE=require
# Valkey / Redis (DigitalOcean Managed Database)
VALKEY_HOST= # Valkey cluster hostname
VALKEY_PORT=25061
VALKEY_USERNAME=default
VALKEY_PASSWORD= # Valkey password
# Auth0
AUTH0_DOMAIN=dev-mqe5tavp6dr62e7u.us.auth0.com
AUTH0_AUDIENCE=https://api.canyougrab.it
# Stripe
STRIPE_SECRET_KEY= # sk_live_... (prod) or sk_test_... (dev)
STRIPE_WEBHOOK_SECRET= # whsec_... (per-environment)
STRIPE_PRICE_BASIC= # price_... (live price ID for Basic plan)
STRIPE_PRICE_PRO= # price_... (live price ID for Pro plan)
STRIPE_PRICE_BUSINESS= # price_... (live price ID for Business plan)
# Cloudflare Turnstile (bot prevention on key creation)
TURNSTILE_SECRET_KEY= # 0x4AAAA... (from Cloudflare dashboard)
# Portal
PORTAL_URL=https://portal.canyougrab.it
# Worker
BATCH_CONCURRENCY=10 # Thread pool size for domain checks
VALKEY_QUEUE_NAME=canyougrab-jobs # RQ queue name (default: canyougrab-jobs)
# Monitoring (optional — only if monitoring stack is installed)
SLACK_ALERTS_WEBHOOK_URL= # Slack incoming webhook for Alertmanager
RQ_METRICS_PORT=9122 # Prometheus metrics exporter port
# Auto-scaler (optional — only on API host with autoscaler enabled)
DO_API_TOKEN= # DigitalOcean API token
DO_WORKER_SNAPSHOT_ID= # Snapshot ID for new worker droplets
AUTOSCALER_MIN_WORKERS=1
AUTOSCALER_MAX_WORKERS=5
AUTOSCALER_SCALE_UP_THRESHOLD=50
AUTOSCALER_SCALE_DOWN_IDLE_MINUTES=10
| Secret | Purpose |
|---|---|
DEPLOY_HOST | Production server IP |
DEV_DEPLOY_HOST | Dev server IP |
DEPLOY_SSH_KEY | SSH private key for both servers |
cd backend
pip install -r requirements.txt
# Start API server
uvicorn app:app --reload --port 8000
# Start worker (separate terminal)
python worker.py
Requires DNS_RESOLVER_HOST pointing to an Unbound instance (or use 8.8.8.8 for basic testing), plus PostgreSQL and Valkey/Redis (local or tunneled to managed instances).
cd portal/docs
npm install
npm run dev # Starts Zudoku dev server (via zuplo)
Or use the configured launch server:
# From repo root
npx zuplo dev # Starts on port 9200
Portal dev server hardcodes API_BASE to https://api.canyougrab.it in portal/docs/src/config.ts. To develop against a local backend, change this to http://localhost:8000.
Because canyougrab.it uses a .it country-code TLD (Italy), all user-facing HTML (portal, docs) must include signals that the content targets English speakers:
<html lang="en"> on every page.<head>:
<link rel="alternate" hreflang="en-US" href="{page_url}" />
<link rel="alternate" hreflang="en" href="{page_url}" />
<link rel="alternate" hreflang="x-default" href="{page_url}" />
<link rel="canonical" href="{page_url}" />.This applies to the portal (portal.canyougrab.it), API docs, and any other publicly rendered HTML.
available: null instead of a potentially dangerous false positive. Consumers should treat null as "could not determine."auth0_sub metadata on the Stripe customer object, avoiding a separate mapping table.usage_log_daily) and per-minute (usage_log_minute) usage are recorded for monthly quota and per-minute rate limiting respectively.3abef70). The Zuplo portal/docs framework is still used for the developer portal./mcp is hosted on this server): Serves remote MCP clientsInstall the monitoring stack with: sudo bash scripts/setup-monitoring.sh
All active services on the host must be managed via systemd and restarted during deployments.
All automated deploys should route through scripts/deploy-host.sh, which is responsible for restarting whichever of these services are installed on the host.
Be the first to review this server!
by Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
by Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
by mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
by Microsoft · Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
by mcp-marketplace · Developer Tools
Scaffold, build, and publish TypeScript MCP servers to npm — conversationally
by Taylorwilsdon · Productivity
Control Gmail, Calendar, Docs, Sheets, Drive, and more from your AI