Skip to content

Commit 04bf3e3

Browse files
authored
Merge pull request #13 from VantaInc/showzeb/controls
Adding tools to fetch controls
2 parents de6ebf3 + 76d4d57 commit 04bf3e3

File tree

5 files changed

+174
-9
lines changed

5 files changed

+174
-9
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ A <a href="https://modelcontextprotocol.com/"> Model Context Protocol </a> serve
2020
- Monitor framework completion progress and compliance status
2121
- Get specific control details that map to automated tests and required documentation
2222

23-
### Document and Evidence Management
23+
### Security Control Management
2424

25-
- Upload compliance documentation and evidence files required for audits
25+
- List all security controls across all compliance frameworks in your account
26+
- View control names, descriptions, framework mappings, and implementation status
27+
- Get specific tests that validate each security control
28+
- Understand which automated tests monitor compliance for specific controls
2629

2730
### Multi-Region Support
2831

@@ -38,6 +41,8 @@ A <a href="https://modelcontextprotocol.com/"> Model Context Protocol </a> serve
3841
| `deactivate_test_entity` | Temporarily suppress alerts for a specific failing resource during planned maintenance, system updates, or while remediation is in progress. Requires a business justification and end date. Helps manage security alerts during planned operational activities without compromising audit trails. |
3942
| `get_frameworks` | List all compliance frameworks available in your Vanta account (SOC 2, ISO 27001, HIPAA, GDPR, FedRAMP, PCI, etc.) along with completion status and progress metrics. Shows which frameworks you're actively pursuing and their current compliance state. |
4043
| `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. |
44+
| `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. |
45+
| `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. |
4146
| `upload_document` | Upload compliance documentation and evidence files to Vanta. Used for policy documents, procedures, audit evidence, and proof of security control implementation. Supports the documentation requirements needed for compliance audits and framework certification. |
4247

4348
## Configuration

src/eval/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ OPENAI_API_KEY="your_openai_api_key_here" node build/eval/eval.js
4040

4141
## Test Cases
4242

43-
The evaluation includes 9 test cases covering:
43+
The evaluation includes 11 test cases covering:
4444

4545
### **Tool Selection Tests**
4646

@@ -51,6 +51,8 @@ The evaluation includes 9 test cases covering:
5151
- **Framework Listing**: `get_frameworks` for available frameworks
5252
- **Control Requirements**: `get_framework_controls` for specific framework details
5353
- **Status Percentage**: `get_frameworks` for completion percentages
54+
- **Control Listing**: `get_controls` for all security controls
55+
- **Control Tests**: `get_control_tests` for tests validating specific controls
5456

5557
### **Negative Tests**
5658

@@ -75,8 +77,8 @@ The evaluation includes 9 test cases covering:
7577
7678
📊 Final Results
7779
================
78-
✅ Passed: 9/9 tests
79-
❌ Failed: 0/9 tests
80+
✅ Passed: 11/11 tests
81+
❌ Failed: 0/11 tests
8082
📈 Success Rate: 100%
8183
🎉 All tests passed! Tool calling behavior is working correctly.
8284
```

src/eval/eval.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import {
99
GetFrameworksTool,
1010
GetFrameworkControlsTool,
1111
} from "../operations/frameworks.js";
12-
import { UploadDocumentTool } from "../operations/documents.js";
12+
import {
13+
GetControlsTool,
14+
GetControlTestsTool,
15+
} from "../operations/controls.js";
1316

1417
// Format all tools for OpenAI
1518
const tools = [
@@ -56,9 +59,17 @@ const tools = [
5659
{
5760
type: "function" as const,
5861
function: {
59-
name: UploadDocumentTool.name,
60-
description: UploadDocumentTool.description,
61-
parameters: zodToJsonSchema(UploadDocumentTool.parameters),
62+
name: GetControlsTool.name,
63+
description: GetControlsTool.description,
64+
parameters: zodToJsonSchema(GetControlsTool.parameters),
65+
},
66+
},
67+
{
68+
type: "function" as const,
69+
function: {
70+
name: GetControlTestsTool.name,
71+
description: GetControlTestsTool.description,
72+
parameters: zodToJsonSchema(GetControlTestsTool.parameters),
6273
},
6374
},
6475
];
@@ -128,6 +139,18 @@ const testCases: TestCase[] = [
128139
expectedParams: {},
129140
description: "Should call get_frameworks to get SOC2 completion percentage",
130141
},
142+
{
143+
prompt: "List all security controls in my Vanta account",
144+
expectedTool: "get_controls",
145+
expectedParams: {},
146+
description: "Should call get_controls to list all available controls",
147+
},
148+
{
149+
prompt: "Show me the tests for control ID access-control-1",
150+
expectedTool: "get_control_tests",
151+
expectedParams: { controlId: "access-control-1" },
152+
description: "Should call get_control_tests for specific control",
153+
},
131154
{
132155
prompt: "What programming tests should I write for my API?",
133156
expectedTool: "none",

src/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ import {
1515
getFrameworks,
1616
} from "./operations/frameworks.js";
1717
import { UploadDocumentTool, uploadDocument } from "./operations/documents.js";
18+
import {
19+
GetControlsTool,
20+
GetControlTestsTool,
21+
getControls,
22+
getControlTests,
23+
} from "./operations/controls.js";
1824
import { initializeToken } from "./auth.js";
1925

2026
const server = new McpServer({
@@ -59,6 +65,20 @@ server.tool(
5965
getFrameworkControls,
6066
);
6167

68+
server.tool(
69+
GetControlsTool.name,
70+
GetControlsTool.description,
71+
GetControlsTool.parameters.shape,
72+
getControls,
73+
);
74+
75+
server.tool(
76+
GetControlTestsTool.name,
77+
GetControlTestsTool.description,
78+
GetControlTestsTool.parameters.shape,
79+
getControlTests,
80+
);
81+
6282
server.tool(
6383
UploadDocumentTool.name,
6484
UploadDocumentTool.description,

src/operations/controls.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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

Comments
 (0)