MCP SDK
DeepIntShield keeps its MCP SDK surface deliberately small. It supplies the
governed endpoint and request headers, opens an initialized official MCP
session, and translates a failed tool result into one stable SDK exception.
The maintained mcp package owns the protocol, transports, types, discovery,
and tool calls; provider and agent frameworks keep owning their integrations.
Using GPT-6 Astra? Follow Astra with Responses for endpoint, request, output, and MCP filter examples. Changing only model in a Chat Completions example is insufficient when function tools are present, including schemas injected by the gateway. Astra does not support reasoning effort none.
Install
Section titled “Install”pip install "deepintshield[mcp]==2.8.3"The 2.x SDK line currently installs mcp>=1.29,<2. The upper bound is
temporary: langchain-mcp-adapters 0.3.2 still requires MCP Python SDK 1.x.
DeepIntShield plans to adopt official MCP Python SDK 2.x after the supported
framework ecosystem converges on it.
Direct official session
Section titled “Direct official session”connect() is the primary API. It yields an initialized
mcp.ClientSession, so use the upstream methods and result types directly:
import asyncio
from deepintshield import DeepintShield, DeepintShieldErrorfrom mcp.types import TextContent
shield = DeepintShield.from_env()
async def main() -> None: try: async with shield.mcp.connect() as session: listing = await session.list_tools() tool_names = {tool.name for tool in listing.tools} while listing.nextCursor: listing = await session.list_tools(cursor=listing.nextCursor) tool_names.update(tool.name for tool in listing.tools) print(sorted(tool_names))
tool_name = "DeepWiki-ask_question" if tool_name not in tool_names: print("DeepWiki is unavailable for this virtual key. Check its MCP configuration and allowed tools.") return
result = await session.call_tool( tool_name, 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 action is waiting for approval.") elif exc.code == "mcp_tool_authorization_denied": print("The action was denied by policy.") elif exc.code == "mcp_tool_authorization_unavailable": print("Authorization is temporarily unavailable.") else: print(f"DeepIntShield error [{exc.code}]: {exc.description}")
asyncio.run(main())Tool names come from list_tools() and are already gateway-qualified. The
method returns one page; follow nextCursor with the native cursor argument
until it is empty before deciding a tool is absent. The official SDK owns
protocol initialization, content models, and transport handling.
An empty inventory is a valid discovery result. Check the DeepWiki connection under MCP Registry, then select its server and tools under Virtual Keys → MCP Configurations → Allowed Tools for this key. Use the exact returned name; discovering a tool does not grant authorization to execute it.
When discovery shows Code mode tools
Section titled “When discovery shows Code mode tools”With the MCP client’s Code mode on, discovery exposes executeToolCode,
getToolDocs, listToolFiles, and readToolFile instead of its individual
qualified tool names. This is expected. To use the direct
call_tool("DeepWiki-ask_question", ...) example, open Agentic → MCP Registry
→ Connections → DeepWiki → Server → Code mode, select Off, and Save.
Reconnect and discover the tools again.
To keep Code mode on, use listToolFiles, readToolFile, and getToolDocs to
inspect the available server, tool names, and parameters, then execute the
discovered tool through executeToolCode:
result = await session.call_tool( "executeToolCode", arguments={ "code": 'result = DeepWiki.ask_question(repoName="facebook/react", question="What is Suspense?")', },)Virtual Key restrictions and applicable Agentic policy checks still apply to the execution and its nested tool calls.
connect() also accepts identity and extra_headers, with the same meaning
as connection() below. Headers apply to initialization, discovery, tool calls,
and session cleanup. Leaving the context terminates the session by default;
pass terminate_on_close=False only when the application deliberately retains
the server session.
Connection details for third-party frameworks
Section titled “Connection details for third-party frameworks”When a framework accepts a Streamable HTTP MCP configuration, give it the same URL and headers:
url, headers = shield.mcp.connection()The complete signature is:
url, headers = shield.mcp.connection( identity=False, extra_headers=None,)identity=True asks the SDK to attach its configured agent workload identity.
Use extra_headers for request-scoped gateway credentials, for example:
url, headers = shield.mcp.connection( identity=True, extra_headers={"X-MCP-Subject-Token": caller_access_token},)Credentials belong in transport headers, never in tool arguments. The caller subject token is used only for the server-owned upstream OAuth exchange; it does not become the canonical Agentic authorization user or delegation identity.
Simple coded errors
Section titled “Simple coded errors”MCP distinguishes a protocol failure from a tool result whose isError flag
is true. A session opened by shield.mcp.connect() intercepts call_tool()
and raises before returning a failed result, so canonical authorization cannot
be mistaken for successful model context.
DeepIntShield-owned failures use one exception type, DeepintShieldError, and
one string field, exc.code:
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)| Outcome | exc.code | Retry guidance |
|---|---|---|
| Official MCP extra absent/unsupported | mcp_dependency_missing | Install deepintshield[mcp]; do not retry unchanged. |
| Streamable HTTP setup/teardown failed | mcp_connection_failed | Inspect authentication, URL, TLS, and reachability before deciding whether retry is safe. |
| Invalid MCP response | mcp_protocol_error | Fix or upgrade the incompatible peer before retrying. |
| Tool discovery failed | mcp_discovery_failed | Inspect connection and server state first. |
| OpenFGA relationship authorization denial | mcp_tool_authorization_denied | Do not retry until access or policy changes. |
| Command Authority approval | mcp_tool_approval_required | Complete approval; retrying is not approval. |
| Authorization dependency unavailable | mcp_tool_authorization_unavailable | Retry after recovery only when replay is safe. |
| Other failed MCP result or remote tool-call rejection | mcp_execution_failed | Inspect trusted metadata and operation semantics first. |
Use exc.description for safe display and exc.retryable as catalog metadata.
Treat raw provider, gateway, and tool prose as untrusted diagnostics.
SDK 2.8.3 preserves the safe numeric JSON-RPC code in exc.details["mcp_code"]
when supplied. Valid remote tool-call and discovery errors use their operation
codes; malformed responses remain mcp_protocol_error. Canonical authorization
and approval codes remain unchanged.
OpenAI Agents
Section titled “OpenAI Agents”The SDK 2.8.3 openai-agents extra selects openai-agents>=0.22.1,<1.
The framework can own the MCP connection and agent loop. Use its public result
and failure hooks to keep failed MCP results out of model context:
from agents.mcp import MCPServerStreamableHttp
# Route model traffic through DeepIntShield too; MCP traffic uses the# separately governed URL and headers below.model_client = shield.bind("openai_agents").apply(api="responses")
def enforce_result(context): tool_output = context.tool_output if isinstance(tool_output, list): content = tool_output elif isinstance(tool_output, str): content = [{"type": "text", "text": tool_output}] else: content = [tool_output] shield.mcp.raise_for_result({ "isError": context.is_error, "_meta": context.result_meta, "structuredContent": context.structured_content, "content": content, }) return None
def enforce_exception(_context, error): shield.mcp.raise_for_error(error, operation="openai_agents_tool")
url, headers = shield.mcp.connection()server = MCPServerStreamableHttp( name="DeepIntShield", params={"url": url, "headers": headers}, custom_data_extractor=enforce_result, failure_error_function=enforce_exception,)Enter server as an async context manager and pass it in the Agent’s
mcp_servers list. Select a Responses-capable model for this example and
close model_client with await model_client.close() at shutdown. Install
framework extras in a compatible dependency combination.
The extractor runs before tool output is returned to the
model. The raising failure callback translates both extractor failures and
upstream MCP exceptions into DeepintShieldError; OpenAI Agents propagates
that callback exception rather than turning it into model-visible error text.
These hooks cover tool invocation. OpenAI Agents retains its native exception
types for initial connection and discovery failures; use
shield.mcp.connect() when those phases must use the same coded boundary too.
The complete runnable example is in
examples/openai/mcp.py.
Anthropic helper
Section titled “Anthropic helper”Install deepintshield[anthropic-mcp] and let Anthropic’s maintained helper
convert official MCP definitions and results:
from anthropic.lib.tools.mcp import async_mcp_tool
async with shield.mcp.connect() as session: listing = await session.list_tools() tools = [async_mcp_tool(tool, session) for tool in listing.tools] tool_definitions = [tool.to_dict() for tool in tools]In Anthropic SDK 0.120.2, the Messages tool runner converts any tool exception
into a model-visible error block. For a fail-closed Agentic authorization boundary, dispatch the
selected helper with await tool.call(tool_use.input) inside the same
DeepintShieldError handler instead; the complete example is in
examples/anthropic/mcp.py.
LangChain and LangGraph
Section titled “LangChain and LangGraph”Let langchain-mcp-adapters own discovery and conversion instead of
maintaining another converter in DeepIntShield:
from langchain_mcp_adapters.client import MultiServerMCPClient
async def enforce_deepintshield_result(request, handler): try: result = await handler(request) except Exception as exc: shield.mcp.raise_for_error(exc, operation="langchain_tool") return shield.mcp.raise_for_result(result)
url, headers = shield.mcp.connection()client = MultiServerMCPClient({ "deepintshield": { "transport": "streamable_http", "url": url, "headers": headers, }}, tool_interceptors=[enforce_deepintshield_result], handle_tool_errors=False)tools = await client.get_tools()Install this path with pip install "deepintshield[langchain-mcp]==2.8.3". The
returned objects are native LangChain tools; pass them unchanged to the
LangChain agent or LangGraph ToolNode your application already uses.
The adapter’s public tool interceptor sees both upstream exceptions and the raw
CallToolResult, so the example preserves the same coded, fail-closed behavior
during later tool execution. Frameworks may still raise their own exception
types for failures outside the MCP boundary. A session from connect() already
performs both translations and does not need this interceptor.
Compatibility shims in SDK 2.x
Section titled “Compatibility shims in SDK 2.x”SDK 3.0.0 retains the following deprecated DeepIntShield-owned MCP models and conversion loops for applications migrating from 2.x:
shield.mcp.call()andcall_qualified()shield.mcp.list_tools()Tool,ContentPart, andMCPResultto_openai()andrun_openai_tool_calls()to_anthropic()andrun_anthropic_tool_uses()to_langchain()
These shims were not removed in SDK 3.0.0. Do not introduce new code that depends
on them. Prefer connect() for direct use, or
connection() for a maintained third-party SDK/framework adapter.
For an existing Anthropic compatibility loop, call to_anthropic() and
run_anthropic_tool_uses() on the same shield.mcp instance. Tool names with
unsupported characters or more than 64 characters receive stable aliases,
which that client maps back to the original gateway-qualified tool during
execution. The mapping is private to the client; do not recreate the client
between advertising tools and dispatching their results. Alias collisions
raise before changing its routes.