Tool calling, MCP and secure agent execution

Design tool contracts and control external actions with permissions, idempotency, approval and auditability.

Tool contracts

A good description says when a tool should be used, which arguments it accepts, what it returns and when it must not be called.

tool-schema.jsonjson
{
  "name": "get_order_status",
  "description": "Read an order visible to the current user",
  "parameters": {
    "type": "object",
    "properties": { "order_id": { "type": "string" } },
    "required": ["order_id"],
    "additionalProperties": false
  }
}
  • Use short, stable, verb-object names.
  • Describe limits and side effects, not only the happy path.
  • Require explicit fields and reject unknown arguments.
  • Return stable success and error structures.
  • Derive authorization from the server-side session.

Tool-calling flow

The application declares tools, the model returns a name and arguments, the server validates and executes them, then returns a structured result for the model to continue or explain failure.

Goal -> model selects tool -> server validates schema, identity and permissions -> tool executes -> server records result and trace -> model continues or delivers an answer

Independent read-only calls may run in parallel, but writes and shared state need ordering, concurrency limits and cancellation.

Server-side implementation

Treat model output as untrusted input. A minimum executor needs an allowlist, schema validation, authorization, timeout, normalized errors and tracing.

async def execute_call(call, user):
    if call.name not in ALLOWED_TOOLS:
        return error("tool_not_allowed")
    args = validate_schema(call.arguments)
    authorize(user, call.name, args)
    return await run_with_timeout(call.name, args, seconds=3)

Side-effecting tools need an idempotency key such as user_id + operation + client_request_id so retries cannot duplicate payment, orders, messages or deletion.

MCP concepts

  • Host: the agent application that owns the session and security policy.
  • Client: the connection to a particular MCP server.
  • Server: exposes tools, resources or prompts for an external system.
  • Transport: carries protocol messages, commonly local STDIO or a remote HTTP-style transport.
  • Authorization: determines access and must never be delegated to the model.

Function calling is usually a tool contract inside one model request. MCP adds cross-application discovery, connections, resources and server boundaries. An MCP server can be an adapter layer, but it still needs internal authentication.

Permissions, security and approval

  • Derive tenant identity from the session instead of trusting a model argument.
  • Require confirmation or human approval for deletion, payment, notifications and other high-impact writes.
  • Isolate web or document content from system instructions to reduce prompt injection risk.
  • Minimize, redact, audit and expire sensitive data.
  • Review third-party MCP servers, allowlist tools and lock versions.

Failure recovery and idempotency

  • Schema errors: fix arguments or re-plan; do not blindly retry.
  • Network timeouts: use bounded exponential backoff with the same idempotency key.
  • Rate limits: honor retry guidance, queue or degrade.
  • Permission failures: stop and request authorization.
  • Business conflicts: reread state before deciding whether a retry is safe.

Tool design checklist

  • Clear name, description, schema and return structure.
  • Server-enforced allowlist, permissions and tenant isolation.
  • Idempotency, audit records and compensation for every side effect.
  • Separate timeout and retry policies for model, tool and task.
  • Structured human approval for high-risk actions.
  • MCP servers, third-party APIs and document content treated as untrusted input.

Continue with agent evaluation and productionization.