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 Variant | Purpose | Risk Level |
|---|---|---|
call_tool_read | Read-only queries | Low |
call_tool_write | Create/update operations | Medium |
call_tool_destructive | Delete/irreversible operations | High |
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:
- Tool Selection: Which variant to call (
call_tool_read/write/destructive) - Intent Parameter:
intent.operation_typemust 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
- Tool variant declares expected intent (
call_tool_destructiveexpects "destructive") intent.operation_typeis validated (MUST be "destructive")- Mismatch → REJECT with clear error message
- 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_typeMUST 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_typeMUST 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_typeMUST be "destructive"- Most permissive - allowed regardless of server annotations
Intent Parameter
The intent object is required on all tool calls:
| Field | Required | Values | Description |
|---|---|---|---|
operation_type | Yes | read, write, destructive | Must match tool variant |
data_sensitivity | No | public, internal, private, unknown | Data classification |
reason | No | String (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 Variant | Server Annotation | Result |
|---|---|---|
call_tool_read | readOnlyHint: true | ALLOW |
call_tool_read | destructiveHint: true | REJECT |
call_tool_read | No annotation | ALLOW (trust agent) |
call_tool_write | readOnlyHint: true | WARN + ALLOW |
call_tool_write | destructiveHint: true | REJECT |
call_tool_write | No annotation | ALLOW |
call_tool_destructive | Any | ALLOW (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
| Flag | Description |
|---|---|
--args | Tool arguments as JSON |
--reason | Optional reason for audit trail |
--sensitivity | Data 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
- Use the right variant: Match your intent to the appropriate tool variant
- Provide reasons: Help audit trails with clear explanations
- Classify sensitivity: Mark private data operations appropriately
- Trust retrieve_tools: Use the
call_withrecommendation - 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"
}
}
}
When unsure, use call_tool_destructive - it's the most permissive and will always succeed validation. Then refine based on retrieve_tools guidance.