Skip to content

Tool Execution

When an LLM returns tool calls in its response, DeepIntShield does not automatically execute them. Instead, your application explicitly calls the tool execution API, giving you full control over:

  • Which tool calls to execute
  • User approval workflows
  • Security validation
  • Audit logging

The basic flow is: Chat Request → Review Tool Calls → Execute Tools → Continue Conversation.


Authentication and canonical authorization

Section titled “Authentication and canonical authorization”

The /v1/mcp/tool/execute endpoint uses the same authentication as other inference endpoints like /v1/chat/completions: authenticate every request with your Virtual Key.

Pass the key in any supported Virtual Key carrier:

Terminal window
-H "Authorization: Bearer sk-ds-your-virtual-key"
# or
-H "x-deepintshield-vk: sk-ds-your-virtual-key"
# also supported by compatible clients: x-api-key and x-goog-api-key

For details on how virtual keys work and how to scope them, see Authentication and Virtual Keys.

When Agentic-New GAF is enabled, the key must be associated with an enabled governed agent in the active workspace. Send X-Agent-Subject when one key is associated with multiple agents. If that agent profile uses an identity provider, also send its verified child workload credential in X-Agent-Token. The gateway revalidates the exact current or rotation-grace key at decision time; /mcp does not parse the raw key a second time.

With GAF enabled, every actual call—JSON-RPC /mcp, this endpoint, autonomous agent mode, and nested code-mode tools—uses the same server-side canonical decision. The decision intersects the current Virtual-Key/MCP binding, Registry action metadata, exact-workspace OpenFGA relationships, optional context policy, and Command Authority approval. Any successful plugin short circuit must pass the same boundary. The legacy direct MCP result cache is disabled for both reads and writes on this canonical path because its key does not contain the complete GAF subject, delegated identity, client generation, and policy decision. The older Agentic Tool Integrity and behavior-grant mechanisms are legacy capabilities, not additional legs in this canonical decision.

For POST /v1/mcp/tool/execute, Deny returns 403, Require-approval returns 202, and a dependency or audit failure returns 503 rather than executing the tool. Their stable response codes are mcp_tool_authorization_denied, mcp_tool_approval_required, and mcp_tool_authorization_unavailable. JSON-RPC /mcp expresses the same outcome as an MCP tool result with isError=true and a safe code, verdict, reason, decision ID, and approval ID; it does not translate a tool-level protocol result into those HTTP statuses.


The Python SDK keeps the DeepIntShield-specific layer small and delegates the protocol to the official MCP Python SDK.

Terminal window
pip install 'deepintshield[mcp]'
import asyncio
from deepintshield import DeepintShield, DeepintShieldError
from mcp.types import TextContent
shield = DeepintShield(
virtual_key="sk-ds-...",
base_url="https://app.deepintshield.com",
)
async def main() -> None:
try:
# This is an initialized official mcp.ClientSession.
async with shield.mcp.connect() as session:
tools = await session.list_tools()
print([tool.name for tool in tools.tools])
result = await session.call_tool(
"DeepWiki-ask_question",
arguments={
"repoName": "facebook/react",
"question": "What is Suspense?",
},
)
for part in result.content:
if isinstance(part, TextContent):
print(part.text)
except DeepintShieldError as exc:
if exc.code == "mcp_tool_approval_required":
print("The MCP action is waiting for approval.")
elif exc.code == "mcp_tool_authorization_denied":
print("The MCP action was denied by policy.")
elif exc.code == "mcp_tool_authorization_unavailable":
print("MCP authorization is temporarily unavailable.")
else:
print(f"DeepIntShield error [{exc.code}]: {exc.description}")
asyncio.run(main())

DeepIntShield supports two API formats for tool execution:

Use ?format=chat or omit the parameter:

Terminal window
POST /v1/mcp/tool/execute?format=chat

Request:

{
"id": "call_xyz789",
"type": "function",
"function": {
"name": "filesystem-read_file",
"arguments": "{\"path\": \"notes.txt\"}"
}
}

Response:

