Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ A [Model Context Protocol](https://modelcontextprotocol.com/) server that provid
- Get specific tests that validate each security control
- Understand which automated tests monitor compliance for specific controls

### Risk Scenario Management

- Get all the risk scenarios you are managing in your current risk register.
- Returns details about each risk scenario's status, inherent & residual risk score, treatment plan, and more.
- Filterable by risk category (Access Control, Cryptography, Privacy, and many others).

### Multi-Region Support

- US, EU, and AUS regions with region-specific API endpoints
Expand All @@ -41,6 +47,7 @@ A [Model Context Protocol](https://modelcontextprotocol.com/) server that provid
| `get_framework_controls` | Get detailed security control requirements for a specific compliance framework. Returns the specific controls, their descriptions, implementation guidance, and current compliance status. Essential for understanding what security measures are required for each compliance standard. |
| `get_controls` | List all security controls across all frameworks in your Vanta account. Returns control names, descriptions, framework mappings, and current implementation status. Use this to see all available controls or to find a specific control ID for use with other tools. |
| `get_control_tests` | Get all automated tests that validate a specific security control. Use this when you know a control ID and want to see which specific tests monitor compliance for that control. Returns test details, current status, and any failing entities for the control's tests. |
| `get_risks` | Get all the risk scenarios you are managing in your current risk register. Returns details about each risk scenario's status, inherent & residual risk score, treatment plan, and more. Filterable by risk category (Access Control, Cryptography, Privacy, and many others). |

## Configuration

Expand Down
9 changes: 9 additions & 0 deletions src/eval/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
GetControlsTool,
GetControlTestsTool,
} from "../operations/controls.js";
import { GetRisksTool } from "../operations/risks.js";

// Format all tools for OpenAI
const tools = [
Expand Down Expand Up @@ -60,6 +61,14 @@ const tools = [
parameters: zodToJsonSchema(GetControlTestsTool.parameters),
},
},
{
type: "function" as const,
function: {
name: GetRisksTool.name,
description: GetRisksTool.description,
parameters: zodToJsonSchema(GetRisksTool.parameters),
},
},
];

// Test cases with expected tool calls
Expand Down
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getControls,
getControlTests,
} from "./operations/controls.js";
import { getRisks, GetRisksTool } from "./operations/risks.js";
import { initializeToken } from "./auth.js";

const server = new McpServer({
Expand Down Expand Up @@ -71,6 +72,13 @@ server.tool(
getControlTests,
);

server.tool(
GetRisksTool.name,
GetRisksTool.description,
GetRisksTool.parameters.shape,
getRisks,
);

async function main() {
try {
await initializeToken();
Expand Down
61 changes: 61 additions & 0 deletions src/operations/risks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { baseApiUrl } from "../api.js";
import { Tool } from "../types.js";
import { z } from "zod";
import { makeAuthenticatedRequest } from "./utils.js";

const GetRisksInput = z.object({
pageSize: z
.number()
.optional()
.describe(
"Controls the maximum number of risks returned in a single response. Allowed values: 1–100. Default is 10.",
),
pageCursor: z
.string()
.optional()
.describe("Used for pagination. Leave blank to start from the first page."),
categoryMatchesAny: z
.string()
.optional()
.describe(
"Filter by risk category. Example: Access Control, Cryptography, Privacy, etc.",
),
});

export const GetRisksTool: Tool<typeof GetRisksInput> = {
name: "get_risks",
description: "List all risk scenarios in your Vanta risk register.",
parameters: GetRisksInput,
};

export async function getRisks(
args: z.infer<typeof GetRisksInput>,
): Promise<CallToolResult> {
const url = new URL("/v1/risk-scenarios", baseApiUrl());
if (args.pageSize !== undefined) {
url.searchParams.append("pageSize", args.pageSize.toString());
}
if (args.pageCursor !== undefined) {
url.searchParams.append("pageCursor", args.pageCursor);
}
if (args.categoryMatchesAny !== undefined) {
url.searchParams.append("categoryMatchesAny", args.categoryMatchesAny);
}

const response = await makeAuthenticatedRequest(url.toString());

if (!response.ok) {
return {
content: [
{ type: "text" as const, text: `Error: ${response.statusText}` },
],
};
}

return {
content: [
{ type: "text" as const, text: JSON.stringify(await response.json()) },
],
};
}