Skip to main content

Intent Declaration

MCPProxy provides intent-based tool splitting - a security feature that enables fine-grained permission control in IDEs like Cursor, Claude Desktop, and other AI coding assistants.

The Problem

With a single call_tool command, IDEs can only offer all-or-nothing permission choices:

MCPProxy Tools:
[x] call_tool → Auto-approve ALL operations? (risky!)
[ ] call_tool → Ask every time? (annoying for reads)

This forces users to choose between security (asking for every operation) and convenience (auto-approving everything, including destructive operations).

The Solution: Tool Splitting

MCPProxy splits tool execution into three variants based on operation type:

Tool VariantPurposeRisk Level
call_tool_readRead-only queriesLow
call_tool_writeCreate/update operationsMedium
call_tool_destructiveDelete/irreversible operationsHigh

This enables granular IDE permission settings:

MCPProxy Tools:
[x] call_tool_read → Auto-approve (safe reads)
[ ] call_tool_write → Ask each time
[ ] call_tool_destructive → Always ask + confirm

Two-Key Security Model

Agents must declare intent in two places that must match:

  1. Tool Selection: Which variant to call (call_tool_read / write / destructive)
  2. Intent Parameter: intent.operation_type must match the tool variant
{
"name": "call_tool_destructive",
"arguments": {
"name": "github:delete_repo",
"args_json": "{\"repo\": \"test-repo\"}",
"intent": {
"operation_type": "destructive",
"data_sensitivity": "private",
"reason": "User requested repository cleanup"
}
}
}

Why two keys? This prevents:

  • Accidental misclassification (agent confusion)
  • Intentional misclassification (attack attempts)
  • Sneaking destructive operations through auto-approved read channel

Validation Chain

  1. Tool variant declares expected intent (call_tool_destructive expects "destructive")
  2. intent.operation_type is validated (MUST be "destructive")
  3. Mismatch → REJECT with clear error message
  4. Server annotation check → validate against destructiveHint/readOnlyHint

Tool Variants

call_tool_read

Execute read-only operations that don't modify state.

{
"name": "github:list_repos",
"args_json": "{\"org\": \"myorg\"}",
"intent": {
"operation_type": "read"
}
}

Validation:

  • intent.operation_type MUST be "read"
  • Rejected if server marks tool as destructiveHint: true

call_tool_write

Execute state-modifying operations that create or update resources.

{
"name": "github:create_issue",
"args_json": "{\"title\": \"Bug report\", \"body\": \"Details...\"}",
"intent": {
"operation_type": "write",
"reason": "Creating bug report per user request"
}
}

Validation:

  • intent.operation_type MUST be "write"
  • Rejected if server marks tool as destructiveHint: true

call_tool_destructive

Execute destructive or irreversible operations.

{
"name": "github:delete_repo",
"args_json": "{\"repo\": \"test-repo\"}",
"intent": {
"operation_type": "destructive",
"data_sensitivity": "private",
"reason": "User confirmed deletion of test repository"
}
}

Validation:

  • intent.operation_type MUST be "destructive"
  • Most permissive - allowed regardless of server annotations

Intent Parameter

The intent object is required on all tool calls:

FieldRequiredValuesDescription
operation_typeYesread, write, destructiveMust match tool variant
data_sensitivityNopublic, internal, private, unknownData classification
reasonNoString (max 1000 chars)Explanation for audit trail

Examples

Minimal (required only):

{
"intent": {
"operation_type": "read"
}
}

Full intent:

{
"intent": {
"operation_type": "write",
"data_sensitivity": "private",
"reason": "Creating user profile with personal information"
}
}

Server Annotation Validation

MCPProxy validates agent intent against server-provided annotations:

Tool VariantServer AnnotationResult
call_tool_readreadOnlyHint: trueALLOW
call_tool_readdestructiveHint: trueREJECT
call_tool_readNo annotationALLOW (trust agent)
call_tool_writereadOnlyHint: trueWARN + ALLOW
call_tool_writedestructiveHint: trueREJECT
call_tool_writeNo annotationALLOW
call_tool_destructiveAnyALLOW (most permissive)

