# Connect another MCP client to VirusTotal

<a id="oauth"></a>

## Browser OAuth for remote MCP

Server URL: `https://ai.virustotal.com/mcp`.

1. Add the resource URL as a remote MCP server in a client that supports browser OAuth.
2. Remove manually configured authentication headers and token helpers from that server entry; let the client manage OAuth credentials.
3. Start the client's sign-in flow, continue with Google and approve the requested permissions.
4. Call get_domain_report for virustotal.com to verify this connection.

Free VTAI quota is shared across the same account's OAuth connections. No VirusTotal API key is needed. Google sign-in creates a VTAI account; it does not link VirusTotal account privileges.

- `vt:reports:read`: Read reports and recover this connection's submissions and analyses.
- `vt:submissions:write`: Submit files using standard VirusTotal sharing; also requires vt:reports:read.
- `vt:network-analysis:write`: Submit URLs and request domain/IP reanalysis using standard VirusTotal sharing; also requires vt:reports:read.

Permissions apply to this connection without a VTAI confirmation for each operation; the client's own tool permissions still apply. File permission does not authorize network analysis. Reconnect and approve the network permission when adding it to an existing OAuth connection. Recover submissions through the same OAuth connection, including after token refresh. Other connections and Agent Tokens do not inherit its receipts.

