MCP Tool Rules

MCP Tool Rules Reference

MCP Tool Rules tell MCP Boundary how tools should be exposed and handled.

The easiest mental model:

Start transparent.
Keep useful tools working.
Harden the tools that can cause damage or leak too much data.

MCP Boundary should not make a server useless by default. The purpose is to add a boundary, visibility, and selective hardening, not to force you to rebuild every server by hand.

> Advanced policy reference: Most users can configure tool access, input > rules, limits, and read-before-write in the dashboard Builder without editing > JSON. Use this reference for complex policies, reusable examples, or complete > Policy files.

For practical rule patterns and server-specific playbooks, read MCP Tool Rule Library after this reference.

What A Rule Config Looks Like

A rule config is a JSON policy file. The public docs call these MCP Tool Rules, but the JSON schema still uses the existing policy field names.

This is the complete rule config used by the local email demo:

json
{
"version": "mcp-adapter-host-policy/v1",
"servers": [
{
"server_id": "email-demo",
"tools": [
{
"downstream_tool_name": "email.search_threads",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow"
},
{
"downstream_tool_name": "email.get_thread",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow"
},
{
"downstream_tool_name": "email.create_draft",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow"
},
{
"downstream_tool_name": "email.send_email",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "block"
}
]
}
]
}

This file says:

  • use policy schema mcp-adapter-host-policy/v1
  • apply these rules to server email-demo
  • expose search, read, and draft tools
  • allow those tools to proceed to the normal Core path
  • expose send, but block it before downstream execution

The Fields You Usually Edit

For a first rule config, you mostly edit these fields:

FieldMeaning
server_idThe server/profile ID this policy applies to. It must match the server you added.
downstream_tool_nameExact native tool name discovered from the MCP server. Copy it from the Builder, not from the prefixed agent-facing name.
exposureWhether the agent can see the tool.
handling_modeUsually generic_guarded for normal MCP tools.
policy_input_modeWhether the requested call is allowed, blocked, or review-required.
argument_rulesOptional checks for exact top-level input fields and values.
result_limitsOptional output size limits.
runtime_limitsOptional repeated-call or session-call limits.
observation_rules + state_bindingOptional read-before-write checks for writes that depend on previously observed state.

Do not invent tool or field names. Use the exact native names from the Builder or from tools/list. generic_guarded is the normal handling mode. profile_guarded is reserved and does not execute through the generic packaged runtime; unsupported also returns a clear unavailable result instead of running.

How To Create Your First Rule Config

  • Add your server.
  • Import it in Setup and load its current tool list.
  • Open Builder and select a tool.
  • Set agent access and any simple input rules or limits.
  • Start with one or two obvious rules.
  • Use Save policy and provide the dashboard edit token when asked.
  • Restart the protected runtime so it loads the saved Policy file.
  • If no tool metadata is available, create a manual Policy entry only when you already know the exact native tool and field names.
  • Trigger one harmless call from your agent.
  • Check Activity.

A Builder change is not active just because it appears in the browser. It becomes active only after Save policy succeeds and the protected runtime starts or restarts with that saved profile. Complete Policy JSON remains an advanced path for a full file or a shape the structured Builder cannot represent. Unsupported shapes are preserved rather than silently rewritten.

Tool Visibility

Visibility controls whether the agent can see a tool.

Common choices:

  • visible: the agent can see the tool
  • dashboard_only: the dashboard can show the tool, but the agent does not get it
  • hidden: the tool is hidden from normal use
  • unsupported: MCP Boundary has discovered the tool, but the current runtime cannot safely handle it

Use dashboard-only or hidden for tools that should not be offered to the agent at all.

Basic: Requestability

Requestability controls whether a visible tool can be requested.

A visible tool is not automatically executed. It still goes through the checked runtime path:

requestability -> decision -> downstream execution -> activity/outcome

Transparent mode may mirror discovered tools as requestable by default. That is a usability default, not a Core bypass.

Call Rules

A call rule describes what should happen when the agent requests a tool.

Common outcomes:

  • allow: the call may proceed to the checked decision path
  • block: the call is stopped before downstream execution
  • review_required: the call is not treated as normal allowed work

When human approval and dashboard actions are configured, the current local beta includes a bounded flow for review_required. The first call stops and appears as pending in Activity. An operator can approve or reject it with the same local dashboard edit token used for protected dashboard writes. Approval does not execute the tool. The agent must retry the same call with the top-level approval_retry object returned by the blocked call.

Simple Email Example

A practical email-style policy might classify tools like this:

email.search_threads    visible + allow
email.get_thread        visible + allow
email.create_draft      visible + allow
email.send_email        visible + block
email.trash_message     dashboard_only + block

This means:

  • the agent can search and read
  • the agent can create drafts
  • direct sending is stopped
  • trash/delete tools are not offered as normal agent tools

This is only an example. Real provider servers have their own exact tool names.

Result Limit Example

Use result_limits when a tool can return too much text:

json
{
"downstream_tool_name": "email.search_threads",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow",
"result_limits": {
"max_result_bytes": 32768
}
}

This does not decide whether the tool is safe. It only bounds the result size.

Advanced: Argument Rules

Argument rules let you restrict inputs.

Examples:

  • only allow customer_id values from a test list
  • block external recipients
  • block bcc
  • require a specific state_id
  • limit a search query length
  • require dry_run=true for risky tools

Use argument rules when blocking the whole tool would be too blunt.

Example:

json
{
"downstream_tool_name": "support_db.get_customer_summary",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow",
"argument_rules": [
{
"field": "customer_id",
"required": true,
"max_chars": 32,
"allowed_values": ["C-1001", "C-1002"]
}
],
"result_limits": {
"max_result_bytes": 4096
}
}