Strict Mode

By default, strict validation is enabled. Configure via mcp_config.json:

{
"intent_declaration": {
"strict_server_validation": true
}
}

When strict_server_validation: false, server annotation mismatches log a warning but allow the call.

Tool Discovery

The retrieve_tools response includes annotations and guidance:

{
"tools": [
{
"name": "github:delete_repo",
"description": "Delete a GitHub repository",
"inputSchema": {...},
"annotations": {
"destructiveHint": true,
"readOnlyHint": false
},
"call_with": "call_tool_destructive"
},
{
"name": "github:list_repos",
"description": "List repositories",
"inputSchema": {...},
"annotations": {
"readOnlyHint": true
},
"call_with": "call_tool_read"
}
]
}

The call_with field recommends which tool variant to use based on server annotations.

CLI Commands

Execute tools via CLI with explicit intent:

# Read operation
mcpproxy call tool-read github:list_repos --args '{"org": "myorg"}'

# Write operation
mcpproxy call tool-write github:create_issue \
--args '{"title": "Bug", "body": "Details"}' \
--reason "Creating bug report"

# Destructive operation
mcpproxy call tool-destructive github:delete_repo \
--args '{"repo": "test"}' \
--sensitivity private \
--reason "User confirmed deletion"

CLI Flags

FlagDescription
--argsTool arguments as JSON
--reasonOptional reason for audit trail
--sensitivityData sensitivity: public, internal, private, unknown

Activity Log Integration

Intent is recorded in every activity log entry:

mcpproxy activity list
ID              TIME                 SERVER      TOOL          INTENT  STATUS  DURATION
01JG2... 2025-01-15 10:30:00 github create_issue write success 245ms
01JG2... 2025-01-15 10:29:45 github list_repos read success 123ms
01JG2... 2025-01-15 10:29:30 github delete_repo destr success 567ms

Filter by intent type:

# Show only destructive operations
mcpproxy activity list --intent-type destructive

# REST API
curl -H "X-API-Key: $KEY" "http://127.0.0.1:8080/api/v1/activity?intent_type=destructive"

Error Messages

Clear error messages help agents self-correct:

Intent mismatch:

Intent mismatch: tool is call_tool_read but intent declares write.
Use call_tool_write for write operations.

Server annotation conflict:

Tool 'github:delete_repo' is marked destructive by server.
Use call_tool_destructive instead of call_tool_read.

Missing intent:

intent.operation_type is required.
Provide intent: {operation_type: "read"|"write"|"destructive"}

IDE Configuration Examples

Cursor

In Cursor settings, configure MCP tool permissions:

MCPProxy Tools:
call_tool_read [Auto-approve]
call_tool_write [Ask each time]
call_tool_destructive [Always ask]

Claude Desktop

In claude_desktop_config.json:

{
"mcpServers": {
"mcpproxy": {
"permissions": {
"call_tool_read": "auto",
"call_tool_write": "ask",
"call_tool_destructive": "always_ask"
}
}
}
}

Best Practices

  1. Use the right variant: Match your intent to the appropriate tool variant
  2. Provide reasons: Help audit trails with clear explanations
  3. Classify sensitivity: Mark private data operations appropriately
  4. Trust retrieve_tools: Use the call_with recommendation
  5. Configure IDE permissions: Enable auto-approve for reads, require confirmation for destructive

Migration from call_tool

The legacy call_tool has been removed. Update your integrations:

Before:

{
"name": "call_tool",
"arguments": {
"name": "github:create_issue",
"args_json": "{...}"
}
}

After:

{
"name": "call_tool_write",
"arguments": {
"name": "github:create_issue",
"args_json": "{...}",
"intent": {
"operation_type": "write"
}
}
}
Choosing the Right Variant

When unsure, use call_tool_destructive - it's the most permissive and will always succeed validation. Then refine based on retrieve_tools guidance.