Unphurl
by 123Ergo · remote · local (npm) · no sign-in to list tools · v0.2.3 · published by GitHub user 123Ergo
URL intelligence for AI agents and developers. 16 tools, 25 signal weights, 20 free checks.
18 tools: 2 can delete or cancel things, 2 do something that can't be undone, 7 change things and 7 only read.
Scanned Oct 21, 2026
- Deletes or cancels2Claude Code: deny: see these tools
- Can't be undone2Claude Code: ask: see these tools
- Changes things7Claude Code: ask: see these tools
- Only reads7Claude Code: allow: see these tools
1 check matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.
claude mcp add --transport http unphurl https://mcp.unphurl.com/mcpFindings
Matches of fixed checks, with the text they matched.
The server marks "remove_from_allowlist" not destructive (destructiveHint: false), and its own name, description or input give the class "destructive".
What this check looks forMatched text
annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "remove_from_allowlist" says "remove": a word for removing or ending something.
Tools
Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.
18 tools
Deletes or cancels 2Claude Code: deny
delete_profileDelete a custom scoring profile.No findings
Why it is in this group
- The name "delete_profile" says "delete": a word for removing or ending something.
- The server marks it destructive (destructiveHint: true).
Description, as the server wrote it Delete a custom scoring profile. This is permanent. Any future check requests using this profile name will fall back to default weights. Use list_profiles to see your current profiles before deleting.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: true
- idempotentHint: true
- openWorldHint: false
remove_from_allowlistRemove one or more domains from this account's trusted allowlist.1 finding
Why it is in this group
- The name "remove_from_allowlist" says "remove": a word for removing or ending something.
- The server marks it not destructive (destructiveHint: false), but an annotation never lowers a class.
Findings
- Warn
The server marks "remove_from_allowlist" not destructive (destructiveHint: false), and its own name, description or input give the class "destructive".
What this check looks forMatched text
annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "remove_from_allowlist" says "remove": a word for removing or ending something.
Description, as the server wrote it Remove one or more domains from this account's trusted allowlist. Once removed, those domains resume normal scoring on the next check. Use list_allowlist to see what is currently on the list before removing.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
Can't be undone 2Claude Code: ask
purchasePurchase pipeline check credits.No findings
Why it is in this group
- The name "purchase" says "purchase": a word that usually means money moves, which cannot be taken back.
Description, as the server wrote it Purchase pipeline check credits. Returns a Stripe Checkout URL that the user must open in a browser to complete payment. The AI cannot complete the payment. Tell the user to open the URL in their browser, complete the Stripe checkout, and then confirm they've paid. Credits are added to the account automatically once Stripe confirms payment. After purchase, use get_balance to verify credits have been added.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: false
- openWorldHint: false
resend_verificationResend the email verification link for an existing Unphurl account.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "resend_verification" says "resend": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "resend_verification" says "resend": it sends or publishes something to other people.
Description, as the server wrote it Resend the email verification link for an existing Unphurl account. Use this when a user signed up but their verification link expired (links are valid for 24 hours) and they need a new one. The user's API key won't work until their email is verified. For security, the response is always the same regardless of whether the email exists, is already verified, or was rate limited. This prevents account enumeration. Rate limited to 3 requests per email per hour. This tool does not require an API key.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: true
- openWorldHint: true
Changes things 7Claude Code: ask
add_to_allowlistAdd one or more domains to this account's trusted allowlist.No findings
Why it is in this group
- The name "add_to_allowlist" says "add": a word for a change.
Description, as the server wrote it Add one or more domains to this account's trusted allowlist. Allowlisted domains suppress the compound signal and brand impersonation floor in scoring. The full pipeline still runs — all signals remain visible so you can monitor trusted domains for SSL expiry, parking, or other changes. Submit the registrable domain only (e.g. partnerco.com). Subdomains and full URLs are rejected. Adding partnerco.com covers sub.partnerco.com and all other subdomains automatically. Maximum 1,000 domains per account. Maximum 100 domains per request. Duplicates are silently skipped.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
check_urlCheck a single URL for security and data quality signals.Can send data outNo findings
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
What it can reach
- outboundThe input takes a URL (input.url): a request to a URL can carry data to whoever runs it.
Description, as the server wrote it Check a single URL for security and data quality signals. Returns a risk score (0-100), detailed signal breakdown, and metadata. Unphurl analyses URLs across seven dimensions: redirect behaviour, brand impersonation, domain intelligence (age, registrar, expiration, status codes, nameservers via RDAP), SSL/TLS validity, parked domain detection, URL structural analysis (length, path depth, subdomain count, entropy), and DNS enrichment (MX records). The score is calculated from these signals using either default weights or a custom scoring profile. Higher scores mean more suspicious. The score is a signal, not a verdict. You decide the threshold based on the use case. Billing: Most lookups are free. Known domains (Tranco Top 100K like google.com, github.com) return instantly with score 0 at no cost. Previously analysed domains return cached signals at no cost. Only unknown domains that run through the full analysis pipeline cost 1 pipeline check credit. The response's meta.pipeline_check_charged field tells you whether this check consumed a credit. Use the "profile" parameter to score results with custom weights. For example, a "cold-email" profile might weight parked domains heavily while ignoring brand impersonation. Use list_profiles to see available profiles, or show_defaults to see all signal weights. If the account has zero credits and the URL requires a full pipeline check, returns a 402 error with a link to purchase more credits.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
check_urlsCheck multiple URLs in a single batch.Can send data outNo findings
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
What it can reach
- outboundThe input takes a URL (input.urls): a request to a URL can carry data to whoever runs it.
Description, as the server wrote it Check multiple URLs in a single batch. Returns results for all URLs, handling async processing automatically. Each URL is analysed across seven dimensions: redirect behaviour, brand impersonation, domain intelligence (age, registrar, expiration, status codes, nameservers via RDAP), SSL/TLS validity, parked domain detection, URL structural analysis, and DNS enrichment. Known and cached URLs return results immediately. Unknown URLs are queued for pipeline processing. This tool automatically polls for results until all URLs are complete or the 5-minute timeout is reached. You don't need to manage polling or job tracking. If the timeout is reached before all results are complete, returns whatever is available with a clear message indicating which URLs are still processing. The user can check results later via check_history. Maximum 500 URLs per call. For larger datasets, call this tool multiple times with chunks of up to 500 URLs. Billing: Same as check_url. Known and cached domains are free. Only unknown domains running through the full pipeline cost 1 credit each. The summary shows pipeline_checks_charged (the actual number of credits consumed). If you don't have enough credits for the unknowns in the batch, the entire batch is rejected with a 402 error telling you exactly how many credits are needed. Duplicate URLs in the list are automatically deduplicated (processed once, charged once). Invalid URLs get individual error status without rejecting the batch. Use the "profile" parameter to score all results with custom weights.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
create_profileCreate or update a custom scoring profile.No findings
Why it is in this group
- The name "create_profile" says "create": a word for a change.
Description, as the server wrote it Create or update a custom scoring profile. Profiles are sparse overrides: only specify the weights you want to change. Everything else keeps its default value. If a profile with this name already exists, it is updated with the new weights (full replacement, not merge). Weights are points, not percentages. Each weight is the number of points that signal adds to the score when it fires. They don't need to total 100. A profile with weights totalling 90 is conservative (max possible score is 90). A profile with weights totalling 130 is aggressive (multiple signals quickly push to the cap of 100). The threshold the agent sets for action matters more than the weight totals. Use show_defaults to see all 25 signals with their default weights and descriptions before creating a profile. Use check_url or check_urls with the "profile" parameter to score results with this profile. Maximum 20 profiles per account. Profile name "default" is reserved. Common profiles: - Cold email: weight parked (30), chain_incomplete (25), ssl_invalid (15) higher. Lower brand_impersonation (10). - Security bot: keep brand_impersonation high (40), increase domain_age_7 (30), redirects_5 (25). - Lead gen: weight parked (35), http_only (20), chain_incomplete (20) for dead business detection. - SEO audit: weight redirects_5 (30), chain_incomplete (30), parked (25) for link quality. See the Unphurl API documentation for all 19 use case weight examples.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
estimate_urlsPre-flight credit estimator for a list of URLs.Can send data outNo findings
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
What it can reach
- outboundThe input takes a URL (input.urls): a request to a URL can carry data to whoever runs it.
Description, as the server wrote it Pre-flight credit estimator for a list of URLs. Returns counts + credit estimate. Free, no credits consumed. Call this before check_urls to show the user how many credits the batch will cost. Classifies each URL against three free gates: - tranco: URL's registrable domain is in the Tranco top 100K (trusted, treated as clean with score 0, no pipeline needed) - cached: URL's hostname is already in Unphurl's reputation cache (results available, no pipeline needed) - unknown: URL needs full pipeline analysis (costs 1 credit per URL) Returns counts for each gate plus total, credits_needed, credits_min, and credits_max. credits_min and credits_max are both equal to the unknown count in the current implementation. Maximum 500 URLs per call. Rate limit: 10 requests per minute. Does not follow redirects; classifies each URL as submitted. Typical agent flow: 1. Collect a list of URLs 2. Call estimate_urls to get the cost breakdown 3. Show the user the breakdown and ask for approval 4. On approval, call check_urls on the unknowns only
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
generate_pdfGenerate a branded PDF audit report.No findings
Why it is in this group
- The name "generate_pdf" says "generate": a word for a change.
Description, as the server wrote it Generate a branded PDF audit report. Pass the complete audit JSON object and your reseller details. Returns a 24-hour download link.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: false
- openWorldHint: false
signupCreate a new Unphurl account.Reads text others writeNo findings
Why it is in this group
- The description's first verb is "create": a word for a change.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Create a new Unphurl account. Returns an API key (shown once, store it securely). After signup, the user must check their email and click the verification link. The API key won't work for URL checks until the email is verified. Verification link expires after 24 hours. If the link expires, use the "resend_verification" tool to request a new one. The account starts with 20 free pipeline check credits so the user can test with real URLs. Known domain lookups (google.com, github.com, etc.) and cached domain lookups are always free. To check more unknown domains through the full analysis pipeline, the user can purchase credits via the "purchase" tool. Once the user has their API key, they need to add it to their MCP server configuration as UNPHURL_API_KEY. This tool does not require an API key.
Annotations the server declares
- readOnlyHint: false
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
Only reads 7Claude Code: allow
check_historyView recent URL check history.Reads text others writeNo findings
Why it is in this group
- The description's first verb is "view": a word for reading.
What it can reach
- untrusted-inputThe description's first sentence says "view" and "url": it reads web pages, which anyone can write.
Description, as the server wrote it View recent URL check history. Shows what URLs have been checked, their scores, phishing status, and whether each check was free or used a pipeline credit. Results are paginated. Use page and limit parameters to navigate. Default is 20 results per page, maximum 100. History is retained for 90 days. Account-level stats (total credits, balance) never expire.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
get_balanceCheck your pipeline check credit balance.No findings
Why it is in this group
- The name "get_balance" says "get": a word for reading.
Description, as the server wrote it Check your pipeline check credit balance. Shows credits remaining, total purchased, total used, and lifetime free lookups count. Credits are consumed only when unknown domains run through the full analysis pipeline. Known domains (Tranco Top 100K) and cached domains (previously analysed by any Unphurl customer) are always free. If credits_remaining is 0, you can still check known and cached domains for free. To check unknown domains, purchase more credits using the "purchase" tool.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
get_pricingShow available pipeline check credit packages and pricing.No findings
Why it is in this group
- The name "get_pricing" says "get": a word for reading.
Description, as the server wrote it Show available pipeline check credit packages and pricing. Returns all packages with credit counts and prices. Packages (one-time purchase, no subscription): - Starter: 100 credits for $9 ($0.09 each) - Standard: 500 credits for $39 ($0.078 each) - Pro: 2,000 credits for $99 ($0.0495 each) - Scale: 10,000 credits for $399 ($0.0399 each) Most URL lookups are free (known domains and cached domains). Credits are only consumed when an unknown domain runs through the full analysis pipeline. In typical use, 95-99% of URLs resolve free. This tool does not require an API key.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
get_statsView your account usage statistics.No findings
Why it is in this group
- The name "get_stats" says "get": a word for reading.
Description, as the server wrote it View your account usage statistics. Shows total URLs submitted, breakdown by gate (Tranco lookups, cache lookups, pipeline checks), free rate percentage, score threshold counts, and credit balance. Use this to understand your usage patterns: how many of your checks resolved free (known or cached domains) vs paid pipeline checks, and how many URLs scored above key thresholds. This is useful for: - Checking if your scoring profile is flagging the right proportion of URLs - Understanding your cost efficiency (higher free rate = more value per credit) - Reporting usage metrics
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
list_allowlistList all domains on this account's trusted allowlist.No findings
Why it is in this group
- The name "list_allowlist" says "list": a word for reading.
Description, as the server wrote it List all domains on this account's trusted allowlist. Allowlisted domains suppress the compound signal and brand impersonation floor in scoring. The full pipeline still runs — all signals remain visible for monitoring. Use this to see which domains are currently trusted. Returns the list of domains, current count, and the 1,000-domain limit.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
list_profilesList all custom scoring profiles on this account.No findings
Why it is in this group
- The name "list_profiles" says "list": a word for reading.
Description, as the server wrote it List all custom scoring profiles on this account. Returns profile names and their custom weight overrides. Profiles are named weight sets that change how Unphurl scores URLs. Different use cases need different scoring. A cold email agent cares about dead domains. A security bot cares about phishing. Profiles let one account serve multiple use cases. Profiles only override specific weights. Any signal not specified in a profile uses the default weight. Use show_defaults to see all 25 signals and their default weights.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
show_defaultsShow all 25 scoring signals with their default weights and descriptions.No findings
Why it is in this group
- The name "show_defaults" says "show": a word for reading.
Description, as the server wrote it Show all 25 scoring signals with their default weights and descriptions. This is the baseline scoring that applies when no custom profile is specified. Use this to understand what each signal means and how much it contributes to the score before creating custom profiles. Profiles are sparse overrides on top of these defaults. This tool does not require an API key. The defaults are hardcoded and always available.
Annotations the server declares
- readOnlyHint: true
- destructiveHint: false
- idempotentHint: true
- openWorldHint: false
This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.
Exfil surface
Which tools read private data, which read text someone else can write, and which can send data out. One of each is a path a prompt injection can use.
No private-data, untrusted-input, outbound path in this server alone: no tool is tagged private-data.
- Reads private data0
- Reads files, mail, messages, databases, secrets, calendars or repositories.
- Reads text others write3
- Brings in text someone else can write: web pages, inbound email, messages, issues and comments.
- Can send data out4
- Can carry data to people or systems: it sends, posts or publishes, or takes a recipient, channel or URL.
Other servers in the same agent add their own tools to these paths. Audit your whole setup to see them together.
Remote posture
What anyone on the internet sees at the remote endpoint: an anonymous initialize and tools/list, and the public OAuth metadata.
Looked at Oct 22, 2026, 14:50 UTC, unchanged since Oct 22, 2026. No credentials were sent and no tool was called. What is looked at.
- Reachable
- yes
- HTTP status of an anonymous initialize
- 200
- tools/list served without credentials
- yes
- Tools that change things, listed without credentials
- 11
- Protocol version
- 2025-03-26
- WWW-Authenticate on 401
- not observed
- resource_metadata in WWW-Authenticate
- none
- RFC 9728 protected resource metadata
- https://mcp.unphurl.com/.well-known/oauth-protected-resource/mcp
- Authorization servers
- https://mcp.unphurl.com
- Legacy HTTP+SSE transport
- no
- Dynamic client registration
- yes
- Client ID metadata documents
- no
- HighThe whole server
The endpoint served tools/list without credentials, and the list has 11 tools that change things (2 destructive, 2 irreversible, 7 write). Whether a call also works without credentials was not tested: tools are never called.
What this check looks forMatched text
https://mcp.unphurl.com/mcp: tools/list returned 18 tools to an anonymous client, including delete_profile (destructive), remove_from_allowlist (destructive), resend_verification (irreversible), purchase (irreversible), signup (write) and 6 more
Install
Pinned to version 0.2.3. Values in angle brackets are yours to fill in.
claude mcp add --transport http unphurl https://mcp.unphurl.com/mcpRun in a terminal.
Claude Code permissions
Allow the reads, ask before a change or a way out, deny the deletes. Paste into .claude/settings.json.
{
"permissions": {
"allow": [
"mcp__unphurl__list_profiles",
"mcp__unphurl__show_defaults",
"mcp__unphurl__get_balance",
"mcp__unphurl__get_pricing",
"mcp__unphurl__check_history",
"mcp__unphurl__get_stats",
"mcp__unphurl__list_allowlist"
],
"ask": [
"mcp__unphurl__signup",
"mcp__unphurl__resend_verification",
"mcp__unphurl__check_url",
"mcp__unphurl__check_urls",
"mcp__unphurl__estimate_urls",
"mcp__unphurl__create_profile",
"mcp__unphurl__purchase",
"mcp__unphurl__add_to_allowlist",
"mcp__unphurl__generate_pdf"
],
"deny": [
"mcp__unphurl__delete_profile",
"mcp__unphurl__remove_from_allowlist"
]
}
}Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. A read that can send data out (it takes a URL, say) asks first too. It assumes the server is installed as unphurl. Review it before you use it.
Capability tags behind 6 rules
- mcp__unphurl__signup untrusted-input
- mcp__unphurl__resend_verification untrusted-input, outbound
- mcp__unphurl__check_url outbound
- mcp__unphurl__check_urls outbound
- mcp__unphurl__estimate_urls outbound
- mcp__unphurl__check_history untrusted-input
