# Internet Names MCP Server An MCP server for checking availability of domain names, social media handles, and subreddits. Returns clean JSON responses suitable for programmatic use. ## Features - **Domain names** - Check availability via RDAP (free) or NameSilo API (free, but requires API key - responses include domain prices too) - **Social media handles** - Instagram, Twitter/X, Reddit, YouTube, TikTok, Twitch, Threads, Bluesky, GitHub, Snapchat, Pinterest, Kick, Substack - **Subreddits** - Check if subreddit names are available on Reddit - **Comprehensive search** - Generate name combinations and check everything at once ## Quick Start ### 1. Add to Claude Code (Recommended) ```bash claude mcp add --scope user internet-names-mcp uvx internet-names-mcp ``` That's it! The server works immediately using RDAP for domain lookups. ### 2. Optional: Configure NameSilo API (for domain pricing) Checking domain name availability works best if you configure a NameSilo API key. It's free and requires just a basic registration. Otherwise we fall back to using RDAP, which has some significant limitations (see below). ## Set up your NameSilo API key 1. Create an account at [namesilo.com](https://www.namesilo.com) (or log in) 2. Go to [API Manager](https://www.namesilo.com/account/api-manager) 3. Click **Generate New API Key** 4. Copy the key and run: ```bash uvx internet-names-mcp --setup ``` You'll be prompted to paste your API key. On macOS, your API key is stored safely in your iCloud or login keychain as `internet-names-mcp.namesilo`. On other platforms, it's stored in `~/.config/internet-names-mcp/config.json`. Using `--setup` is the preferred method as it stores the key securely. Alternatively, you can set it via environment variable (see Manual Configuration below). ## Manual Configuration If you're not using Claude Code, or prefer to configure MCP servers manually, add this to your MCP client's configuration file (e.g., `claude_desktop_config.json` for Claude Desktop): ```json { "mcpServers": { "internet-names-mcp": { "command": "uvx", "args": ["internet-names-mcp"] } } } ``` To include a NameSilo API key via environment variable (if you haven't used `--setup`): ```json { "mcpServers": { "internet-names-mcp": { "command": "uvx", "args": ["internet-names-mcp"], "env": { "NAMESILO_API_KEY": "your_api_key_here" } } } } ``` Note: Using `uvx internet-names-mcp --setup` to store the API key is preferred over environment variables, as it uses secure storage (macOS Keychain) when available. ## CLI Commands ```bash uvx internet-names-mcp --setup # Configure API keys interactively uvx internet-names-mcp --show-config # Show current configuration uvx internet-names-mcp --version # Show version uvx internet-names-mcp --help # Show help ``` ## Tools ### get_supported_socials() Returns list of supported social media platforms. **Response:** ```json { "platforms": ["instagram", "twitter", "reddit", "youtube", "tiktok", "twitch", "threads", "bluesky", "github", "snapchat", "pinterest", "kick", "substack", "subreddit"] } ``` Note: `subreddit` is checked via `check_subreddits()`, not `check_handles()`. --- ### check_domains(names, tlds?, method?, only_report_available?) Check domain name availability and pricing. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `names` | list[str] | required | Domain names or base names to check | | `tlds` | list[str] | `["com", "io", "ai", "co", "app", "dev", "net", "org"]` | TLDs to check | | `method` | str | `"auto"` | `"auto"`, `"rdap"`, or `"namesilo"` | | `only_report_available` | bool | `false` | If true, omit unavailable domains from response | If a name contains a dot, it's treated as a full domain. Otherwise, it's combined with each TLD. **Response:** ```json { "available": [ {"domain": "myapp.com", "price": 17.29}, {"domain": "myapp.io", "price": 34.99} ], "unavailable": ["myapp.ai"], "summary": { "cheapestAvailable": {"domain": "myapp.com", "price": 17.29}, "shortestAvailable": {"domain": "myapp.io", "price": 34.99} } } ``` --- ### check_handles(username, platforms?, only_report_available?) Check social media handle availability across platforms. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `username` | str | required | The username/handle to check | | `platforms` | list[str] | all platforms | Platforms to check | | `only_report_available` | bool | `false` | If true, omit unavailable handles from response | Supported platforms: `instagram`, `twitter`, `reddit`, `youtube`, `tiktok`, `twitch`, `threads`, `bluesky`, `github`, `snapchat`, `pinterest`, `kick`, `substack` All platforms are checked in parallel; a typical call takes 2–5 seconds. Instagram, Threads and Reddit use a headless Chromium browser (installed automatically on first use). How each platform is checked, and what "available" means: | Platform | Method | "Available" means | |----------|--------|-------------------| | `twitter` | X signup's username availability endpoint | X would let you register it | | `instagram` | Profile page, then Instagram signup's username validation | Instagram would let you register it | | `threads` | Follows Instagram (Threads handles are Instagram usernames); profile page for the URL | Instagram would let you register it | | `bluesky` | `com.atproto.temp.checkHandleAvailability` on bsky.social, for `.bsky.social` | Bluesky would let you register it | | `reddit` | Reddit's `username_available` endpoint | Reddit would let you register it | | `tiktok` | Profile page's embedded user status | No account found (banned or removed accounts look the same) | | `youtube` | `@handle` page status | No channel found (reserved or terminated handles may look the same) | | `twitch` | Twitch web GraphQL user lookup, including suspended and deleted accounts | No account found | | `github` | GitHub REST API user lookup (users and organizations; 60 lookups/hour without a token) | No account found (reserved names may look the same) | | `snapchat` | `snapchat.com/@username` page status | No account found (reserved names may look the same) | | `pinterest` | Profile page title, or the page's embedded "User not found" error | No account found (reserved names may look the same) | | `kick` | Kick public channel API, plus the hyphenated slug Kick gives some underscore usernames | No channel found (Kick does not publish length rules, so only characters are validated) | | `substack` | Whether `.substack.com` serves or redirects (publication subdomains, not Substack user handles) | No publication or reserved subdomain found | A platform is reported as available only when it positively says so. Block pages, rate limits and unexpected responses are reported as errors (in `unavailable`, with an `error` field), never as available or taken. **Response:** ```json { "available": ["instagram", "tiktok", "youtube"], "unavailable": [ {"platform": "twitter", "url": "https://x.com/myapp"}, {"platform": "reddit", "url": "https://www.reddit.com/user/myapp"}, {"platform": "bluesky", "note": "Reserved or not allowed by Bluesky"}, {"platform": "twitch", "error": "Twitch returned HTTP 503"} ] } ``` --- ### check_subreddits(names, only_report_available?) Check subreddit name availability on Reddit. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `names` | list[str] | required | Subreddit names to check (with or without r/ prefix) | | `only_report_available` | bool | `false` | If true, omit unavailable subreddits from response | **Response:** ```json { "available": ["mynewsubreddit"], "unavailable": [ {"name": "programming", "subscribers": 6835000}, {"name": "privatesubreddit", "note": "private"} ] } ``` Subreddits are looked up through Reddit's `about.json` inside a headless browser session. `note` carries Reddit's reason when a subreddit is held but not public (`private`, `gold_only`, `quarantined`, `banned`, ...), or explains why a name is not a valid subreddit name. --- ### check_everything(components, tlds?, platforms?, method?, require_all_tlds_available?, only_report_available?, also_include_hyphens?) Comprehensive check across domains and social media. Generates name combinations from components, checks domains first (fast), then checks social handles for names that pass the domain check. **Parameters:** | Name | Type | Default | Description | |------|------|---------|-------------| | `components` | list[str] | required | Name components to combine (e.g., `["red", "sweater"]`) | | `tlds` | list[str] | `["com", "net", "org", "io", "ai"]` | TLDs to check | | `platforms` | list[str] | all platforms | Social platforms to check | | `method` | str | `"auto"` | `"auto"`, `"rdap"`, or `"namesilo"` | | `require_all_tlds_available` | bool | `false` | If true, name must be available in ALL TLDs to qualify for handle checking | | `only_report_available` | bool | `false` | If true, omit unavailable items from response | | `also_include_hyphens` | bool | `false` | If true, also check domains for hyphenated versions (handles are not checked for these) | **Name Generation:** From components `["red", "sweater"]`, generates: - Single components: `red`, `sweater` - Concatenations: `redsweater`, `sweaterred` - With `also_include_hyphens`: `red-sweater`, `sweater-red` (domains only) Hyphenated versions can appear in `available_domains` and `domain_successful_basenames`, but are never checked for social handles, so they never appear in `available_handles`, `unavailable_handles` or `fully_available`. A hyphen typed into a component (e.g., `["red-sweater"]`) is the caller's own spelling and is checked for handles like any other name. **Response:** ```json { "available_domains": [ {"domain": "redsweater.com", "price": 17.29}, {"domain": "redsweater.io", "price": 34.99} ], "domain_successful_basenames": ["redsweater", "sweaterred"], "available_handles": { "redsweater": ["instagram", "twitter", "youtube"], "sweaterred": ["instagram", "tiktok"] }, "unavailable_handles": { "redsweater": [{"platform": "reddit", "url": "..."}] }, "summary": { "fully_available": ["sweaterred"], "cheapest_domain": {"domain": "redsweater.com", "price": 17.29} } } ``` The `fully_available` list contains names that are available on ALL checked platforms. ## Domain Lookup Methods | Method | Description | Pricing | Speed | |--------|-------------|---------|-------| | `auto` | Uses NameSilo if API key configured, otherwise RDAP | With NameSilo | Fast | | `rdap` | Direct registry queries via IANA bootstrap | No | Fast | | `namesilo` | NameSilo API (requires API key) | Yes | Fast | ### RDAP Limitations RDAP is free and requires no API key, but has some limitations: - **TLD coverage** - Not all top level domains (TLDs) have RDAP servers. Queries for unsupported TLDs will fail. RDAP works for .com, .net, .org, .app, .ai and more. See [deployment.rdap.org](https://deployment.rdap.org) for an up-to-date list (look for 'Yes' in the 'RDAP' column). - **No pricing** - RDAP only reports availability, not the cost to register the domain. - **False positives** - A domain may appear available via RDAP but actually be reserved or considered "premium" by registrars, making it effectively unavailable or prohibitively expensive to purchase. For reliable results with pricing, configure a NameSilo API key. ## Configuration API key storage: - **macOS:** Keychain (as `internet-names-mcp.namesilo`) - **Linux:** `~/.config/internet-names-mcp/config.json` - **Windows:** `%APPDATA%/internet-names-mcp/config.json` API key lookup order (first match wins): 1. macOS Keychain (on macOS only) 2. Environment variable (`NAMESILO_API_KEY`) 3. Config file (fallback) ## Development ### Local Setup The `devsetup.sh` script handles virtual environment creation and dependency installation: ```bash git clone https://github.com/drewster99/InternetNamesMCP.git InternetNamesMCP cd InternetNamesMCP source devsetup.sh # Creates .venv, activates it, installs dependencies playwright install chromium # Required for Instagram, Threads and Reddit checks (auto-installed on first use otherwise) ``` Options: - `source devsetup.sh` - Set up environment (default) - `source devsetup.sh --clean` - Delete venv and caches - `source devsetup.sh --clean --setup` - Clean rebuild ### API Key Configuration (Development) When running from source, use the module directly instead of `uvx`: ```bash source devsetup.sh # Activate environment first python -m internet_names_mcp --setup # Configure API keys interactively python -m internet_names_mcp --show-config # Show current configuration and key source python -m internet_names_mcp --version # Show version ``` ### Running Tests ```bash source devsetup.sh # Activate environment first # Main test suites python test_server.py # Full test suite - offline validation + online API tests python test_mcp_interface.py # Tests via MCP protocol (stdio transport) python test_rdap_client.py # Async RDAP client, rate limiter, batch queries # Comparison/diagnostic tests python test_methods.py # Compare RDAP vs NameSilo results for discrepancies python test_rdap.py # Quick RDAP-only domain check ``` | Test File | Description | |-----------|-------------| | `test_server.py` | Main test suite covering all MCP tools, edge cases, and API calls | | `test_mcp_interface.py` | Tests the server through actual MCP protocol via stdio | | `test_rdap_client.py` | Tests async RDAP client, rate limiting, and batch queries | | `test_methods.py` | Compares RDAP vs NameSilo to detect availability discrepancies | | `test_rdap.py` | Simple RDAP-only test for quick domain availability checks | ### Interactive Testing Use MCP Inspector for interactive debugging with a web UI: ```bash npx @modelcontextprotocol/inspector .venv/bin/python -m internet_names_mcp ``` ### Project Structure ``` ├── src/internet_names_mcp/ │ ├── __init__.py # CLI entry point │ ├── __main__.py # Module runner │ ├── server.py # MCP server │ ├── config.py # Configuration management │ ├── rdap_bootstrap.py # RDAP bootstrap cache │ └── rdap_client.py # Async RDAP client ├── pyproject.toml # Package configuration └── README.md ``` ### Data Files **RDAP Bootstrap Cache** - Maps TLDs to their authoritative RDAP servers (auto-downloaded from IANA): - **macOS/Linux:** `~/.cache/internet-names-mcp/rdap_bootstrap.json` - **Windows:** `%APPDATA%/internet-names-mcp/rdap_bootstrap.json` The cache is automatically refreshed when expired (default 24h TTL from IANA's Cache-Control headers). ## Troubleshooting ### "playwright not installed" or Chromium errors Install Playwright browser: ```bash playwright install chromium ``` Or with uvx: ```bash uvx --from playwright install chromium ``` ### "Instagram is throttling username checks" Instagram limits how many signup username validations one IP address can make. While throttled it answers "not available" for every name; the server detects this with a random control name and reports an error (noting whether a profile exists) instead of a wrong answer. In testing the throttle tripped after roughly 20–40 checks from one IP address and cleared within about 25 minutes. Taken names are settled from the profile page and do not count toward it. ### Reddit errors ("bot-check page" or "without data") Reddit blocks non-browser clients and rate-limits by IP address. The server passes Reddit's JavaScript check in a headless browser and keeps the resulting Reddit cookies in memory (never on disk) for the life of the server process, because clearing a fresh challenge on every call makes Reddit escalate to a CAPTCHA. If Reddit still asks for a CAPTCHA, the error says so; the server never attempts to solve it. Wait a while and retry. ## Copyright Copyright (C) 2026 Nuclear Cyborg Corp ## License [MIT](license.md)