Skip to content
/docs

MCP Integration

Use Veto with Model Context Protocol (MCP) tool definitions.

Veto supports Model Context Protocol (MCP) tool definitions through provider adapters and a guarded callTool forwarder.

MCP tools are schema-only. They do not contain an execute function, so veto.wrap() is for executable in-process tools and veto.wrapMCPTools() is for MCP forwarding. If you want to protect an MCP server you don't own or can't modify, skip to the MCP Gateway guide instead.

Quick start

Pass MCP tool definitions plus your actual MCP callTool implementation:

import { Veto } from 'veto-sdk';

const veto = await Veto.init();
const { tools, callTool } = veto.wrapMCPTools(mcpToolDefinitions, {
  callTool: (input) => mcpClient.callTool(input),
});

// Give `tools` to the model. Use guarded `callTool` when it requests a tool.
await callTool({
  name: 'read_file',
  arguments: { path: '/repo/package.json' },
});

How MCP tools differ

MCP tools use inputSchema instead of parameters:

// MCP tool format
const mcpTool = {
  name: "read_file",
  description: "Read a file from disk",
  inputSchema: {
    type: "object",
    properties: {
      path: { type: "string" },
    },
    required: ["path"],
  },
};

// OpenAI tool format (what Veto normalizes to internally)
const openAITool = {
  type: "function",
  function: {
    name: "read_file",
    description: "Read a file from disk",
    parameters: {
      type: "object",
      properties: {
        path: { type: "string" },
      },
      required: ["path"],
    },
  },
};

When you register MCP tools for Charter, the SDK converts inputSchema to Veto tool parameters. Policies and rules reference tool arguments the same way regardless of the source format.

Manual adapter

For advanced use cases, you can use the adapter functions directly:

import { fromMCP, isMCPTool } from 'veto-sdk/providers';

// Check if a tool is MCP format
if (isMCPTool(tool)) {
  const normalized = fromMCP(tool);
  // normalized has OpenAI-style `parameters` instead of `inputSchema`
}

Example: MCP server with Veto policies

import { Veto } from 'veto-sdk';

// Your MCP server tools
const mcpTools = [
  {
    name: "execute_command",
    description: "Run a shell command",
    inputSchema: {
      type: "object",
      properties: {
        command: { type: "string" },
        cwd: { type: "string" },
      },
      required: ["command"],
    },
  },
  {
    name: "write_file",
    description: "Write content to a file",
    inputSchema: {
      type: "object",
      properties: {
        path: { type: "string" },
        content: { type: "string" },
      },
      required: ["path", "content"],
    },
  },
];

const veto = await Veto.init();
const safeMcp = veto.wrapMCPTools(mcpTools, {
  callTool: (input) => mcpClient.callTool(input),
});

await safeMcp.callTool({
  name: "execute_command",
  arguments: { command: "ls", cwd: "/repo" },
});

With a YAML rule like:

rules:
  - id: restrict-commands
    name: Block dangerous shell commands
    action: block
    tools:
      - execute_command
    conditions:
      - field: arguments.command
        operator: matches
        value: "^(rm -rf|sudo|chmod 777)"

Exports

ImportWhat
fromMCPConvert a single MCP tool to Veto's internal format
isMCPToolType guard, returns true if the tool has inputSchema
Veto.wrapMCPTools()Guard a schema-only MCP callTool forwarder before the upstream server runs

Provider adapters are available from veto-sdk/providers; wrapMCPTools() is a Veto client method.

Register MCP tools for Charter

If you want Charter to draft policies against an MCP server's tool inventory, register the MCP tools/list output with the cloud SDK:

import { Veto } from "veto-sdk";

const veto = await Veto.init({
  baseUrl: "https://api.veto.so",
  apiKey: process.env.VETO_API_KEY!,
});

await veto.registerTools([
  {
    name: "execute_command",
    description: "Run a shell command",
    parameters: [
      { name: "command", type: "string", required: true, description: "Command to run" },
      { name: "cwd", type: "string", required: false, description: "Working directory" },
    ],
  },
]);

const res = await fetch("https://api.veto.so/v1/charter/turn", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Veto-API-Key": process.env.VETO_API_KEY!,
  },
  body: JSON.stringify({
    message: "Shell commands that change permissions require an engineering lead approval.",
    mode: "compile",
    surface: "mcp",
  }),
});
const draft = await res.json();

registerTools() accepts a parameters array where each entry maps to a JSON Schema property. Include required, description, type, and any constraint hints.


Not your MCP server?

If you're connecting to a third-party MCP server or want to enforce policies without modifying the server code, use the MCP Gateway instead.

The gateway runs as a proxy: your MCP client calls POST /v1/mcp/:slug (managed) or veto mcp serve (self-hosted), Veto validates every tools/call against your policies, and the upstream receives the request only if it passes. No SDK, no code changes on the server side.

Polymarket agents: See the Polymarket With Veto MCP guide for a ready-made MCP sidecar that wraps the Polymarket CLI with Veto policy guardrails. It covers tool coverage, policy profiles, simulation vs live trading, and economic authorization.