Every tool this server actually exposes — parameters, scopes, and example request/response shapes — plus token setup and copy-paste client config for Claude and Cursor.
Go to /settings/api-tokens (an ADMIN can create/revoke; other roles can view). Give it a name and choose whether to allow action tools:
read scope, which registers list_controls, control_status, failing_tests, get_evidence, and query_compliance.action scope, which also registers draft_remediation and open_finding. Both action tools are propose-only; the scope only controls whether the tool exists for that token, not whether it can mutate anything external (it never can).The full plaintext token (prefixed gro_) is shown exactly once at creation — copy it immediately. The token list afterward only ever shows a non-secret prefix, its scopes, and a last-used timestamp; revoking it takes effect immediately.
The server is a single Streamable HTTP MCP endpoint at POST /api/mcpon your site's domain. Authenticate with a Bearer header:
Authorization: Bearer gro_...
Content-Type: application/jsonA missing, malformed, invalid, or revoked token returns a JSON-RPC auth error over HTTP 401. Every tool call is scoped to the org the token belongs to — the org id is never taken from the request body.
Replace YOUR-DOMAIN with your site's domain and YOUR_TOKEN with a token from /settings/api-tokens.
{
"mcpServers": {
"grc-oversight": {
"type": "http",
"url": "https://YOUR-DOMAIN/api/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Claude Desktop: Settings -> Developer -> Edit config, then restart. Claude Code: add the same block under mcpServers in .mcp.json (project) or your user MCP config.
{
"mcpServers": {
"grc-oversight": {
"url": "https://YOUR-DOMAIN/api/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Cursor: Settings -> MCP -> Add new MCP server, or edit ~/.cursor/mcp.json directly with the block above.
list_controlsscope: readControls across the org's entitled frameworks, each with a computed status. Optionally filter by framework (id or name substring) or status.
Parameters
framework string, optionalFramework id or a case-insensitive name substring to filter to.
status "PASS" | "FAIL" | "WARNING" | "NOT_TESTED" | "NO_TESTS", optionalOnly return controls currently in this status.
Example request
{
"name": "list_controls",
"arguments": { "framework": "SOC 2", "status": "FAIL" }
}Example response shape
{
"content": [{ "type": "text", "text": "1 control(s):\n[FAIL] SOC 2 CC6.1 — Encryption at rest" }],
"structuredContent": {
"controls": [
{
"id": "ctl_...",
"code": "CC6.1",
"title": "Encryption at rest",
"framework": "SOC 2",
"status": "FAIL"
}
]
}
}If the org has no activated frameworks, or the framework filter matches nothing, this returns an honest empty result (never a fabricated list).
control_statusscope: readFramework-level readiness rollups for the org (pass/fail/warning/not-tested/no-tests counts and percent ready). Optionally narrow to one framework.
Parameters
framework string, optionalFramework id or a case-insensitive name substring to filter to.
Example request
{
"name": "control_status",
"arguments": { "framework": "ISO 27001" }
}Example response shape
{
"content": [{ "type": "text", "text": "ISO 27001: 82% ready (41 pass / 3 fail / 2 warning / 5 not-tested / 0 no-tests of 51)" }],
"structuredContent": {
"frameworks": [
{
"framework": "ISO 27001",
"readinessPct": 82,
"pass": 41,
"fail": 3,
"warning": 2,
"notTested": 5,
"noTests": 0,
"total": 51
}
]
}
}failing_testsscope: readTests whose latest result is FAIL (and WARNING when includeWarnings is set) for controls in the org's entitled frameworks — the "what is broken now" view.
Parameters
includeWarnings boolean, optionalAlso include tests whose latest result is WARNING, not just FAIL.
Example request
{
"name": "failing_tests",
"arguments": { "includeWarnings": true }
}Example response shape
{
"content": [{ "type": "text", "text": "1 failing test(s):\n[FAIL] Encryption-at-rest scan — affects SOC 2 CC6.1" }],
"structuredContent": {
"tests": [
{
"testId": "tst_...",
"testName": "Encryption-at-rest scan",
"status": "FAIL",
"executedAt": "2026-06-30T12:00:00.000Z",
"affectedControls": ["SOC 2 CC6.1"],
"outputDetails": null
}
]
}
}Tests that have never run are reported as not-tested, not failing — they will not appear here.
get_evidencescope: readRecent evidence items for the org (source type, created time, linked test result), scoped strictly by the evidence's orgId.
Parameters
limit number (1–100), optionalMax items to return. Defaults to 25.
sourceType string, optionalExact match filter, e.g. MANUAL_UPLOAD, AUTOMATED_SCAN, INTEGRATION.
Example request
{
"name": "get_evidence",
"arguments": { "limit": 10, "sourceType": "AUTOMATED_SCAN" }
}Example response shape
{
"content": [{ "type": "text", "text": "1 evidence item(s):\n2026-06-30T12:00:00.000Z [AUTOMATED_SCAN] Encryption-at-rest scan (PASS)" }],
"structuredContent": {
"evidence": [
{
"id": "ev_...",
"sourceType": "AUTOMATED_SCAN",
"createdAt": "2026-06-30T12:00:00.000Z",
"hasFile": true,
"test": "Encryption-at-rest scan",
"testStatus": "PASS"
}
]
}
}query_compliancescope: readNatural-language query over the org's compliance graph. Uses semantic search over the org's own knowledge chunks when embeddings are configured, always falls back to keyword search over entitled controls, and optionally summarizes with the LLM when configured — grounded only in retrieved matches, never fabricated.
Parameters
query string (min length 1), requiredThe natural-language question.
limit number (1–25), optionalMax matches to retrieve/cite. Defaults to 8.
Example request
{
"name": "query_compliance",
"arguments": { "query": "What covers encryption at rest?", "limit": 5 }
}Example response shape
{
"content": [{ "type": "text", "text": "Encryption at rest is covered by SOC 2 CC6.1...\n\nSources:\n[1] (control) SOC 2 CC6.1: Encryption at rest — ..." }],
"structuredContent": {
"matches": [
{ "kind": "control", "ref": "SOC 2 CC6.1", "text": "Encryption at rest — ..." }
],
"summarized": true
}
}If no frameworks are activated and no matches are found, the tool says so explicitly instead of guessing. "summarized" is false when no LLM is configured — the raw matches are still returned as citations.
Only registered on tokens with the action scope. Neither tool mutates a connected external system.
draft_remediationscope: actionReturns a PROPOSED remediation for a control or a failing test. Persists nothing and touches no customer system — grounded in the real control/test row when an LLM is configured, otherwise a structured stub built from real data. Requires exactly one of controlId or testId.
Parameters
controlId string, optionalA control id in this org's activated frameworks.
testId string, optionalA test id mapped to this org's activated-framework controls.
note string, optionalExtra context to ground the draft.
Example request
{
"name": "draft_remediation",
"arguments": { "controlId": "ctl_...", "note": "We use RDS, not self-managed Postgres." }
}Example response shape
{
"content": [{ "type": "text", "text": "PROPOSED REMEDIATION (not applied — requires human approval)\nSubject: SOC 2 CC6.1 — Encryption at rest\n\n1. Enable RDS storage encryption...\n2. ...\n3. Re-run the associated test and capture the passing result as evidence." }],
"structuredContent": {
"subject": "SOC 2 CC6.1 — Encryption at rest",
"proposal": "1. Enable RDS storage encryption...\n2. ...",
"applied": false,
"requiresApproval": true
}
}Requires the "action" scope — a read-only token cannot even see this tool registered. Never mutates a connected system on its own.
open_findingscope: actionCreates ONE internal Finding row for human triage. The only action tool that writes, and it writes only to GRC Oversight's own Finding table — never to an external system. Fails soft (returns a clear "not yet available" result, does not throw) if the Finding model is not provisioned in this deployment.
Parameters
title string (min length 1), requiredFinding title.
severity "LOW" | "MEDIUM" | "HIGH" | "CRITICAL", optionalDefaults to MEDIUM if omitted or unrecognized.
description string, optionalOptional free-text description.
source "scan" | "test" | "integration" | "manual", optionalDefaults to "manual" if omitted or unrecognized.
controlId string, optionalOptional link to a (global) control.
testResultId string, optionalOptional link to a test result. Must belong to this org — otherwise the call is rejected and nothing is created.
Example request
{
"name": "open_finding",
"arguments": {
"title": "S3 bucket policy allows public read",
"severity": "HIGH",
"source": "scan"
}
}Example response shape
{
"content": [{ "type": "text", "text": "Opened finding \"S3 bucket policy allows public read\" (severity HIGH, source scan) for triage." }],
"structuredContent": { "created": true, "id": "fnd_...", "severity": "HIGH", "source": "scan" }
}Requires the "action" scope. If testResultId does not belong to the calling org, the tool returns { created: false, reason: "test_result_not_found" } rather than creating anything.
Mint a token in Settings and drop the config above into your client.