project_tracker_base: CINO Project Tracker:appf7fRQUvY9Iy7sL
project_tracker_table: Projects:tblchmbxSAavvJKaY
project_tracker_record: SecID-Service:recJ2sF2CudDqTJRN
project_source: github:CloudSecurityAlliance-Internal/CINO-Projects/projects/SecID-Service
REST API and MCP server for resolving security identifiers to URLs. A Cloud Security Alliance project by Kurt Seifried, Chief Innovation Officer.
Live at secid.cloudsecurityalliance.org
Add SecID to your AI assistant as a remote MCP server:
https://secid.cloudsecurityalliance.org/mcp
That's it. No API keys, no local install, no configuration. Works with Claude Desktop, Claude Code, Cursor, Windsurf, and any MCP client that supports remote servers. Your AI assistant gets three tools (resolve, lookup, describe) and can immediately look up CVEs, CWEs, ATT&CK techniques, NIST controls, and more than 2,100 other security knowledge sources.
Other ways to use SecID: Claude Code plugin (local MCP server, supports internal resolvers) | Client SDKs (Python, TypeScript, Go) | REST API (below)
One endpoint:
GET https://secid.cloudsecurityalliance.org/api/v1/resolve?secid=secid:advisory/mitre.org/cve%23CVE-2021-44228
Note: # must be encoded as %23 in the query parameter.
Response:
{
"secid_query": "secid:advisory/mitre.org/cve#CVE-2021-44228",
"status": "found",
"results": [
{
"secid": "secid:advisory/mitre.org/cve#CVE-2021-44228",
"weight": 100,
"url": "https://www.cve.org/CVERecord?id=CVE-2021-44228"
}
]
}No authentication. CORS is open (Access-Control-Allow-Origin: *) for GET, HEAD, POST and
OPTIONS, never with credentials — there is no ambient credential to protect.
https://secid.cloudsecurityalliance.org/mcp (and /mcp/) is a stateless Streamable HTTP
endpoint: every MCP message is a POST. Everything else is answered explicitly, by one middleware
(src/method-gate.ts, ADR-015):
| Request | Answer |
|---|---|
POST |
JSON-RPC (the MCP SDK's handling) |
GET / HEAD from an MCP client |
405, Allow: POST, OPTIONS — no server-push stream on a stateless server |
GET / HEAD from a browser |
302 to the setup instructions |
DELETE, PUT, PATCH, QUERY, TRACE, CONNECT |
405, Allow: POST, OPTIONS |
| an unregistered method | 501 |
A browser is identified positively (Sec-Fetch-Mode: navigate, or Accept: text/html) and only
after ruling out every MCP-client signal (text/event-stream in Accept, MCP-Protocol-Version,
Mcp-Session-Id). Getting this wrong is not cosmetic: an MCP client handed a redirect instead of a
405 reconnects once a second for its whole session.
secidinput limit: 1024 characters on REST and MCP tool inputs.- REST returns
status="error"with:SecID query exceeds 1024 characters. Limit: 1024 characters. - MCP returns tool error content with the same explicit limit message.
- REST returns
- MCP HTTP request body limit: 64 KiB (
413when exceeded). - Cloudflare KV value limit: 25 MiB per key.
- Registry upload script enforces this limit before upload.
- Service also checks
full:registrypayload size before serving/api/v1/registry.json.
- Abuse throttling: the Worker enforces the input-size limits above. Edge rate limiting (Cloudflare WAF) is the intended primary layer (ADR-012), but that configuration lives in the Cloudflare dashboard, not in this repository — check the zone's rules rather than assuming they are active.
- Runtime: Cloudflare Workers
- Framework: Hono + @modelcontextprotocol/sdk
- Registry: Compiled from SecID registry JSON files (more than 2,100 namespaces across 10 types)
- Website: Astro static site served from the same Worker
Today SecID-Service runs only at secid.cloudsecurityalliance.org/mcp (anonymous, no auth — the friction-free public utility). Per CSA-MCP-Server ADR-002, every capability ships in two shapes — a standalone deploy (this Worker, unchanged) AND a plugin form consumed by CSA-MCP-Server at cloudsecurityalliance.org/mcp (Auth0-gated, alongside Search and future Working Groups / Training / Navigator).
SecID is the likely first test of the two-shapes pattern because it already exists as a working standalone Worker. The refactor is to lift the tool logic (the resolve / lookup / describe data work — currently in src/mcp.ts) into a shared package that both this Worker's mcp.ts AND a new front-door plugin package import. Standalone keeps anonymous access; front door adds Auth0 on top. A SecID-specific ADR (forthcoming, will live here in this repo's DECISIONS.md or DECISIONS-ADR.md) will document the concrete refactor steps when work starts.
The umbrella SecID will join:
| Repo | Role |
|---|---|
| CSA-MCP-Server | The front-door composition — deploys to cloudsecurityalliance.org/mcp. Will import SecID's plugin form alongside the Search plugin. |
| CSA-MCP-Core | Shared infrastructure library — auth, rate limits, observability, MCP protocol plumbing. SecID's plugin form will import this; standalone SecID may also adopt it incrementally for DRY observability/safety helpers. |
| CSA-Search-2.0 | First non-platform plugin (search / ask / get_artifact). The pattern SecID's plugin form will follow. |
| CINO-Products / csa-mcp-server | Product-level umbrella — strategic positioning, capability roadmap. |
This is forward-looking — no code change today, just signal that the repo's role is broadening.
npm install
npm install --prefix website # the website is a separate Astro project
npm run dev # Local dev server
npm run test # Unit/integration tests (Vitest, inside workerd)
npm run test:e2e # Playwright against production (SITE_URL to override)
npm run build:registry # Recompile registry from SecID repo
npm run build:website # Rebuild the static site into website/distMerging to main deploys to production, through
.github/workflows/registry-kv-upload.yml. The same
workflow runs on repository_dispatch from the SecID spec repo when the registry changes, and on
manual workflow_dispatch. Every run, in order:
- Unit tests (
npx vitest run) against a registry snapshot built from the SecID revision being deployed — a red suite stops the run before anything is uploaded. - KV sync of the registry (
upload-registry-kv.ts --sync). wrangler deployof the Worker and the website.- Post-deploy verification: the Playwright suite against production — the website, plus the
MCP endpoint (a real MCP client, the method table above, CORS). Tests tagged
@third-party(cve.org, cwe.mitre.org, …) are excluded so an outage elsewhere cannot fail a deploy.
Pull requests are gated separately by ci.yml (typecheck, website
build, unit tests). It never deploys.
If post-deploy verification fails, the new version is already live. The job fails and its summary says so. Roll back with:
npx wrangler rollback --message "post-deploy verification failed"Watch and confirm a deploy: gh run list --workflow=registry-kv-upload.yml --limit 3, then
npx wrangler deployments list — each deployment should match one workflow run, about two minutes
after it starts. The site footer shows the deployed commit SHA.
Every version names its source. The workflow tags each Worker version with the SecID-Service
commit and stamps a message with that commit, the SecID registry commit, the trigger and the
Actions run — npx wrangler versions list (or the dashboard's Versions list) shows them. Cloudflare
still lists the author as "Unknown": that field is the API token's owner, and the deploy token is an
account token. A break-glass npm run deploy stamps break-glass by <git user>, and +dirty if
the working tree had uncommitted changes.
Correction (2026-10-01): this section previously said Cloudflare Workers Builds deployed on push, with no test gate (#28). Since at least 2026-09-25 every production deployment corresponds one-to-one with a run of the workflow above, with no additional deployments, so Workers Builds is not deploying. The Cloudflare dashboard (Workers & Pages →
secid-service→ Settings → Build) is the place to confirm no Git connection remains.
npm run deployUse this only when the automatic pipeline is unavailable. It builds the website
and then deploys, in that order, and the order matters: wrangler.toml sets
[assets] directory = "./website/dist", and website/dist/ is gitignored. A bare
wrangler deploy publishes whatever happens to be on your disk — which, on a
checkout that has not built the site recently, silently replaces the live website
with a stale build. Prefer merging to main.
A hand deploy skips the post-deploy verification, so run it yourself afterwards:
npx playwright test --grep-invert @third-party| Repo | Purpose |
|---|---|
| SecID | Specification + registry data |
| SecID-Service (this repo) | Cloudflare Worker REST API + MCP server |