Share this article

Give your agents capabilities
Accelerating security solutions for small businesses Tagore offers strategic services to small businesses. | A partnership that can scale Tagore prioritized finding a managed compliance partner with an established product, dedicated support team, and rapid release rate. | Standing out from competitors Tagore's partnership with Vanta enhances its strategic focus and deepens client value, creating differentiation in a competitive market. |
This blog is part of our Trustcraft series, in which we dig into Vanta’s approach to building with AI. Read the first blog in this series to learn more about how we define Trustcraft.
A few years ago, I walked into a coffee shop and, feeling spontaneous, ordered a custom coffee combo I had never tried before:
- Chai latte
- Iced
- Almond milk
- Two shots of espresso
From the first sip, it instantly became one of my favorite combinations. A few weeks later, I went to order it again. However, this time I ordered it through the coffee shop's mobile app:
- Chai latte
- Iced
- Almond milk
- ...
...but there was no option in the app to add espresso. While ordering the drink was entirely possible via the in-person cafe experience, the mobile app wasn’t capable of delivering the same outcome.
The takeaway here is that the more surfaces a web platform has, the more scalable and composable its capabilities need to be to serve the same outcomes, regardless of surface.
Here’s how Vanta crafts our capability layer across surfaces so users and their agents are empowered to build with Vanta wherever they like.
What is a capability?
Over the years, platforms have described what they can do in many ways: actions, endpoints, APIs, etc.
At Vanta, we define these as capabilities.
- Capability: A task the Vanta platform can perform (e.g., "create a control")
The purpose of capabilities is to serve outcomes for users.
- Outcome: A customer goal achieved via a series of capabilities (e.g., "SOC 2 readiness")
Capabilities can be called from a variety of surfaces: a web user interface, REST API endpoints, CLIs, SDKs, or, more recently, MCPs. However, agents have drastically changed how these capabilities can be leveraged.
How agents reframed capability usage
Whether building a button or writing script, invoking platform capabilities has historically required some degree of upfront configuration. Seldom would a user configure a call to an API that wasn’t reusable. Configuring API calls wasn’t a very “disposable” action.
With agents, one key factor changes: speed of configuration. Agents can configure calls to multiple capabilities back-to-back, and even in parallel, with no upfront configuration needed beyond a prompt. It’s like having a personal integrations engineer who can cobble together ad hoc workflows in a matter of milliseconds. As a result, AI-driven traffic across the web surged by 7,815% in 2025 alone.
However, agents haven’t just changed how capabilities are consumed; they also change how they should be built.
Vanta’s capability lifecycle: service to surface

