|
| 1 | +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; |
| 2 | +import { baseApiUrl } from "../api.js"; |
| 3 | +import { Tool } from "../types.js"; |
| 4 | +import { z } from "zod"; |
| 5 | +import { makeAuthenticatedRequest } from "./utils.js"; |
| 6 | + |
| 7 | +const GetControlsInput = z.object({ |
| 8 | + pageSize: z |
| 9 | + .number() |
| 10 | + .describe("Number of controls to return (1-100, default 10)") |
| 11 | + .optional(), |
| 12 | + pageCursor: z.string().describe("Pagination cursor for next page").optional(), |
| 13 | + frameworkMatchesAny: z |
| 14 | + .array(z.string()) |
| 15 | + .describe( |
| 16 | + "Filter controls by framework IDs. Returns controls that belong to any of the specified frameworks, e.g. ['soc2', 'iso27001', 'hipaa']", |
| 17 | + ) |
| 18 | + .optional(), |
| 19 | +}); |
| 20 | + |
| 21 | +export const GetControlsTool: Tool<typeof GetControlsInput> = { |
| 22 | + name: "get_controls", |
| 23 | + description: |
| 24 | + "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. Optionally filter by specific frameworks using frameworkMatchesAny.", |
| 25 | + parameters: GetControlsInput, |
| 26 | +}; |
| 27 | + |
| 28 | +const GetControlTestsInput = z.object({ |
| 29 | + controlId: z |
| 30 | + .string() |
| 31 | + .describe( |
| 32 | + "Control ID to get tests for, e.g. 'access-control-1' or 'data-protection-2'", |
| 33 | + ), |
| 34 | + pageSize: z |
| 35 | + .number() |
| 36 | + .describe("Number of tests to return (1-100, default 10)") |
| 37 | + .optional(), |
| 38 | + pageCursor: z.string().describe("Pagination cursor for next page").optional(), |
| 39 | +}); |
| 40 | + |
| 41 | +export const GetControlTestsTool: Tool<typeof GetControlTestsInput> = { |
| 42 | + name: "get_control_tests", |
| 43 | + description: |
| 44 | + "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.", |
| 45 | + parameters: GetControlTestsInput, |
| 46 | +}; |
| 47 | + |
| 48 | +export async function getControls( |
| 49 | + args: z.infer<typeof GetControlsInput>, |
| 50 | +): Promise<CallToolResult> { |
| 51 | + const url = new URL("/v1/controls", baseApiUrl()); |
| 52 | + |
| 53 | + if (args.pageSize !== undefined) { |
| 54 | + url.searchParams.append("pageSize", args.pageSize.toString()); |
| 55 | + } |
| 56 | + if (args.pageCursor !== undefined) { |
| 57 | + url.searchParams.append("pageCursor", args.pageCursor); |
| 58 | + } |
| 59 | + if (args.frameworkMatchesAny !== undefined) { |
| 60 | + args.frameworkMatchesAny.forEach(framework => { |
| 61 | + url.searchParams.append("frameworkMatchesAny", framework); |
| 62 | + }); |
| 63 | + } |
| 64 | + |
| 65 | + const response = await makeAuthenticatedRequest(url.toString()); |
| 66 | + |
| 67 | + if (!response.ok) { |
| 68 | + return { |
| 69 | + content: [ |
| 70 | + { |
| 71 | + type: "text" as const, |
| 72 | + text: `Error: ${response.statusText}`, |
| 73 | + }, |
| 74 | + ], |
| 75 | + }; |
| 76 | + } |
| 77 | + |
| 78 | + return { |
| 79 | + content: [ |
| 80 | + { type: "text" as const, text: JSON.stringify(await response.json()) }, |
| 81 | + ], |
| 82 | + }; |
| 83 | +} |
| 84 | + |
| 85 | +export async function getControlTests( |
| 86 | + args: z.infer<typeof GetControlTestsInput>, |
| 87 | +): Promise<CallToolResult> { |
| 88 | + const url = new URL(`/v1/controls/${args.controlId}/tests`, baseApiUrl()); |
| 89 | + |
| 90 | + if (args.pageSize !== undefined) { |
| 91 | + url.searchParams.append("pageSize", args.pageSize.toString()); |
| 92 | + } |
| 93 | + if (args.pageCursor !== undefined) { |
| 94 | + url.searchParams.append("pageCursor", args.pageCursor); |
| 95 | + } |
| 96 | + |
| 97 | + const response = await makeAuthenticatedRequest(url.toString()); |
| 98 | + |
| 99 | + if (!response.ok) { |
| 100 | + return { |
| 101 | + content: [ |
| 102 | + { |
| 103 | + type: "text" as const, |
| 104 | + text: `Error: ${response.statusText}`, |
| 105 | + }, |
| 106 | + ], |
| 107 | + }; |
| 108 | + } |
| 109 | + |
| 110 | + return { |
| 111 | + content: [ |
| 112 | + { type: "text" as const, text: JSON.stringify(await response.json()) }, |
| 113 | + ], |
| 114 | + }; |
| 115 | +} |
0 commit comments