This means:

  • customer_id must exist
  • it must be short
  • only two customer IDs are allowed
  • downstream is not called if the argument rule fails

Argument rules apply only to top-level fields. They do not use nested paths such as customer.id.

Use allowed_values or denied_values for scalar top-level values. Use items_allowed_values for scalar items in a top-level JSON array, and max_items to bound that array. For a delimited scalar string such as a comma-separated recipient field, list_separator makes the runtime check each element against allowed_values or denied_values instead of matching the whole string.

json
{
"field": "to",
"allowed_values": ["alice@example.com", "bob@example.com"],
"list_separator": ","
}

The separator may be at most 8 bytes and cannot be combined with items_allowed_values. Argument-rule values may be JSON scalars. State-fact rule values are different: they must be strings because runtime facts are matched as strings. Server validation rejects non-string state-fact values.

Path-shaped values are exact text matches. Allowing a folder string does not automatically allow every file or subfolder below it; configure filesystem roots in the filesystem server itself.

Result Limits

Result limits control how much data can come back. The current schema supports max_result_bytes, which caps the total result size in bytes up to 1 MiB. This does not decide whether the tool is safe; it only bounds the returned bytes.

Runtime limits separately bound repeated activity with max_same_fingerprint_count, max_recent_error_count, max_tool_call_count, and max_session_call_count. For visible generic_guarded tools, 0 or an omitted field uses the packaged safety baseline (3, 3, 100, and 500 respectively); it does not mean unlimited.

State Binding

State binding means the tool call must match an expected state.

Example:

Update customer C-1002 only if the current state hash still matches what was inspected.

Why this matters:

  • the agent may inspect data
  • time passes
  • the underlying data changes
  • a write based on stale state may be wrong

State binding is a way to say:

Do not execute this write if the world changed since the read.

This is more advanced than a first-run setup, but it is useful for database, ticketing, workflow, and mailbox actions.

For the normal no-code path, select the write tool in Builder, open Advanced controls, and use the optional read-before-write (state binding) recipe. Choose the read tool, confirm the shared ID field, and add only facts you know the read result actually returns. The dashboard does not execute tools or infer result schemas while you configure the recipe. Existing complex observation or binding shapes remain available in the manual advanced controls or Full Policy JSON.

A state-bound pattern usually has two parts.

First, a read tool records a bounded observation:

json
{
"downstream_tool_name": "support_db.get_customer_summary",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "allow",
"observation_rules": [
{
"source_tool": "support_db.get_customer_summary",
"resource_type": "customer",
"resource_field": "customer_id",
"ttl_seconds": 60,
"facts": [
{ "name": "status", "from_result_field": "status" },
{ "name": "version", "from_result_field": "version" }
]
}
]
}

Then a write tool requires a reference to that observation:

json
{
"downstream_tool_name": "support_db.update_customer_status",
"exposure": "visible",
"handling_mode": "generic_guarded",
"policy_input_mode": "review_required",
"state_binding": {
"observation_ref_field": "observation_ref",
"resource_type": "customer",
"resource_field": "customer_id",
"state_hash_field": "state_hash"
}
}

In plain language:

  • the agent first reads customer state
  • MCP Boundary records selected facts for a short time
  • the later write must refer to that observed state
  • if the state reference does not match, the write is not treated as normal allowed work

Optional state_fact_rules can require an observed fact, allow or deny specific string values, and express string-only conditional_requires. The Builder edits the representable string form; complex existing shapes remain preserve-only for Full Policy JSON. Non-string state-fact values are rejected by server validation.

This is useful when a write depends on what the agent previously saw.

Review Required

review_required is a policy state for work that should not silently proceed as normal allowed work.

When human approval and dashboard actions are configured in the current local beta:

  • the first call is stopped before downstream execution
  • Activity shows the pending request and lets the local operator approve or reject it
  • approval alone still does not execute the tool
  • the agent retries with the returned top-level approval_retry proof
  • expired, rejected, mismatched, or already-used approvals fail closed

The retry is a new tools/call. Put approval_retry beside arguments under params, not inside the downstream tool arguments. Replace the example IDs with the values returned for the approved item, and resend the same tool name and original arguments:

json
{
"jsonrpc": "2.0",
"id": "retry-1",
"method": "tools/call",
"params": {
"name": "example.send",
"approval_retry": {
"pending_id": "pending-...",
"approval_id": "approval-..."
},
"arguments": {
"recipient": "you@example.com",
"body": "Hello"
}
}
}

What Policies Should Not Do

A policy should not pretend to know that a tool is safe just because:

  • the tool name contains read
  • the tool description sounds harmless
  • the provider is popular
  • the server was tested once

Tools can be misleading. Use evidence and explicit rules.

Recommended First Policy

For a new server:

  • Start transparent so the server is still usable.
  • Open Builder and inspect the loaded tool surface.
  • Confirm exact native tool and field names.
  • Identify obvious destructive tools.
  • Hide or block those first.
  • Add result limits for broad list/search tools.
  • Add argument rules for sensitive fields.
  • Run one harmless tool call.
  • Check Activity.
  • Tighten from evidence, not guesses.

That gives you a usable starting point without turning the first setup into a long security project.

Quick Reference

Common starting choices:

Tool typeFirst policy choice
Search/list/read metadatavisible + allow + result limit
Create draft / prepare local changevisible + allow or review_required
Send, publish, merge, delete, trashvisible + block or dashboard_only + block
Admin/raw/freeform toolshidden + block
Large result toolsadd result_limits.max_result_bytes
Sensitive target fieldsadd argument_rules
Write based on prior readadd observation_rules and state_binding