At Vanta, we're not slapping an MCP wrapper on some legacy REST endpoints and calling it a day. To us, that's an "AI sprinkle." Instead, we're building out a capability layer driven by outcomes.
Often, we’ve been surprised to find that certain user outcomes are already fully achievable with the capabilities currently available to an agent. When we find a gap, that’s when we craft a new capability.
We track coverage per-capability to ensure parity across all surfaces. Each stage is highlighted below.
- Service: The capability is defined once, containing business logic and authorization
- REST: The service is referenced by REST and exposed via an OpenAPI Specification
- CLI: A CLI is derived from the OpenAPI Specification
- Vanta Agent: The service is referenced by a tool available to the Vanta Agent
- MCP: The tool is extended to be leveraged by the Vanta MCP server
- REST: The service is referenced by REST and exposed via an OpenAPI Specification
What makes a good capability
A capability built specifically for REST may differ from a capability built specifically for an MCP. How do we navigate building distinct capabilities that are shared across all surfaces at Vanta? It comes down to building capabilities that are:
- Composable
- Contextual
- Secure
- Scalable
Composable
Composable capabilities strike a balance of being distinct while also complementary to other tools. The addPersonnelGroupMembers tool demonstrates a concrete action, the descriptions of its inputs, and how other tools like listPersonnelGroups and listPersonnel additionally serve as complementary tools to its inputs. Rather than capabilities that trigger large bespoke workflows, smaller, more scoped tools allow agents more flexibility.
export const addPersonnelGroupMembersTool = mutativeScopedToolWithDeps({
// ...
inputSchema: z.object({
groupId: z
.string()
.describe("The ID of the group. Get this from listPersonnelGroups tool."),
personnelRecordIds: z
.array(z.string())
.min(1)
.describe("IDs of the personnel records to change membership for. Get these from listPersonnel tool."),
}),
execute: async (args, toolCtx, authorizationContext) => {
// A single call to the associated service for adding group members
const frontdoor = toolCtx.personnelGroupFrontdoorFactory({ authorizationContext });
return await frontdoor.addGroupMembers({
groupId: args.groupId,
personnelRecordIds: args.personnelRecordIds,
});
},
// ...
});
Contextual
Capabilities with rich context in their descriptions and outputs have been a best practice in API design for years: good descriptions, informative error responses, etc. With agents as rapid power users, the need for rich context only increases. The createControls tool demonstrates this not only with its comprehensive description, but also with its fine-tuned error states so agents can dynamically course-correct to achieve their outcome.
/** Codes the tool reports: the frontdoor's validation codes plus a runtime create failure. */
export type CreateControlErrorCode =
| "err_duplicate_shorthand_name_in_request" // the request repeats an ID
| "err_shorthand_name_in_use" // the ID already exists in Vanta
| "err_invalid_framework"
| "err_invalid_framework_section"
// ...seven more
| "err_create_failed";
export interface CreateControlError {
readonly type: CreateControlErrorCode; // branch on this
readonly values: readonly string[]; // the offending ids
readonly message: string; // relay this to the user
}
export const createControlsTool = mutativeScopedTool({
title: AiTool.CREATE_CONTROLS, // "createControls"
description:
"Create one or more custom controls in the organization's compliance program. " +
"Validates each control first and creates only the valid ones; controls that fail " +
"validation are returned in `errors` with a human-readable message and a machine code.",
// ...
execute: async (args, toolCtx, authorizationContext) =>
const frontdoor = new ControlsFrontdoorApi({ authorizationContext });
// A validation step to decide which controls are valid, and why.
const { valid, invalid } = await frontdoor.validateControlsToCreate({ controls: inputControls });
const { controls } = await frontdoor.bulkCreateControls({ controls: valid /* ... */ });
const errors = formatErrors(groupValidationErrors(invalid));
// Return controls and context detailing the result
return {
message: buildSummaryMessage(controls.length, args.controls.length, errors.length),
controls,
errors,
};
},
});
Secure
Endpoint security is critical, especially when capabilities are exposed across multiple surfaces. The key isn’t to rebuild authorization for each surface, which could cause fragmented authorization, but to root authorization at the shared service layer at the core of the capabilities. The create capability for the risk register demonstrates this well, by providing a secure “frontdoor” function that’s authorized via an annotation for the risk:manage permission.
@authorize({ resourceType: "Organization", permission: "risk:manage" })
async create(args: {
name: string;
description?: string;
}): Promise<RiskRegisterView> {
const created = await this.writeService.create({
// ...
});
return toRiskRegisterView(created);
}
Scalable
Capabilities must be scalable and adaptable to any surface. This scalability comes naturally by building composable, contextual, and secure capabilities, but it’s also a matter of ensuring that business logic is stored cleanly within the services themselves. The listPolicyTemplates tool demonstrates this by making a single call to its corresponding frontdoor function as a lightweight wrapper. REST, MCP, CLI, the Vanta Agent, and even the Vanta web app UI can call the capability in the same manner for parity across surfaces.
export const listPolicyTemplatesTool = domainScopedTool({
// ...
execute: async (_args, _toolCtx, authorizationContext) => {
const frontdoorApi = new PolicyTemplateFrontdoorApi({
authorizationContext,
});
return await frontdoorApi.listTemplates();
},
// ...
});
Meeting builders where they are
There’s an attention to detail that goes into crafting a unified capability platform. However, by rooting our platform’s capabilities in customer outcomes and scaling them with parity to users’ and agents’ preferred surfaces, we’ve curated a platform that supports builders where they’re at.
And don't worry, my coffee shop's mobile app eventually added the ability to add espresso to chai lattes, but it’s a reminder to build composable, contextual, secure, and scalable capabilities that scale across surfaces so users and agents can achieve their outcomes regardless of surface.
Want to work on projects and experiments like this? The Vanta engineering team is growing. See open roles.





FEATURED VANTA RESOURCE
The ultimate guide to scaling your compliance program
Learn how to scale, manage, and optimize alongside your business goals.

















