A side-by-side look at the Sonarqube MCP Server and Urldna MCP MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Sonarqube MCP Server for security work over a hosted endpoint; choose Urldna MCP for security over a hosted endpoint. You can also run both side by side in one mcpServers block.
SONARQUBE_TOKEN, SONARQUBE_ORG, SONARQUBE_URL, SONARQUBE_PROJECT_KEY.analyze_code_snippet, projectKey, filePath.new_scan, get_scan, fast_check.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Security | Security |
| Quality signal | 59/100 · Good | 60/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Remote HTTP/SSE | Remote HTTP/SSE |
| Auth | Not declared | API key required |
| Pricing | Not declared | BYOK (pay the provider) |
| Required env vars | SONARQUBE_TOKENSONARQUBE_ORGSONARQUBE_URLSONARQUBE_PROJECT_KEY | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 664 | 7 |
| Directory activity | 1 views 0 installs 0 upvotes | 4 views 0 installs 0 upvotes |
| Tools | 80 | 14 |
| Full listing | View Sonarqube MCP Server | View Urldna MCP |
analyze_code_snippetAnalyze file content with SonarQube analyzers to identify code quality and security issues. Always analyzes the complete file content for accuracy. Optionally filter results to a specific code snippet.projectKeyThe SonarQube project key - _Required String_ _(Ignored when `SONARQUBE_PROJECT_KEY` is defined)_filePathProject-relative path of the file to analyze (e.g., `src/main/java/MyClass.java`). Used when the workspace is mounted at `/app/mcp-workspace` - _String_fileContentComplete file content as a string. Required when workspace is not mounted - _String_codeSnippetCode snippet to filter issues (must match content in fileContent) - _String_languageLanguage of the code (e.g., 'java', 'python', 'js', 'ts', 'tsx', 'jsx') - _String_scopeScope of the file: MAIN or TEST (default: MAIN) - _String_analyze_file_listAnalyze files in the current working directory using SonarQube for IDE. This tool connects to a running SonarQube for IDE instance to perform code quality analysis on a list of files.file_absolute_pathsList of absolute file paths to analyze - _Required String[]_toggle_automatic_analysisEnable or disable SonarQube for IDE automatic analysis. When enabled, SonarQube for IDE will automatically analyze files as they are modified in the working directory. When disabled, automatic analysis is turned off.enabledEnable or disable the automatic analysis - _Required Boolean_run_advanced_code_analysisRun Vortex analysis on a single file. Organization is inferred from MCP configuration (SonarQube Server uses the nil UUID placeholder).new_scanSubmit a URL to urlDNA for a full scan and wait for the result.
This tool performs a complete website scan: it submits the URL, then polls
the API until the scan finishes (status: DONE) or fails (status: ERROR).
The result is automatically truncated to fit within the model's context window.
The URL is automatically normalized: if no scheme is provided (e.g., "example.com"),
"https://" is prepended automatically.
Polling behavior:
- Checks every 2 seconds
- Maximum 30 retries (up to ~60 seconds)
- Raises an error if the scan does not complete in time
Args:
url (str): URL to submit for scanning. Can be provided with or without the "https://" prefix.
Examples: "https://example.com", "example.com", "www.suspicious-site.net"
Returns:
dict: Truncated scan result JSON from urlDNA, including page metadata,
technologies, HTTP transactions, threat classification, and more.
Raises:
RuntimeError: If submission fails, polling fails, or the scan does not complete successfully.get_scanRetrieve the full results of a previously submitted urlDNA scan by its ID.
Use this tool when you already have a scan ID (e.g., from new_scan or search results)
and want to fetch the detailed scan report.
Args:
scan_id (str): The unique identifier of the scan (e.g., "660d0abc123...").
Obtained from new_scan, fast_check, or search results.
Returns:
dict: Truncated scan result JSON including page metadata, threat classification,
technologies, certificates, network info, and more.
Raises:
RuntimeError: If API key retrieval or the HTTP request fails.fast_checkQuickly check if a URL has already been scanned in the urlDNA database.
This is a lightweight lookup — it does not submit a new scan. Use this
to avoid redundant scans and get instant results if the URL was previously analyzed.
The URL is automatically normalized: if no scheme is provided (e.g., "example.com"),
"https://" is prepended automatically.
Args:
url (str): URL to check. Can be provided with or without the "https://" prefix.
Examples: "https://example.com", "example.com", "www.phishing-site.net"
Returns:
dict: Fast check result from urlDNA, including scan_id if a match is found.
Raises:
RuntimeError: If the API key retrieval or the HTTP request fails.searchSearch urlDNA scans using CQL (Custom Query Language) syntax.
Returns matching scans sorted by submission date descending (newest first).
Page 1 is available to all users. Pages beyond the first require a PREMIUM subscription.
--- SEARCHABLE FIELDS ---
id : Scan ID (e.g., 660d0...)
domain : Scan domain name (e.g., "google.com")
submitted_url : Submitted URL for the scan (e.g., "https://www.google.com/search")
target_url : Final redirected URL after following all redirects
redirect_url : Redirect chain URL
nsfw : Flag for adult content (true/false)
device : Device used for the scan (MOBILE/DESKTOP)
user_agent : Web browser user agent string
http_referer : URL of the page that made the request
scanned_from : Country from which the scan was initiated
submitter_tag : User-defined submitter tags
origin : Source of the scan (USER/API)
protocol : Protocol used (HTTP/HTTPS)
title : Page title
text : Full page text (PREMIUM users only)
ip : IP address used to access the website
org : Organization associated with the IP
isp : Internet Service Provider for the IP
asn : Autonomous System Number associated with the IP
city : City associated with the IP
country_code : Country code associated with the IP (e.g., "IT", "US")
http_transaction : HTTP transaction URL
outgoing_link : Page outgoing link
favicon : Perceptual hash of the website's favicon image
screenshot : Hash of the website's screenshot
registrar : Whois registrar attribute
serial_number : Certificate serial number (for SSL certificates)
issuer : Certificate issuer (for SSL certificates)
subject : Certificate subject (for SSL certificates)
malicious : Flag for malicious website (true/false)
threat : Detected threat classification
technology : Technologies used by the website
cookie_name : Name of a specific cookie found on the website
cookie_value : Value of a specific cookie found on the website
category : Page category (e.g., "Security") (PREMIUM users only)
language : Detected page language (e.g., "English") (PREMIUM users only)
topic : Page topics (e.g., "phishing detection") (PREMIUM users only)
--- OPERATORS ---
= Exact match → domain = google.com
!= Exclude exact value → domain != facebook.com
LIKE Partial/wildcard match → title LIKE Login
!LIKE Exclude pattern → domain !LIKE amazon
Combine multiple filters using AND (both must be true) or OR (at least one must be true):
Combine multiple filters using AND or OR:
domain = www.google.com AND title LIKE search
domain = google.com OR domain = youtube.com
--- EXAMPLES ---
CORRECT (Partial search):
query="domain LIKE google"
CORRECT (Exact search):
query="domain = www.google.com"
WRONG (Will fail to find subdomains):
query="domain = google"
Find scans from mobile devices in either Italy or Spain:
query="device = MOBILE AND country_code = IT OR country_code = ES"
Find malicious scans using either WordPress or Shopify:
query="malicious = true AND technology LIKE wordpress OR technology LIKE shopify"
Find scans matching a specific favicon hash:
query="favicon LIKE d417e43"
Find scans with a specific domain and page title pattern:
query="domain = www.google.com AND title LIKE search"
Find scans for either of two domains:
query="domain = google.com OR domain = youtube.com"
Find non-NSFW scans submitted via API from Germany:
query="nsfw = false AND origin = API AND country_code = DE"
Get second page of results (PREMIUM only):
search(query="malicious = true", page=2)
Args:
query (str): Search expression in urlDNA CQL syntax.
Combine multiple conditions with AND or OR.
Combine multiple conditions with AND or OR.
page (int, optional): Page number for pagination (1-indexed). Default is 1.
Pages beyond page 1 require a PREMIUM subscription.
Returns:
list[dict]: Array of Scan objects matching the query, sorted by submission date
descending. Each scan includes submitted_url, domain, status, device,
malicious flag, country, and more.
Raises:
RuntimeError: If the search request fails or the API key is missing.list_queriesList all saved queries for the authenticated user.
Saved queries are reusable filter combinations that can be executed against the
urlDNA scan database. Each query has a name, a unique ID, and one or more filter
conditions that are combined with logical AND.
Use this to discover existing queries before creating new ones or to retrieve
query IDs needed for get_query, query_scans, update_query, or delete_query.
Returns:
list[dict]: Array of Query objects, each containing:
- id (str): Unique query identifier.
- name (str): User-defined name.
- query_filters (list): Array of filter conditions (attribute, operator, value).
Raises:
RuntimeError: If the API key retrieval or HTTP request fails.get_queryRetrieve the full details of a specific saved query by its ID.
Returns the query name and all filter conditions configured for it.
Use list_queries first if you don't know the query ID.
Args:
query_id (str): Unique identifier of the saved query (e.g., "abc123...").
Returns:
dict: A Query object containing:
- id (str): Unique query identifier.
- name (str): User-defined name.
- query_filters (list): Array of filter conditions, each with:
- attribute (str): The scan attribute to filter on.
- operator (str): Comparison operator (=, !=, LIKE, !LIKE).
- value (str): The value to match against.
- logical_operator (str): Logical operator to combine with the next filter (AND, OR).
Raises:
RuntimeError: If the query ID is not found or the request fails.query_scansExecute a saved query and return all matching scans.
Runs all filter conditions stored in the query against the urlDNA database
and returns matching scans sorted by submission date (newest first).
Pages beyond the first require a PREMIUM subscription.
Args:
query_id (str): Unique identifier of the saved query to execute.
page (int, optional): Page number for pagination (1-indexed). Default is 1.
Pages > 1 require PREMIUM subscription.
Returns:
list[dict]: Array of Scan objects matching the query filters, sorted by
submission date descending. Each scan includes metadata such as
submitted_url, domain, status, device, malicious flag, etc.
Raises:
RuntimeError: If the query ID is not found or the request fails.create_queryCreate a new saved query with one or more filter conditions.
Saved queries allow reusable, complex searches across the urlDNA database.
Requires a PREMIUM subscription.
--- QUERY FILTER STRUCTURE ---
Each filter in query_filters must be a dict.
- The FIRST filter (index 0) requires: attribute, operator, value.
- SUBSEQUENT filters (index 1+) require: attribute, operator, value, AND logical_operator.
The 'logical_operator' defines how the current filter connects to the
filters defined before it.
--- VALID ATTRIBUTES ---
id, domain, ip, submitted_url, target_url, scanned_from, user_agent, nsfw,
device, origin, asn, org, protocol, title, city, country_code, isp, favicon,
screenshot, issuer, serial_number, subject, malicious, technology, cookie_name,
http_referer, cookie_value, http_transaction, outgoing_link, submitter_tag,
registrar, category, topic, language, text, redirect_url, threat
--- VALID OPERATORS ---
= Exact match (e.g., malicious = true)
!= Exclude exact value (e.g., device != MOBILE)
LIKE Partial/wildcard match (e.g., technology LIKE wordpress)
!LIKE Exclude pattern (e.g., domain !LIKE amazon)
--- EXAMPLES ---
1. Find malicious scans where the title is 'login' OR 'register':
query_filters = [
{
"attribute": "malicious",
"operator": "=",
"value": "true"
},
{
"attribute": "title",
"operator": "LIKE",
"value": "login",
"logical_operator": "AND"
},
{
"attribute": "title",
"operator": "LIKE",
"value": "register",
"logical_operator": "OR"
}
]
2. Find scans using WordPress, but NOT from facebook.com:
query_filters = [
{
"attribute": "technology",
"operator": "LIKE",
"value": "wordpress"
},
{
"attribute": "domain",
"operator": "!=",
"value": "facebook.com",
"logical_operator": "AND"
}
]
Args:
name (str): Descriptive name for the saved query (e.g., "Login Phish Search").
query_filters (list[dict]): Array of filter conditions.
- attribute (str): Scan field to filter on.
- operator (str): One of =, !=, LIKE, !LIKE.
- value (str): Value to match against the attribute.
- logical_operator (str, optional): Required for all filters except the first.
Must be 'AND' or 'OR'.
Returns:
list[dict]: Array of all user Query objects after creation.
Raises:
ValueError: If any filter is missing required fields, has invalid values,
or if a logical_operator is missing on chained filters.
RuntimeError: If the API key retrieval or HTTP request fails.update_queryUpdate an existing saved query's name and/or filter conditions.
Replaces ALL previous filter conditions with the new provided list.
Partial updates are not supported — always provide the full desired filter set.
Requires a PREMIUM subscription.
--- QUERY FILTER STRUCTURE ---
Each filter in query_filters must be a dict.
- The FIRST filter (index 0) requires: attribute, operator, value.
- SUBSEQUENT filters (index 1+) require: attribute, operator, value, AND logical_operator.
The 'logical_operator' defines how the current filter connects to the
filters defined before it.
--- VALID ATTRIBUTES ---
id, domain, ip, submitted_url, target_url, scanned_from, user_agent, nsfw,
device, origin, asn, org, protocol, title, city, country_code, isp, favicon,
screenshot, issuer, serial_number, subject, malicious, technology, cookie_name,
http_referer, cookie_value, http_transaction, outgoing_link, submitter_tag,
registrar, category, topic, language, text, redirect_url, threat
--- VALID OPERATORS ---
= Exact match (e.g., malicious = true)
!= Exclude exact value (e.g., device != MOBILE)
LIKE Partial/wildcard match (e.g., technology LIKE wordpress)
!LIKE Exclude pattern (e.g., domain !LIKE amazon)
--- EXAMPLE: UPDATING TO MULTIPLE FILTERS ---
To update a query to find (Malicious AND Title LIKE 'login') OR (Title LIKE 'register'):
query_filters = [
{
"attribute": "malicious",
"operator": "=",
"value": "true"
},
{
"attribute": "title",
"operator": "LIKE",
"value": "login",
"logical_operator": "AND"
},
{
"attribute": "title",
"operator": "LIKE",
"value": "register",
"logical_operator": "OR"
}
]
Args:
query_id (str): Unique identifier of the query to update.
name (str): New name for the query.
query_filters (list[dict]): Full replacement filter list. Each dict requires:
- attribute (str): Scan field to filter on.
- operator (str): One of =, !=, LIKE, !LIKE.
- value (str): Value to match against the attribute.
- logical_operator (str, optional): Required for all filters except the first.
Must be 'AND' or 'OR'.
Returns:
dict: The updated Query object.
Raises:
ValueError: If any filter is missing required fields, has invalid values,
or if a logical_operator is missing on chained filters.
RuntimeError: If the query ID is not found or the request fails.delete_queryPermanently delete a saved query by its ID.
This operation is irreversible — the query and all its filter conditions
are permanently removed. The scans matched by the query are not affected.
Use list_queries to confirm the query ID before deleting.
Args:
query_id (str): Unique identifier of the query to delete.
Returns:
dict: Confirmation response from the API.
Raises:
RuntimeError: If the query ID is not found or the request fails.list_brandsList all brands available to the authenticated user for threat monitoring.
Brands represent organizations or entities whose domains and assets are tracked
in the urlDNA database. Use brands to monitor phishing attempts, brand impersonation,
and suspicious lookalike domains. Requires PREMIUM subscription.
--- FILTER OPTIONS ---
ALL : All brands accessible to the user (default if omitted).
FREE : Public brands available to all users.
PREMIUM : Premium-tier brands with advanced monitoring.
USER_BRANDS : Brands created by the authenticated user.
--- EXAMPLES ---
List all brands:
list_brands()
Search for brands containing "google":
list_brands(query="google")
List only user-created brands:
list_brands(filter="USER_BRANDS")
Search public brands with pagination:
list_brands(filter="FREE", query="microsoft", page=2)
Args:
filter (str, optional): Visibility filter for brands.
One of: ALL, FREE, PREMIUM, USER_BRANDS. Default: returns all accessible brands.
query (str, optional): Search term for filtering brands by name (case-insensitive partial match).
Example: "google" will match "Google LLC", "Google Cloud", etc.
page (int, optional): Page number for pagination (1-indexed). Default is 1.
Pages > 1 require PREMIUM subscription.
Returns:
list[dict]: Array of Brand objects, each containing:
- id (str): Unique brand identifier.
- name (str): Brand name.
- private_brand (bool): True if only visible to the creating user.
Raises:
ValueError: If an invalid filter value is provided.
RuntimeError: If the API key retrieval or HTTP request fails.get_brandRetrieve the full details of a specific brand by its ID.
Returns complete brand metadata including configuration and visibility settings.
Use list_brands first if you need to look up a brand ID by name.
Requires PREMIUM subscription.
Args:
brand_id (str): Unique identifier of the brand (e.g., "abc123...").
Obtain from list_brands.
Returns:
dict: A Brand object containing:
- id (str): Unique brand identifier.
- name (str): Brand name.
- private_brand (bool): True if only visible to the creating user.
Raises:
RuntimeError: If the brand ID is not found or the request fails.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"sonarqube-mcp-server": {
"url": "https://your-server:8443/mcp"
}
}
}
{
"mcpServers": {
"urldna-mcp": {
"url": "https://mcp.urldna.io/"
}
}
}