{
"role": "tool",
"content": "{\"key\": \"value\"}",
"tool_call_id": "call_xyz789"
}

Use ?format=responses for the Responses API format:

Terminal window
POST /v1/mcp/tool/execute?format=responses

Request:

{
"type": "function_call_output",
"call_id": "call_xyz789",
"name": "filesystem-read_file",
"arguments": "{\"path\": \"notes.txt\"}"
}

Response:

{
"type": "function_call_output",
"call_id": "call_xyz789",
"output": "{\"key\": \"value\"}"
}

LLMs often request multiple tools in a single response. Execute each through the official session, validate each result, and append all successful results before continuing the conversation:

async with shield.mcp.connect() as session:
results = []
for call in requested_calls:
result = await session.call_tool(call.name, arguments=call.arguments)
results.append(result)

With the Gateway, send one POST /v1/mcp/tool/execute request per tool call and append each returned tool message to your conversation history.


Beyond the Virtual-Key tool allow-list, DeepIntShield authorizes each MCP call against the exact workspace’s relationship graph backed by OpenFGA. The canonical intersection checks the governed agent’s permission on the derived resource, its can_use relationship to the Registry tool, and its can_execute relationship to the server-resolved named action. The current MCP adapter submits the governed agent as a direct caller; it does not populate the GAF user or delegation fields from X-MCP-Subject-Token. That header is only an upstream OAuth transport credential. Explicit control-plane decisions that supply a user additionally require acts_for, an active scoped delegation, and the user’s permission, but that must not be inferred from the MCP subject-token header.

The server owns tool/action classification. It canonicalizes the arguments to an approval digest and never trusts a caller-supplied action class. Write-like or unknown action classes require a live approval for that exact agent, tool, action, target, permission, and digest (plus user when an explicit decision actually supplies one). Every OpenFGA check uses the higher-consistency mode; workspace stores and model pins are durable and replica-coordinated.

Manage relationships from Agentic → Policy & Access, Registry actions from Agentic → Assets → Tools / Actions, and live server connections from Agentic → MCP Registry (page title: MCP Connections).


Tool execution can fail for several reasons:

  • The tool execution timed out
  • The tool does not exist, or its MCP client is disconnected
  • The tool is filtered out by configuration (tools_to_execute)
  • The Virtual Key is inactive, no longer bound to the current MCP configuration, or cannot resolve one enabled governed agent
  • Workload proof, OpenFGA, context policy, or the durable decision ledger failed
  • GAF denied the call or an exact Command Authority approval is still pending

The Python SDK exposes DeepIntShield failures through one exception and one stable string code. A session opened by shield.mcp.connect() checks every tool result automatically because JSON-RPC /mcp represents a blocked tool as a successful protocol response with isError=true:

from deepintshield import DeepintShieldError
try:
async with shield.mcp.connect() as session:
result = await session.call_tool(tool_name, arguments=arguments)
except DeepintShieldError as exc:
if exc.code == "mcp_tool_authorization_denied":
stop_without_retrying()
elif exc.code == "mcp_tool_approval_required":
show_pending_approval()
elif exc.code == "mcp_tool_authorization_unavailable":
retry_later_only_if_replay_is_safe()
else:
report_code(exc.code)

Do not place a failed tool result in model context before this check. Provider and framework libraries may additionally raise their own native exceptions; DeepIntShield-specific branching should use DeepintShieldError.code. If an external adapter creates its own session and exposes a raw CallToolResult, call shield.mcp.raise_for_result(result) in its result interceptor before model consumption.

When execution fails, /v1/mcp/tool/execute returns an error response:

{
"error": {
"type": "tool_execution_error",
"message": "Tool 'filesystem-delete_file' is not allowed for this request"
}
}

Tool execution responses are designed to be appended directly to your conversation history. Each response already includes:

  • The correct role field ("tool")
  • A matching tool_call_id for correlation
  • Properly formatted content

Append the returned message to your message list and send it back in your next chat request - no reshaping required.


Agent Mode

Enable autonomous tool execution with an eligibility allow-list

Open →

Tool Filtering

Control which tools are available per request

Open →