MCP Server
CVD Portal exposes a Model Context Protocol (MCP) server so AI agents can act on the platform directly, the same way a script would use the REST API. An agent connected to it can submit a vulnerability report, read a company's CRA compliance status, and generate a CSAF advisory, without any custom integration code.
There are two servers, one for each side of a disclosure.
| Server | Endpoint | Who it is for | Auth |
|---|---|---|---|
| Manufacturer | /api/mcp | The company operating a portal, acting inside its own workspace | OAuth 2.1 token or API key |
| Researcher | /api/mcp/public | Anyone reporting a vulnerability to a manufacturer | None |
The rest of this page covers the manufacturer server. The researcher server is documented under Public MCP server below.
The manufacturer server accepts companies on the Reporting plan or higher. Each tool also checks the plan.
- Reporting.
get_getting_started,get_next_deadline,get_next_task, the Article 14 toolsrecord_article14_decision,prepare_article14_stageandopen_incident,record_remediation_decision, andsubmit_jobandget_job_statusfor drafting jobs. - Compliance. The CRA product tools,
get_conformity_gaps,get_clause_mapping,record_assessment_result, and classification and conformity jobs. - Enterprise. All other tools, for example submission triage, advisories and
get_compliance_status.
Below Enterprise, an API key can carry only the read scope, so it reaches only the read tools. The Article 14 and remediation tools need the article14:prepare, submissions:write or submissions:triage scope, so on Reporting and Compliance use browser sign-in (OAuth) for them. No MCP tool files a notification with ENISA. You file on the Single Reporting Platform yourself.
The server accepts either an OAuth 2.1 access token obtained via authorization code grant with PKCE, or a legacy Bearer API key. When accessed without credentials, it returns an HTTP 401 status with an RFC 9728 WWW-Authenticate header pointing to /.well-known/oauth-protected-resource so compatible agents can discover the authorization server and register automatically.
Endpoint
https://cvdportal.com/api/mcp
The transport is streamable HTTP. There are two ways to authenticate.
| Method | Use it for | What happens |
|---|---|---|
| Browser sign-in (OAuth 2.1) | Claude Code, the Claude apps, and other MCP clients that a person uses | The client opens your browser. You sign in to CVD Portal and approve the access. The client stores and renews the token. |
| API key | Scripts, CI/CD pipelines, and servers with no browser | You send the key in the Authorization header on every request. |
Use browser sign-in when a person runs the agent. The token is bound to you, so the audit log names you, and it is the only credential that can act for a client company (see Acting for a client company). An API key belongs to the company, not to a person.
Connecting with browser sign-in
Claude Code
Add the server without a header:
claude mcp add --transport http cvd-portal https://cvdportal.com/api/mcp
Then, in an interactive claude session, run /mcp, select cvd-portal, and select Authenticate. Your browser opens.
Claude desktop app and claude.ai
Add a custom connector with the URL https://cvdportal.com/api/mcp and connect it. Your browser opens.
What you see in the browser
- The CVD Portal sign-in page, if you are not signed in already. Sign in with your password or with a sign-in provider such as Google, Microsoft or GitHub. If your account at that provider uses a passkey, you confirm with your fingerprint or face there.
- The consent screen. It names the client, your company, and the permissions (scopes) the client asks for. Select Authorize Access.
- The browser returns to the client, and the client is connected.
How it works
The client needs no configuration beyond the URL. A request without a token gets HTTP 401 with a WWW-Authenticate header that points to /.well-known/oauth-protected-resource. From there the client finds the authorization server, registers itself (RFC 7591), and starts the authorization code flow with PKCE. An access token lasts 1 hour. The client renews it with a refresh token, which lasts 30 days. After 30 days without use, you sign in again.
The token can only call tools whose scope you approved on the consent screen. A client that asks for no scopes gets a token that can call no tools. If every tool call fails with "OAuth token lacks the required scope", remove the connection, add it again, and check the permissions on the consent screen.
See Authentication for the endpoints and the standards they follow.
Connecting with an API key
Create and scope keys under Settings → API Keys in the dashboard. On the Reporting or Compliance plan, you can create a key with only the read scope for the MCP server. Keys with other scopes need the Enterprise plan. A key with no scopes has full access. A scoped key can only call tools whose scope it carries.
Claude Code
claude mcp add --transport http cvd-portal https://cvdportal.com/api/mcp \
--header "Authorization: Bearer <your-api-key>"
Any MCP client
Point the client at https://cvdportal.com/api/mcp with the Authorization: Bearer <your-api-key> header. Clients that support the Model Context Protocol streamable HTTP transport discover the tools automatically.
Scopes
Scoped API keys and OAuth tokens restrict which tools an agent can call.
| Scope | Description |
|---|---|
read | Read vulnerabilities, products, compliance and SBOM metadata |
submissions:write | Submit vulnerability reports |
submissions:triage | Triage and progress vulnerability reports |
submissions:communicate | Draft messages to reporters for approval |
article14:prepare | Prepare CRA Article 14 notification dossiers |
advisory:draft | Draft CSAF and OpenVEX security advisories |
products:write | Register products, record classifications, and build the threat model |
conformity:prepare | Run conformity jobs, record assessments, and sign off Annex I requirements |
Tools
| Tool | Scope required | What it does |
|---|---|---|
list_submissions | read | Lists vulnerability reports with optional stage or status filtering. |
get_submission | read | Returns decrypted case details excluding reporter contact information. |
get_submission_journey | read | Returns the active journey stages, current status, and next step. |
acknowledge_submission | submissions:triage | Acknowledges a report and sends the standard template message. |
triage_submission | submissions:triage | Sets severity, CVSS vector and score, and affected components. |
link_submission_product | submissions:triage | Links a vulnerability report to a workspace product. |
record_article14_decision | article14:prepare | Records whether a vulnerability is actively exploited under CRA Article 14. |
prepare_article14_stage | article14:prepare | Saves draft narrative fields and generates copy-ready SRP text. |
assign_coordinator | submissions:triage | Assigns a vulnerability coordinator from your team. |
record_remediation_decision | submissions:triage | Records the remediation decision and target deadline. |
record_fix_verification | submissions:triage | Confirms that a deployed fix has been verified in production. |
close_submission | submissions:triage | Closes a vulnerability report as RESOLVED or DISMISSED with justification. |
draft_reporter_message | submissions:communicate | Enqueues a message to the vulnerability reporter for human approval. |
request_advisory_publication | advisory:draft | Enqueues publication of an advisory draft for human approval. |
open_incident | submissions:write | Creates a severe incident report and starts CRA Article 14 tracking. Severity is HIGH or CRITICAL, and HIGH when omitted. |
submit_vulnerability | submissions:write | Files a disclosure report in your inbox, or in a client inbox with onBehalfOfCompanyId. Requires a description. The response names the company that received the report. |
get_compliance_status | read | Returns your CRA compliance summary and recommendations. |
generate_csaf_advisory | read | Returns the machine-readable advisory document for an existing advisory. |
assess_response | read | Evaluates an incident or vulnerability response plan under CRA Article 14. |
get_getting_started | read | Returns the ordered first steps for the company. Call it first. |
get_next_deadline | read | Returns the soonest open Article 14 deadline. |
get_next_task | read | Returns the next open CRA obligation. |
submit_job | depends on the job type | Runs a long job in the background. See Background jobs. |
get_job_status | read | Returns the status, progress and result of a job. The plan check follows the job type, the same as submit_job. |
CRA product tools
These tools cover the product journey, from registration to the technical file. They need the Compliance plan features of the company they act for.
| Tool | Scope required | What it does |
|---|---|---|
list_products | read | Lists active products with their ids, the decided classification, the unconfirmed suggestion, and progress counts. |
create_product | products:write | Registers a product. Records a class suggestion only. Refuses a duplicate name and returns the existing id. |
set_product_classification | products:write | Records the classification decision (Annex III or IV category, so the Article 32 route) and its legal basis. Needs an OAuth token. |
generate_product_threats | products:write | Generates STRIDE threat candidates. Does not save. Check source: catalogue means generic entries, not an analysis. Can take more than a minute. |
propose_threat_treatment | products:write | Proposes a mitigation and the residual risk for one threat. Does not save. |
get_threat_model | read | Returns the saved assets, threats and risks, and every Annex I requirement with its key, evidence and sign-off. |
save_threat_model | products:write | Saves assets, threats and risks row by row. null deletes a row. Sets risk treatment. Any validation failure refuses the whole save. |
signoff_annex_i_requirements | conformity:prepare or products:write | Signs off or revokes Annex I requirements. See Annex I sign-off over MCP. |
get_conformity_gaps | read | Lists open gaps across Annex I, technical documentation and evidence. |
record_assessment_result | conformity:prepare or products:write | Records a dated assessment of one CRA clause. |
get_declaration_of_conformity | read | Builds the technical file with the Annex V Declaration of Conformity through the export gates. Returns the gaps when the file is not ready. Does not sign the declaration. |
Background jobs
submit_job takes a type and an input object.
| Type | Input | Result |
|---|---|---|
CLASSIFY_PRODUCT | text, or productName and description | A class suggestion. It does not change the product. Use set_product_classification to record the decision. |
IMPORT_PREFILL | documentContent (text), productId, optional documentName | Extracts facts from the document and prefills the product assessment, each field with provenance. Send the text of a PDF, not the file. |
AUTO_ENRICH | productId | Fills the fixed risk methodology and the category standards. |
ASSEMBLE_DOSSIER | productId | Assembles the technical dossier index and requests approval. |
DRAFT_ARTIFACT | artifactId, productId, inputs | Drafts one CRA artifact. |
A job for a product that is not in the job's company fails with "Product not found in this workspace". It does not report success.
Filing with the ENISA Single Reporting Platform stays with a person. Tools prepare draft text and calculate deadlines. Authorized personnel review the draft, file on the SRP, and record the reference in the portal.
Acting for a client company
A consultancy can act for a client company where the consultancy user holds a seat (a partner membership). Pass the client's company id as onBehalfOfCompanyId on any tool that reads or changes company data.
- Only a user-bound OAuth token can act for another company. A plain API key stays in its own company.
- A read needs a membership in the client company. A write needs ADMIN there.
- The membership is checked again on every call. When the client removes the seat, access stops at once.
- The plan check uses the client's plan, not the consultancy's.
- Background jobs run in the client company, so the job worker finds the client's products.
Without onBehalfOfCompanyId, every tool works in the company of the key or token. The company always comes from the verified credential and the membership check, never from a free tool argument.
Annex I sign-off over MCP
signoff_annex_i_requirements records a legal attestation in the technical file.
- The signatory is the person who owns the OAuth token. The platform takes the name and email from that user account, never from tool arguments.
- A plain API key cannot sign.
- The signatory must be ADMIN in the company the call acts for.
- Each signed entry records that it came through MCP and through which OAuth client. The audit log records the same.
This differs from the dashboard. In the dashboard, a partner who drills into a client workspace cannot sign. Over MCP, a partner user who is ADMIN in the client company can sign for that client. Call the tool only when the signatory has reviewed the requirement and its evidence and has asked for the signature.
Public MCP server
The researcher side needs no account and no API key. It exposes the same actions a researcher already performs in a browser, so an agent can find a vendor's portal, file a report, and follow it afterwards.
https://cvdportal.com/api/mcp/public
Claude Code
claude mcp add --transport http cvd-portal-public https://cvdportal.com/api/mcp/public
| Tool | What it does |
|---|---|
find_vendor_portal | Looks up a manufacturer's portal by slug or custom domain. Returns the portal URL, the security.txt URL, the policy URL, and the disclosure contact. |
submit_vulnerability_to_vendor | Files a report to a manufacturer's portal. Takes a slug plus the report fields. Returns a reference number and a tracking token. |
track_vulnerability_report | Reads the current status of a filed report from its tracking token. |
subscribe_to_report | Subscribes a public HTTPS callback URL to real-time status push notifications using the tracking token. Callbacks receive signed HTTP POST webhooks with status-only payloads. |
Three things are worth knowing before an agent uses this.
Record the tracking token. It is the only route back to an anonymous report and it cannot be recovered.
Filing is limited to 5 reports per minute, per portal, per IP, the same ceiling as the web form. The limit is shared, so switching between the form and MCP does not raise it.
track_vulnerability_report returns status, vendor, product, and dates. It never returns the report body, the reproduction steps, the impact, or the contact details. A tracking token proves possession, not authorship.
A disclosure is a permanent record on the vendor's side and cannot be retracted through the API. Confirm the vendor and the finding with the person you are acting for before filing.
Privacy
Tool-call analytics record which tool ran, its outcome, and timing. Argument and result content, which can include vulnerability details, is not recorded by default. This applies to both servers.