MCP server¶
The MCP server (security-review-mcp) is the process of the AI agents: it gives them the
review prompts, the checklists, the pattern scan and the development standards, and saves their
reports. It never calls the controller: both share the database (see
Architecture).
What it serves¶
| Route | Used by | Credential |
|---|---|---|
/mcp |
the agents: prompts, tools and resources | mcp token (personal or not) or admin token |
/mcp without a token |
an agent not yet connected: enrollment server | none |
POST /api/v1/enroll |
security-review-mcp enroll and scripts: enrollment |
none |
/healthz |
probes (Docker HEALTHCHECK, Kubernetes) |
none |
It serves no web page and no other route of the REST API.
Two transports¶
| Transport | Command | Use |
|---|---|---|
| Streamable HTTP | security-review-mcp serve (default of the image, port 8000) |
shared mode: every agent of the team, with a token |
| stdio | security-review-mcp local |
local mode: started by the host on one workstation, local SQLite database, no token |
Start-up¶
In HTTP, security-review-mcp serve:
- checks the schema of the database and refuses to start (code
2) while the controller has not migrated it: it never migrates (see Schema migrations); - checks the TLS settings, loads the rules of the database;
- serves
/mcp. While no token exists, it stays closed: a client only reaches the enrollment, unless the server listens on localhost orSECREVIEW_ALLOW_UNAUTHENTICATED=true(see Bearer tokens).
In stdio, security-review-mcp local migrates its local database itself.
What an agent gets¶
| Client | Answer |
|---|---|
| No token | the enrollment server: request_enrollment and prompts that explain how to get a token; 401 when SECREVIEW_ENROLLMENT=false |
| Unknown, revoked or expired token | 401 that tells the user where to create a new token (rejected token) |
Valid mcp or admin token |
every prompt, tool and resource, on the projects of the token (tokens restricted to a project) |
Tokens and rules are read in the database: a token created or revoked on the controller applies to the next request, a changed rule within 5 seconds.
What it never does¶
- It makes no outgoing connection (the OSV database is imported by the controller), and loads no module of the controller.
- Its tools never read the disk from a path given by the client: they work on the content sent.
- Every input is bounded (
SECREVIEW_MAX_CONTENT_CHARS,SECREVIEW_MAX_FINDINGS_PER_REPORT,SECREVIEW_MAX_MCP_REQUEST_MB, 64 KiB for a client without a token), and secrets are masked before storage. - It never changes the status of a finding:
propose_finding_statusonly records a proposal, that a person approves in the web interface. - In stdio, stdout is kept for the MCP protocol: the logs go to stderr.
See Security and limits.
Exposure and replicas¶
Expose the MCP server with TLS (built-in or reverse proxy) to the workstations of
the developers. Behind a reverse proxy or an Ingress, set SECREVIEW_FORWARDED_ALLOW_IPS, otherwise
every client shares the address of the proxy for the enrollment limits.
The MCP sessions (streamable HTTP) live in the memory of the process that opened them: before
running several replicas, send every request of a session to the same replica (affinity on the
Mcp-Session-Id header, see Kubernetes).
The variables of the MCP server: Configuration. Its
commands (serve, local, enroll): Command line.
Enrollment: request the project¶
The enrollment never creates a token: it requests the project, which an administrator
approves, and the developer creates a personal MCP token in the web interface. It is enabled by
default (SECREVIEW_ENROLLMENT=true), even before the first token is created (except on an
open server, which needs no token).
-
Request. The client gives the name of its project (usually the repository name). A new project is pending: it is not a project yet and gives no access. A project that already exists, or is already pending, is unchanged, and the answer is the same in every case, so that it does not tell which projects exist. Three ways to enroll:
- From the agent, at the first review. An MCP client that connects without an
Authorizationheader is not rejected: it reaches a restricted MCP server (samesecurity-reviewname) that only exposes therequest_enrollmenttool, and prompts named like the reviews (security-review,codebase-review…) that explain the steps. The agent callsrequest_enrollmentwith the project name. -
From the command line, from the developer workstation or the CI:
security-review-mcp enroll --url https://secreview.example.internal:8443 \ --project shop-api --requested-by "alice (shop team)" # {"project": "shop-api", "web_url": "https://secreview.example.internal/ui", "next_steps": "…"} -
Through the MCP server API:
POST /api/v1/enroll(without a token) with{"project": "shop-api"}, answered with202. The controller does not serve this endpoint: it exposes no unauthenticated write route. - Approval. A user holding
projects.create(theadminrole among others) approves or rejects the request in the Pending projects section of the Projects screen, or from the CLI:
security-review-controller project list # requester, address, expiry security-review-controller project approve 3 # id of the list security-review-controller project reject 4The approval creates the project, with its group
project:<project>without any member: nobody gains any access, the approver included. A request nobody approved withinSECREVIEW_PROJECT_ENROLLMENT_TTL_DAYS(7 days by default) is deleted. 3. Personal token. The answer of the enrollment gives the URL of the web interface (web_url, fromSECREVIEW_WEB_URL) and the next steps: the user signs in, creates a personal MCP token in My account (see personal MCP tokens) and puts it in the host configuration. The token reaches the projects the user may read, at each request. 4. Access. An administrator gives the user access to the project by adding them to the groupproject:<project>(Groups screen, see access control); the personal token reaches the project from the next request, without reconfiguration. - From the agent, at the first review. An MCP client that connects without an
An agent that already has a token may also call request_enrollment (for instance when
save_security_report refuses a project its token does not reach): the project is requested
the same way, and the token gains no project. When a user with projects.create
creates the project directly, its pending request is
settled.
Rejected token: sign in again¶
A client whose token is unknown, revoked or expired gets 401 on /mcp
(error="invalid_token"): the description tells the user to sign in to the web interface (with
its URL when SECREVIEW_WEB_URL is set) and to create a new personal MCP token, then to replace
it in the host configuration. Such a client never
reaches the review tools nor the enrollment server. The rejection is logged as a warning by the
MCP server (Rejected unauthenticated request / Rejected expired token).
Safeguards: anybody who reaches the server may enroll, so at most 20 projects are pending at a
time, a client address enrolls at most 5 times per hour (429 beyond; behind a reverse proxy,
set SECREVIEW_FORWARDED_ALLOW_IPS), and a pending project
expires after SECREVIEW_PROJECT_ENROLLMENT_TTL_DAYS. Every request, approval, rejection and
expiry is written to the audit log (enrollment.requested,
enrollment.approved, enrollment.rejected, with the requester and the client address). To disable the enrollment: SECREVIEW_ENROLLMENT=false
(clients without a token then get 401, with the same advice).
--url is the URL of the MCP server (with or without /mcp): it receives the requests.