Connect MCP and provider-hosted tools
Choose between portable local tools, SDK-managed MCP tools, and provider-native hosted execution without blurring trust boundaries.
Tool location is part of your security and portability contract:
- Local callable tool: your application executes a function through the portable SDK tool loop.
- SDK-managed MCP tool: your application owns the MCP client and transport; discovered MCP methods become callable SDK tools.
- Provider-hosted tool: the provider executes search, file retrieval, code, remote MCP, or another native capability.
Hosted tools are not portable merely because they are passed through the common tools option.
Availability and stability
| Surface | TypeScript | Python |
|---|---|---|
| SDK-managed MCP | createMcpToolSet() is stable |
MCP discovery and registry helpers are stable |
| Local + MCP composition | Shared tool set; advanced registries are experimental | Stable ToolRegistry |
| Provider-hosted tools | Provider-native and experimental | Provider-native and beta |
| Provider-managed approvals | Provider/capability dependent | Beta; currently integrated for OpenAI and Azure OpenAI remote MCP |
Published adapters do not imply identical hosted-tool support. Select the exact provider, model, transport, and tool class using the current provider guide.
SDK-managed MCP in TypeScript
createMcpToolSet() accepts an application-provided client with listTools() and callTool() methods:
import { Agent, createMcpToolSet } from "@zhivex-ai/sdk";
import type { McpClient } from "@zhivex-ai/sdk";
declare const myMcpClient: McpClient;
const mcpTools = await createMcpToolSet(myMcpClient, {
includeTools: ["search_docs", "read_page"],
trustServerToolAnnotations: false,
maxListPages: 10,
maxListedTools: 100,
listToolsTimeoutMs: 5_000,
callToolTimeoutMs: 20_000
});
const agent = new Agent({
model,
instructions: "Use documentation tools only when they improve the answer.",
tools: mcpTools,
maxSteps: 4
});
Opaque pagination, input schemas, declared structured outputs, cancellation, timeouts, and idempotency keys are handled by the wrapper. Server annotations are untrusted by default, so discovered calls require approval unless your application explicitly trusts the server annotations. Destructive or open-world hints still require approval.
SDK-managed MCP in Python
Python can own a stdio or HTTP MCP lifecycle through a stable registry:
import os
from zhivex_ai import (
Agent,
ApprovalDecision,
create_mcp_tool_registry,
create_openai,
mcp_http_server,
run_agent,
)
async def review_mcp_call(request):
remote_name = request.tool_metadata.get("mcp_tool_name")
if request.tool_source == "mcp" and remote_name == "search_docs":
return ApprovalDecision(approved=True)
return ApprovalDecision(approved=False, reason="MCP tool is not allowed.")
server = mcp_http_server(
name="docs",
url="https://mcp.example.com/api",
headers={"authorization": f"Bearer {os.environ['MCP_SERVER_TOKEN']}"},
timeout_ms=20_000,
)
async with await create_mcp_tool_registry(server) as tools:
agent = Agent(
name="docs_assistant",
model=create_openai()("gpt-5.6-terra"),
tools=tools,
approval_policy=review_mcp_call,
)
result = await run_agent(agent=agent, prompt="Find the retention policy.")
All discovered Python MCP tools require approval by default, even when a server advertises readOnlyHint=true. After authenticating and reviewing a server, trusted_tools={"exact_remote_name"} can bypass approval for only those exact names. Keep lifecycle cleanup, authentication, and that allowlist in server-owned configuration.
Provider-hosted tools are native
Use hosted tools only when provider execution is the product requirement. Isolate them behind an application adapter because the tool names, approval events, stored files, response references, and retention behavior are provider-specific.
TypeScript example:
import { generateText } from "@zhivex-ai/sdk";
import { createOpenAI, openAIWebSearchTool } from "@zhivex-ai/openai";
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
const result = await generateText({
model: openai("gpt-5.6-terra"),
prompt: "Verify the current release notes.",
tools: { search: openAIWebSearchTool() }
});
Python makes the native boundary explicit:
from zhivex_ai import create_openai, generate_text, openai_web_search_tool
openai = create_openai()
result = await generate_text(
model=openai.native.language_model("gpt-5.6-terra"),
prompt="Verify the current release notes.",
tools={"search": openai_web_search_tool(search_context_size="high")},
)
Portable/foundation Python models reject hosted tools rather than silently treating them as local callables.
Trust and authorization checklist
- Authenticate and pin each MCP server; constrain commands, URLs, roots, environment variables, headers, pages, tool counts, payload sizes, and deadlines.
- Treat descriptions, schemas, annotations, and tool output as untrusted data. Validate results before using them in SQL, shell commands, filesystem paths, browser actions, or prompts.
- Approval is not authorization. Bind the approver, tenant, tool version, exact arguments, expiration, and one-time decision in application state.
- Forward cancellation and idempotency into external tools. If a timed-out side effect has an unknown outcome, reconcile it before retrying.
- Do not expose provider or MCP credentials to browser code. Redact credentials and sensitive tool inputs/outputs from traces.
- Provider-hosted storage and code environments have their own region, network, retention, and billing semantics. Review those semantics for the exact provider feature.
For model-directed delegation, see subagents. For the surrounding control boundary, see production architecture.