[Your connections: review and revoke access](https://ai.virustotal.com/oauth/connections). Revoke access in Your connections. Signing out or removing a local server entry does not revoke a connection.

Local stdio, existing token recipes and direct REST use VTAI Agent Tokens. Runtime plugins have their own documented access methods; the Claude Code and Antigravity MCP plugins use browser OAuth. OAuth supports dynamic client registration and Client ID Metadata Documents (CIMD). Public CIMD clients use none with PKCE; private_key_jwt is not supported. Hosted-client compatibility requires a completed connection, not just metadata discovery. OAuth validation is stated in each client's browser sign-in recipe. Agent Token validation notes apply only to the manual token recipes. [Hosted-client compatibility and setup](https://github.com/VirusTotal/virustotal-mcp/blob/main/docs/hosted-clients.md).

<a id="chatgpt"></a>

## Connect ChatGPT

[Install the VirusTotal plugin](https://chatgpt.com/plugins/plugin_asdk_app_6abadac2d9a08191ae2307c047bb3806).

The published plugin does not require Developer mode. Installation and use depend on your ChatGPT account and workspace policies.

1. Open the published VirusTotal plugin using the installation link and select Install plugin.
2. Connect VirusTotal when prompted. Continue with Google on VTAI, review the permissions and select Allow access. If needed, open the plugin's linked application and choose Connect under Connected accounts.
3. Start a new chat, type @ and select VirusTotal. Ask for the domain report below and check the tool activity and returned report.

First query:

```text
@VirusTotal Get the domain report for virustotal.com. Show the source, analysis date, coverage and report link.
```

If installation is unavailable, check your ChatGPT account and workspace policies. If no login opens or no tools appear, open the plugin's linked application and check Connected accounts; choose Connect or Connect another account if needed. After connecting, refresh the application's tools if that control is available and start a new chat. A response that only browses the public VirusTotal website does not verify an MCP connection. Do not paste an API key or Agent Token into chat or OAuth client fields.

VirusTotal is published in the ChatGPT plugin directory. Browser consent, IP reports and automatic token renewal were verified separately in ChatGPT Work on 24 September 2026. Those checks did not cover hosted revocation, incremental permissions or writes; publication does not validate every workflow. [ChatGPT setup and compatibility](https://github.com/VirusTotal/virustotal-mcp/blob/main/docs/hosted-clients.md#chatgpt-public-connection-and-individual-oauth).

### Alternative: create a custom app

1. For a custom setup, use an account or workspace that permits Developer mode and enable it in ChatGPT settings. Create a plugin/application named VirusTotal, or reuse your existing custom application.
2. Enter the server URL below and choose OAuth. Leave Client ID and Client Secret empty for automatic registration. Create the application, install the custom plugin if prompted, and connect your account through Google and VTAI consent.
3. Start a new chat and select your VirusTotal entry. Keep one working VirusTotal connection; preserve existing access and any pending submissions when switching setups.

Server URL: `https://ai.virustotal.com/mcp`. Authentication: OAuth.

## Native OAuth for Antigravity and Gemini CLI

### Antigravity

Merge this entry into Antigravity's MCP server configuration, then use Authenticate and approve the requested permissions. Do not add token headers. Keep one VirusTotal entry; preserve other servers.

```json
{
  "mcpServers": {
    "virustotal": {
      "serverUrl": "https://ai.virustotal.com/mcp"
    }
  }
}
```

Plugin installation, browser sign-in, one domain report and revocation were verified with Agy 1.2.14/1.2.15 on 2 October 2026. This manual serverUrl entry was not tested separately. Renewal, writes and graphical IDE OAuth remain unverified. Local stdio was tested separately.

### Gemini CLI extension

```sh
gemini extensions install https://github.com/VirusTotal/virustotal-mcp --ref main
```

Review and approve extension installation, then authenticate VirusTotal from /mcp in a Gemini CLI session. Your model-provider login is separate from VTAI access. Gemini CLI 0.58.0 installed extension 0.9.8, discovered its skill and uninstalled it in an isolated profile on 2 October 2026. A separate session completed one domain query using a Gemini API key and supplied VTAI OAuth bearer, with extensions disabled. Native browser OAuth and token renewal remain unverified.

[Installation, updates and removal](https://github.com/VirusTotal/virustotal-mcp/blob/main/docs/google-clients.md).

Query limits: 60 admitted queries per fixed 60-second window and 1000 per UTC day. Agent Tokens share the agent's allowance across REST and MCP; OAuth connections share the account's allowance. Unknown reports and upstream failures after admission count. Every repeated lookup counts again, including cached reports and hashes with no report. On 429, honor Retry-After and reuse your credential. File submissions first check whether the hash exists in VirusTotal. Only confirmed unknown files are uploaded, without using query quota, even when the query allowance is exhausted. A separate file contribution limit permits 20 admitted upload attempts per fixed 60-second window and 500 per UTC day, per Agent Token identity or OAuth account across connections. Failed or uncertain admitted attempts still count; known files and receipt recovery do not. Creating multiple identities or accounts to evade limits is not permitted. If the file is known, returning its existing report costs one query and no file is uploaded. Explicit hash lookups and analysis-result reads still count. The access check costs no query quota and does not report remaining quota.

Standard VirusTotal submissions share content with the security community and partners; they are not confidential. Upload files or attachments, submit URLs, or request domain/IP reanalysis only when the user explicitly requests that action. A missing report, an unfamiliar file or a suspicious indicator is not permission to submit it. Explain the sharing and ask before submitting the user's own documents, internal code, credentials or personal data. Submission tools add no per-call confirmation; client permissions still apply.

Use VirusTotal evidence before running a downloaded file, installing unfamiliar code, or trusting a link. Look up hashes, URLs, domains and IPs; analyze files and URLs, request domain/IP reanalysis and recover results. A missing report or no detections does not establish safety.

Transport: **http**. MCP endpoint: `https://ai.virustotal.com/mcp`. This recipe uses a VTAI Agent Token; compatible remote MCP clients can also use [browser OAuth](#oauth). Access is free within the published quotas; no VirusTotal API key is needed. Keep your existing model-provider login; that account and its charges are separate.

Choose a host that can map your credential into a protected HTTP header.

Client setup: [Antigravity CLI (agy)](https://ai.virustotal.com/connect/mcp?client=agy&format=markdown) · [Claude Code](https://ai.virustotal.com/connect/mcp?client=claude&format=markdown) · [Codex](https://ai.virustotal.com/connect/mcp?client=codex&format=markdown) · [Cursor](https://ai.virustotal.com/connect/mcp?client=cursor&format=markdown) · [VS Code](https://ai.virustotal.com/connect/mcp?client=vscode&format=markdown) · [GitHub Copilot CLI](https://ai.virustotal.com/connect/mcp?client=copilot&format=markdown) · [Devin Local / CLI](https://ai.virustotal.com/connect/mcp?client=devin&format=markdown) · [Cascade / Windsurf](https://ai.virustotal.com/connect/mcp?client=cascade&format=markdown)

Choose a client with a documented protected credential mapping.

## 1. Reuse or create access

Reuse `~/.config/vt-mcp/token` if configured. Changing clients does not require registration. Keep credentials out of prompts, tool arguments, URLs and printed command output. An authorized setup process may store the token directly without exposing it to model context. This Agent Token path needs no browser sign-in. Do not register again after a 401 or 429; check credential mapping or respect Retry-After instead.

If no credential exists and setup is authorized, this POSIX Python 3 command registers once and saves it with owner-only permissions. The handle and activity totals may appear on the public leaderboard. It sends one registration request, with no automatic retry on an uncertain result. Windows needs equivalent user-only file permissions.

```bash
python3 - <<'PY'
import json
import os
import re
from pathlib import Path
from urllib.request import HTTPRedirectHandler, Request, build_opener

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

path = Path.home() / ".config" / "vt-mcp" / "token"
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
path.parent.chmod(0o700)
if path.exists() or path.is_symlink():
    raise SystemExit("Reuse the existing credential; registration was not sent.")
request = Request(
    "https://ai.virustotal.com/api/v3/agents/register",
    data=json.dumps({"agent_family": "vt-mcp-other", "agent_version": "0.9.9"}).encode(),
    headers={"Content-Type": "application/json"}, method="POST",
)
try:
    with build_opener(NoRedirect).open(request, timeout=20) as response:
        if response.status != 200:
            raise ValueError("Unexpected status")
        raw = response.read(8193)
        if len(raw) > 8192:
            raise ValueError("Response limit")
        token = json.loads(raw)["agent_token"]
        if not isinstance(token, str) or not re.fullmatch(r"vtai_[A-Za-z0-9_-]{1,507}", token):
            raise ValueError("Credential format")
except Exception:
    raise SystemExit("Registration is uncertain. Do not retry automatically.") from None
try:
    with os.fdopen(os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), "w") as output:
        output.write(token + "\n")
except OSError:
    raise SystemExit("Access was created but saving failed. Resolve storage before retrying.") from None
print("Credential saved to protected storage. Its value was not displayed.")
PY
```

## 2. Configure your MCP host

Use Streamable HTTP at `https://ai.virustotal.com/mcp` and your host's protected credential mapping for either `x-apikey: <VTAI Agent Token>` or `Authorization: Bearer <VTAI Agent Token>`. Send one authentication header, never both. Do not substitute a real token into chat or a shell argument. For an OAuth client, use the [browser OAuth instructions](#oauth) instead of these token headers.

Select a host-specific recipe from the client links above.

## 3. Verify and use

Confirm that your client discovers `get_file_report`, `get_url_report`, `get_domain_report`, `get_ip_report`, `get_analysis`, `submit_file`, `get_submission`, `submit_url`, `reanalyze_domain` and `reanalyze_ip`. Local stdio additionally offers `submit_local_file`; remote MCP also lists `submit_attachment` for attachment descriptors with a public HTTPS download_url. The optional fields are file_id, file_name and mime_type. The client must supply the original file's URL; never invent one. Native attachment availability depends on the client. Call `get_domain_report` once for `virustotal.com`. Check the returned source, analysis date, coverage and report link. This consumes a query. Tool discovery alone does not verify authenticated access.

For files, hash first and use `get_file_report`. Submit actual bytes only when the user explicitly requests the upload, using the sharing guidance above. A missing report is not permission to upload. `submit_local_file(path, expected_sha256=None)` accepts up to 32,000,000 bytes over stdio. `submit_file(sha256, content_base64)` accepts up to 24,000,000 decoded bytes over either transport. Remote HTTP cannot read a local path. Submitted content may be shared with the VirusTotal community and security partners; base64 also passes through your MCP host. Host permissions apply; the tools add no per-call confirmation argument.

If this client cannot transfer the file bytes for an explicitly requested upload, offer the existing VirusTotal upload page at https://www.virustotal.com/gui/home/upload under the same sharing guidance. Before uploading, manually check the file's actual hash in VirusTotal and upload only if unknown. After the user uploads the file, obtain its actual hash and look up its report. The report may not be available immediately; every repeated query consumes quota. A web upload creates no VTAI receipt. This alternative does not bypass client permissions, authentication or quota limits, and must not repeat an uncertain submission.

For submissions through this connection, keep the SHA-256. Recover an uncertain submission with `get_submission(sha256)` using the same connection or credential, without sending bytes again. `exists` means an existing report; `submitted` supplies an analysis ID. Read that ID using `get_analysis` within a finite polling budget. Unknown can remain unknown permanently; do not repeat submission merely to resolve uncertainty.

Only when the user explicitly requests URL submission or network reanalysis, use `submit_url(url, request_id)`, `reanalyze_domain(domain, request_id)` or `reanalyze_ip(ip, request_id)`. Generate and persist a canonical lowercase UUIDv4 request ID before calling. Use `get_submission(request_id=request_id)` to recover with the same connection, then `get_analysis(analysis_id, request_id=request_id)` to select the returned analysis. Receipt recovery accepts exactly one of `sha256` or `request_id`. Standard sharing applies. Do not automatically replay an uncertain POST; a deliberate later rescan uses a new request ID. OAuth requires `vt:reports:read` and `vt:network-analysis:write`; approve the new network permission when reconnecting an existing OAuth client. File submission permission does not include network analysis.

## Troubleshooting and limits

For repeated file checks, deduplicate hashes within the task and reuse reports while their age remains appropriate to your decision. Space requests to avoid bursts; token-based REST and MCP use the same quota. The [Python REST lookup example](https://ai.virustotal.com/examples/query_file_reports.py) implements deduplication and bounded quota retries; see the [API skill](https://ai.virustotal.com/skills/BASIC.md) for usage.

REST and MCP using the same Agent Token share quotas; unknown reports and upstream failures can consume admitted queries. For 429, respect `Retry-After` and keep the existing credential; registering another identity is not quota recovery. For 400, check conflicting or malformed authentication headers. For 401/403, check credential mapping, validity and permissions without displaying the value. `X-VTAI-Error: unexpanded_credential` means an x-apikey variable reference was sent literally. Load the protected token into the client environment and use its documented variable syntax; do not paste the token into chat or register again. This specific diagnostic applies to x-apikey. Check the endpoint and method for MCP HTTP errors; a tool's `not_found` is an unknown indicator, not evidence of safety. Only when the user explicitly requests an upload, submit actual file bytes with the existing submission tools; a hash cannot start an analysis. Network submissions and reanalysis also require the user's explicit request. A service failure is not a clean report. Avoid URLs containing private paths or query parameters; use a domain lookup when appropriate.

[Current access limits and browser setup](https://ai.virustotal.com/connect/mcp?client=other&transport=http&auth=token) · [Agent API instructions](https://ai.virustotal.com/skills/BASIC.md) · [OpenAPI](https://ai.virustotal.com/openapi.json)

## Disconnect the token-based setup

Remove the `virustotal` entry and restart the client. A shared credential remains active in other clients. To disable it everywhere, use [Revoke access](https://ai.virustotal.com/connect/mcp?client=other&transport=http&auth=token#revoke). The authenticated API is `DELETE /api/v3/agents/me/token`; success returns 204. Reconnect with the same credential while it remains active.
