{
  "question": "How do I separate read-only analysis MCP tools from side-effecting tools and lazy-load by task?",
  "slug": "how-to-separate-read-only-mcp-tools-from-side-effecting-and-lazy-load-by-task",
  "url": "https://www.rightshold.com/answers/how-to-separate-read-only-mcp-tools-from-side-effecting-and-lazy-load-by-task",
  "short_answer": "Do not dump every connected MCP server's full tools/list into the model at session start. Split the catalog into a read-only analysis set (search, fetch, get, list) and a side-effecting set (write, send, pay, delete, deploy), keep side-effecting tools out of context until the task actually needs them, and record which tool name plus which schema/version you exposed when you load it. MCP client best practices (2025-11-25) call this progressive discovery: fetch tools/list host-side, give the model a lightweight search or catalog meta-tool, and inject full definitions only as needed — especially once tool definitions take more than a small share of the context window. Treat readOnlyHint and destructiveHint as untrusted hints unless the server is one you pin and trust; enforce the split with your own allowlists, separate credentials, and human approval for anything that writes, sends, or spends. Pair check_tool_integrity (or precall_check) before enabling a write toolset, and record_ai_run when you want a signed receipt of the selected tool fingerprint for the run.",
  "checklist": [
    "Inventory every connected MCP server's tools into two buckets you own: analysis/read (no environment change) vs side-effecting (write, send, pay, delete, mutate). Do not trust the tool name alone.",
    "Keep a host-side full tools/list (or per-server cache). Do not inject every schema into the model prompt by default.",
    "Expose a small always-on catalog: search_tools / list_toolsets / enable_toolset (or your host's equivalent), with one-line descriptions and a read_only vs side_effect flag per entry.",
    "Lazy-load by task: when the model (or a skill) picks a toolset, inject only that tool's full inputSchema — append after the cache breakpoint, or route calls through a stable call_tool meta-tool so the tools array stays cache-friendly.",
    "Gate side-effecting enablement: require a separate credential or scope, and request_human_approval (or your own confirm) before the first write tool of a session or task.",
    "On enable, record tool name, server URL/serverInfo.version, and a hash of the tool definition (name + description + inputSchema + annotations). Re-check on notifications/tools/list_changed.",
    "Treat annotations as routing hints for trusted servers only. readOnlyHint defaults to false and destructiveHint to true; unannotated tools are side-effecting until proven otherwise by your allowlist and credentials.",
    "After the run, seal which tools were selected and which versions with record_ai_run (tool names + definition hashes in params). Use check_tool_integrity or precall_check before unfamiliar write servers."
  ],
  "sections": [
    {
      "heading": "Split the catalog before the model sees it",
      "points": [
        "MCP client best practices warn that naive hosts pass every connected server's tool definitions into the model at the start of each conversation; with dozens of servers that can consume most of the context window before the user message is read.",
        "Progressive discovery: the host still calls tools/list, but defers injecting definitions. A lightweight search_tools (or list_toolsets) meta-tool returns names and one-line descriptions; get_tool_details or enable_toolset loads the full schema only for the chosen tool or source.",
        "Put the risk split in your catalog metadata, not only in the model's judgment: mark each entry analysis vs side_effect from your allowlist. A tool that mostly reads but also writes a cache, bumps a counter, or marks notifications seen is not analysis-only.",
        "GitHub's MCP server is a concrete pattern at the server layer: read-only mode and toolsets let you load repos/issues without write tools. Your host should mirror that idea across every server, including ones that lack a built-in /readonly mode."
      ]
    },
    {
      "heading": "Lazy-load by task, record what was selected",
      "points": [
        "Switch to progressive discovery when tool definitions take a meaningful share of the context window (MCP's client guidance suggests thresholds around 1%–5% as a starting point). For a handful of tools, loading all is fine.",
        "Preserve prompt caching: appending newly discovered definitions after the cache breakpoint, or routing every call through one stable call_tool({name, args}) meta-tool, avoids reshuffling the tools array mid-conversation.",
        "Refresh the search index when a server sends notifications/tools/list_changed. A write tool that appears after you reviewed a read-only set is a rug-pull class event — surface it before enable_toolset succeeds.",
        "Record the selection: tool name, serverInfo.version (or package pin), and SHA-256 of the canonical tool definition you actually injected. That is the schema/version the agent reasoned over; chat transcripts alone are not enough for audit."
      ]
    },
    {
      "heading": "Hints are not the boundary",
      "points": [
        "Tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) shipped in the 2025-03-26 MCP revision. The schema says they are hints and are not guaranteed to describe behavior faithfully; clients must treat them as untrusted unless they come from trusted servers.",
        "Use annotations from servers you pin to decide confirmation UX (for example auto-approve a trusted readOnlyHint: true fetch). Enforce safety with least-privilege credentials, a client allowlist, and human approval for side effects — the same posture as Are MCP tool annotations like readOnlyHint a security guarantee?.",
        "Lazy-loading fewer schemas is not authorization. A read-only analysis pass that later enables a write toolset still needs the write credential and approval path; withholding the schema only reduces accidental selection."
      ]
    },
    {
      "heading": "What can go wrong",
      "points": [
        "Every server's full tools/list is pasted into the system prompt; the model picks a send_email tool while \"just researching\".",
        "You rely on readOnlyHint: true from an unpinned third-party server; the tool still writes.",
        "enable_toolset loads write tools for the whole session after one yes, with no per-task re-check or definition hash.",
        "You lazy-load by name but never record schema version; after list_changed the agent is using a different tool than the one you reviewed.",
        "Prompt-cache thrash from re-sorting the tools array every turn costs more tokens than progressive discovery saved."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "ask": "Before I enable write tools for this task, check that this MCP server's tools/list fingerprint still matches what I recorded for the read-only analysis set, and flag any new or changed side-effecting tools.",
    "mcp_call": {
      "tool": "check_tool_integrity",
      "arguments": {
        "url": "https://mcp.example.com",
        "expected_tools_sha256": "<sha256 of the reviewed tools/list or selected tool definitions>"
      }
    },
    "also": [
      "Free on listed servers when used via precall_check; check_tool_integrity reports tools added, removed, or changed since the fingerprint you recorded.",
      "precall_check (free, no key) combines registry listing, integrity probe, and invisible-text scan before you trust a new server.",
      "request_human_approval before the first side-effecting call of a task; put tool name and arguments_sha256 in the action object.",
      "record_ai_run can seal which tool names and definition hashes were selected for the run without uploading the full transcripts."
    ],
    "scope": "Rightshold checks MCP listings and recorded tool fingerprints and can record human approvals and AI-run receipts for agents that call it. It does not sit in your host's context window, split your tool catalog, or stop a client that injects every tools/list into the model. Enforce progressive discovery and the read vs write split in your MCP host. Not legal advice."
  },
  "sources": [
    {
      "title": "MCP: Client best practices — progressive tool discovery (2025-11-25)",
      "url": "https://modelcontextprotocol.io/docs/2025-11-25/develop/clients/client-best-practices"
    },
    {
      "title": "MCP: Tool annotations as risk vocabulary (March 16, 2026)",
      "url": "https://blog.modelcontextprotocol.io/posts/2026-03-16-tool-annotations/"
    },
    {
      "title": "MCP specification (2025-11-25): Tools — annotations are untrusted unless from trusted servers",
      "url": "https://modelcontextprotocol.io/specification/2025-11-25/server/tools"
    },
    {
      "title": "GitHub MCP Server: remote server (toolsets, /readonly, X-MCP-Readonly)",
      "url": "https://github.com/github/github-mcp-server/blob/main/docs/remote-server.md"
    },
    {
      "title": "Are MCP tool annotations like readOnlyHint a security guarantee? (Rightshold)",
      "url": "https://www.rightshold.com/answers/are-mcp-tool-annotations-like-readonlyhint-a-security-guarantee"
    }
  ],
  "related": [
    {
      "title": "Are MCP tool annotations like readOnlyHint a security guarantee?",
      "url": "https://www.rightshold.com/answers/are-mcp-tool-annotations-like-readonlyhint-a-security-guarantee"
    },
    {
      "title": "How to detect MCP tool description changes (rug pulls)",
      "url": "https://www.rightshold.com/answers/how-to-detect-mcp-tool-description-changes-rug-pull"
    },
    {
      "title": "How to cache MCP tool results safely",
      "url": "https://www.rightshold.com/answers/how-to-cache-mcp-tool-results-safely"
    },
    {
      "title": "How to give an MCP server least-privilege access to GitHub",
      "url": "https://www.rightshold.com/answers/how-to-give-an-mcp-server-least-privilege-access-to-github"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-11-06"
}