MCP connected app integration standards

The MCP server establishes the framework for connecting your app's functions (MCP tools) to the Connected Apps Platform. To enable it to more reliably select, call, and process the results of your service, your MCP configuration must adhere strictly to the following standards.

Guiding principles

For each function, focus on user intent and use cases, and not the function's capabilities. The description is your primary way to tell the model what your tool is for and what use cases it can respond to.

Versioning

To support scalable and robust MCP deployments, adopt a structured URI architecture that utilizes path-based versioning—for example, /v1, /v2. Doing so helps you maintain backward compatibility, and gate new features. Complementing this with provider-specific subdomains, such as https://gemini.mcp.company.com/mcp/v1, allows for granular traffic management and the optimization of tool definitions tailored to the unique context and processing behaviors of different AI models.

MCP protocol version compatibility

The Connected Apps Platform supports the Model Context Protocol (MCP) specification up to version 2025-11-25. Newer specification releases are not yet supported.

  • Developer responsibility: You are responsible for ensuring your MCP server strictly adheres to a protocol version supported by the Connected Apps Platform (2025-11-25) during the initialize handshake and across all tool and schema definitions.
  • Platform backward compatibility: As the Connected Apps Platform adopts newer MCP specification versions in the future, our client will maintain backward compatibility with previously supported protocol versions so existing production integrations continue to function without disruption.
  • Managing upgrades: When you are ready to adopt a newly supported MCP specification or introduce schema changes, expose those updates on a new versioned URI path (for example, /mcp/v2) while maintaining your existing endpoint (/mcp/v1) for active traffic.

Changes to MCP schemas are permitted between app versions. We recommend that you leverage this feature and version the MCP endpoints to support a gentle rollout of new features, or strictly rely on backward compatible changes.

Transport protocol specification

The Connected Apps Platform strictly supports the Streamable HTTP transport Model Context Protocol specification version 2025-03-26 and higher. It does not support the legacy HTTP with SSE transport described by the 2024-11-05 specification.

  • Single HTTP endpoint. The MCP endpoint provided in the Connected Console (for example, https://api.example.com/mcp) is the single URL where applicable Google products send all JSON-RPC requests (initialize, tools/list, tools/call) through standard HTTP POST.
  • SSE streaming within streamable HTTP. Legacy multi-endpoint SSE transport is unsupported, but Server-Sent Events such as text/event-stream are supported within Streamable HTTP. Outgoing requests from applicable Google products include Accept: application/json, text/event-stream, meaning your server can either return a standard application/json payload or stream incremental responses using Content-Type: text/event-stream directly in the HTTP POST response body.
  • GET stream & 405 Method Not Allowed. Clients may attempt an optional GET stream on the endpoint for server-to-client notifications. If your server is stateless or request-response only, returning HTTP 405 Method Not Allowed is explicitly supported and handled cleanly by the client proxy of the Google applicable product.

Server instructions

The Connected Apps Platform is optimized to ingest server instructions, allowing for a degree of control over the model's behavior not available by other means.

Follow the server instructions.

Treat as a "model hint" with usage context

Follow these guidelines with regard to providing hints to models.

  • Write instructions as a high-level "hint" or guide to help the LLM understand the server's overall purpose and capabilities.
  • Describe what it can do, instead of listing features or describing how the model should use the server's tools and resources. If the model understands the "what" correctly, it can determine the how fairly well based on the user journey.
  • Assume this text will be added directly to the model's system prompt. Keep it clear, directive, and token-efficient.

Do complement, and don't duplicate

Use this space for global context that applies across all tools, avoiding redundancy with individual tool descriptions.

Server instructions examples

The following examples show do's and don'ts pertaining to properly writing server instructions.

Recommended Not recommended

Act as a specialized GitHub repository maintainer. This server provides deep access to repository metadata, issue tracking, and PR workflows. Use it to maintain project health, automate triaging, and ensure code standards. Assume all actions are performed within the context of the authenticated organization unless specified. Prioritize identifying stale threads or unlinked PRs to keep the repository organized.

Concise, directive and complementary to the model

  • Defines the "what" (project health, triaging) rather than "how"
  • Mentions authentication scope which applies to all tools

This server has tools for GitHub. You should use the list_issues tool when the user asks for issues. You can use create_pull_request to make a PR. Use these tools carefully to help the user manage their code. Always check the repository name before calling a tool. If the user asks for a summary, call the tools first and then summarize the output

  • don't relist tool names,
  • don't tell the model "how" to think which can conflict with the model's built-in reasoning that will use user context to make the right choice.
  • does not explain the nature of server's access or its specific constraints)

Tool guidelines

The following guidelines describe best practices for creating MCP server configurations for various functions, or tools, within your app.

Tool naming conventions

Observe the following guidelines for naming your tool:

Use intuitive, human-readable names

Use human-readable names with clear, intuitive meanings instead of ambiguous ones. For example, get_user_information is more straightforward than userInfo.

Observe proper casing style

Adhere to the following casing rules for tool function names and app triggers:

  • Tool function casing. Use snake_case or camelCase for tool function names defined in your MCP schema (for example, get_user_information or publishDraft).
  • App trigger handle casing. App triggers and mentions (for example, @MyTwoWordApp) are case-insensitive. The platform normalizes user inputs across lowercase (@mytwowordapp), PascalCase (@MyTwoWordApp), and camelCase (@myTwoWordApp).
  • Display name formatting. Register your official app title using PascalCase or standard spaces for clear visual display in UI elements.
  • Typo handling. While casing variations are handled automatically, character typos (for example, @mytwowrodapp) rely on fallback intent recognition rather than direct string matching. Developers should encourage accurate handle spelling in documentation.

Use explicit verb-noun naming

Use specific verb_noun naming, and avoid ambiguity—for example, publish_draft is better than handle_content.

Tool granularity

Observe the following guidelines with respect to tool granularity.

Treat tools as atomic endpoints

Treat tools as atomic endpoints without inside tool conditions or multiple functionalities with respective required parameters. Granular tools allow the model to chain steps logically, reducing hallucinations.

Use flatter schemas

If you need deeply nested objects or complex oneOf / anyOf schema logic to validate inputs, your tool is likely too broad.

Observe pre-aggregation and payload guardrails

Although pre-aggregating data isn't strictly enforced by protocol for small, scoped payloads, backend pre-aggregation is strongly required in practice when working with large record sets, longitudinal history, and multi-source datasets. The Connected Apps Platform enforces guardrails around context window limits, execution latency, and model reasoning steps. To adhere to these guardrails, follow these guidelines:

  • Filter and summarize on backend. Always filter, pre-aggregate, or summarize large data streams on your MCP server before returning responses to applicable Google products.
  • Avoid raw dumps. Never dump raw database rows or complete unindexed logs into the tool return payload.

Omit internal and meta tools

Expose only model-facing capabilities in your server's tools/list response:

  • Filter helper tools: Omit any internal, administrative, or meta tools that are not intended to be called by the model (for example, server health checks, internal setup routines, or developer utility helpers).
  • Context window efficiency: Excluding unnecessary tools keeps your MCP schema concise, reduces context window token consumption, and prevents model tool-routing confusion. MCP servers exposing dozens of tools or deeply nested 'mega-tool' schemas will be flagged during review for fixes.

Tool granularity do's and don'ts

The following examples show do's and don'ts pertaining to tool naming and granularity usage.

Recommended Not recommended

Granular tools with clear names and parameters

The input schema for each tool is strict. If the LLM wants to create a user, it must provide an email. If it wants to delete, it must provide an ID. There is no guessing.

...
@mcp.tool()
def create_user(email: str, role: str = "viewer") -> str:
    """Register a new user. Returns the new User ID."""
    # Logic is purely creation
    return "user_123"

@mcp.tool()
def delete_user(user_id: str) -> str:
    """Permanently remove a user by their ID."""
    # Logic is purely deletion
    return f"User {user_id} deleted"

@mcp.tool()
def update_user_role(user_id: str, new_role: str) -> str:
    """Change an existing user's permission level."""
    return f"User {user_id} is now {new_role}"

    ...

Tool Name: manage_user

Description: "Use this tool to create, update, delete, or retrieve users."

...
@mcp.tool()
def manage_user(
    action: Literal["create", "update", "delete", "get"],
    user_id: Optional[str] = None,
    email: Optional[str] = None,
    role: Optional[str] = "viewer"
) -> str:
    """
    Use this to manage users.
    WARNING: logic is hidden inside Python, not visible in the schema.
    """
    if action == "create":
        if not email: return "Error: Email required for creation" # Runtime error, not schema error
        return f"Created user {email}"
    elif action == "delete":
        if not user_id: return "Error: User ID required"
        return f"Deleted {user_id}"
    
    return "Action completed"
    """
    ...

Declare tool annotations for safety

Configure protocol annotations inside your tool definitions to convey execution behavior and trigger built-in safety gates across applicable Google products:

  • Runtime confirmation gates: applicable Google products use annotations.destructiveHint: true and annotations.openWorldHint: true as key flags to trigger runtime user confirmation prompts. Declare destructiveHint: true on state-changing, write, or destructive actions, and set openWorldHint: true for tools that interact with open-world or external environments so the surface natively prompts the user for confirmation prior to execution.
  • Read-only query hint: set readOnlyHint: true on query tools that only fetch data without modifying backend state.
  • Explicit annotation defaults: according to the Model Context Protocol specification, omitting readOnlyHint causes clients to assume readOnlyHint: false and destructiveHint: true by default for safety. Explicitly declaring annotations on all tools ensures predictable model routing and proper execution safety.

Tool descriptions

The description is the only context the model has to decide if your tool can solve the current problem. Observe the following guidelines for writing descriptions:

Write instructions, not comments

Write tool descriptions as instructions—and not as code comments, which are often passive. LLMs respond well to directives.

Explain the "how"

A good description bridges the gap between "I have a tool" and "I know how to wire this tool into a conversation."

Explain "the why"

Distinguish this tool from similar ones—for example, "Use this for internal users, use customer_update for external".

Tell the model where to find the arguments—for example, "from the order_search tool".

Specify the format

Specify units (meters versus feet) and formats (ISO dates, semantic versions).

Avoid model discretion

Don't instruct the model to use its "best judgment" or suggest optional, unguided actions.

Tool descriptions examples

The following examples show do's and don'ts pertaining to writing tool descriptions.

Recommended Not recommended

Source of truth

"Updates a user's email. Must strictly use the UUID 'id' returned from the search_users tool. Don't infer or guess user IDs."

(It explicitly links the output of one tool to the input of this one.)

"Updates the user's email address."

(The model might guess an ID like 12345.)

Format and constraint

"Schedule a meeting. 'date' must be ISO 8601 format (YYYY-MM-DD). If the user says 'tomorrow' or another relative reference, calculate the date based on the current context."

"Schedule a meeting for the given date."

(looks correct to a human (for example, "tomorrow") but fails schema validation (needs "2025-10-12"))

Output expectations

"Retrieves server logs. Returns the last 50 lines of raw text. For analysis, request smaller time windows to avoid token limits."

"Get server logs."

(The model may call a tool expecting a summary, gets a raw 5MB JSON dump, and may result in a bad user experience.)

@mcp.tool()
def update_campaign_status(campaign_id: str, status: str) -> str:
    """
    Promotes or pauses an advertising campaign.
    
    CRITICAL USAGE RULES:
    1. 'campaign_id': Must be the 32-character UUID retrieved from 'list_campaigns'.
    2. 'status': Must be exactly one of: ['active', 'paused', 'archived'].
    
    Returns: A confirmation string with the new status and timestamp.
    """
    # Logic...

    ...

This description acts as a mini-manual. It handles the Source (where to get the ID), the Constraints (valid enums), and the Action.

@mcp.tool()
def update_campaign_status(id: str, status: str) -> str:
    """
    Updates the campaign status in the database.
    """
    # Logic...

This relies on the variable names to do all the work. It leaves the model guessing about the valid values for "status"

Tool parameters

In addition to the tool itself, describe the parameters to make them unambiguous.

Explicit types

Use strong, specific types (string, integer, boolean, array).

Use enums for fixed value sets

If a parameter only accepts a fixed set of values (for example, daylight, cool, warm), list them in an enum array. This dramatically improves accuracy.

Clearly state required parameters

Clearly define which parameters are mandatory for the function to operate.

Apply semantic schema matching

When integrating your app, a semantic schema matching from your tools parameters with the vertical you're developing for could be required. You will receive the expected vertical schema (a JSON schema) during the onboarding process.

Tool parameters examples

The following examples show do's and don'ts pertaining to handling tool parameters.

Recommended Not recommended

Argument formats and units

radius: "The radius in meters. Must be a float > 0."

(prevents unit hallucination)

radius: "The radius of the circle."

ID formats and context

user_id: "The database UUID (for example, 550e8400-e29b...). Don't use email addresses here."

Note: As a best practice, all numerical ID fields should be treated as String values. In particular, if your ID could be 10+ digits, you run the risk of it being truncated by internal systems (forced into a 32 bit container) and this version being used by the model in future tool calls.

(prevents type errors)

user_id: "The ID of the user."

Return value expectations

"Updates the record and returns the updated JSON object including the new timestamp."

(Tells the model it doesn't need to call a "get" tool immediately after the "update" to verify the change.)

"Updates the record."


@mcp.tool()
def search_logs(query: str, limit: int = 10) -> str:
    """
    Searches server error logs using Lucene syntax (e.g. 'level:ERROR AND service:auth').
    
    Args:
        query: A Lucene-formatted search string.
        limit: Max results to return. capped at 50.
    
    Returns:
        A formatted string of log entries with timestamps.
    """
    ...
  
@mcp.tool()
def search_logs(query: str, limit: int = 10) -> str:
    """Searches the logs."""
    ...

The LLM doesn't know what syntax "query" supports (SQL? Regex? Plain text?) It doesn't know what the max "limit" is.

Responses and error handling

In accordance with the core principle of The response is the next prompt, the tool reports the outcome of its underlying execution by returning a data payload, which is then pasted directly into the LLM's context window. The success or failure of the outcome determines what happens next:

  • If it's successful: the model needs the return value to know what happened to decide its next step.
  • If it fails: the model needs to know why it failed and how to fix it. A crash (500 error) stops the conversation; a descriptive error message allows the model to self-correct.

Whether your tool's underlying logic succeeds or fails, you must format your return value payload so the model can easily parse it. Follow these best practices when packaging your response payload.

Use JSON

Format structured data in TextContent (Temporary constraint). Because structuredContent is not yet supported in the Connected Apps Platform, manually serialize your data. Even if returning a straightforward list, json.dumps() it.

Include URLs in response

Returned URLs should be relatively short and without many parameters. It is recommended to use URL shortening services to save on token counts and reduce risk of hallucinations.

Self-descriptive anti-hallucination payloads

Always pair raw codes or internal IDs with human-readable display names, explicit measurement units, and reference ranges. Never return compressed dictionary keys or unmapped numeric codes that force the model to infer context.

Implement robust error handling

Return clear, meaningful error messages so the model can understand what went wrong and inform the user. See the MCP error handling.

Use try/except

Wrap tool logic in try/except blocks and return stringified errors.

Provide actionable errors

Suggest the next step in the error message (for example, "Try using tool X first").

Confirm actions

Don't return empty strings for side-effect tools (create/update/delete).

Responses and error handling examples

The following examples show do's and don'ts pertaining to responses and error handling.

Recommended Not recommended

Explicit serialization


...
import json

@mcp.tool()
def query_metrics(metric_id: str) -> str:
    result = {"id": metric_id, "value": 42, "unit": "ms"}
    # GOOD: Returns '{"id": "cpu", "value": 42}' -> Valid JSON
    return json.dumps(result, indent=2)

...
@mcp.tool()
def query_metrics(metric_id: str) -> str:
    result = {"id": metric_id, "value": 42, "unit": "ms"}
    # BAD: Returns "{'id': 'cpu', 'value': 42}" -> Invalid JSON
    return str(result)

Catch and inform


...
@mcp.tool()
def get_user_details(user_id: str) -> str:
    try:
        user = db.get_user(user_id)
        if not user:
             # GOOD: Informative error returned as content
            return f"Error: User with ID '{user_id}' not found. Please verify the ID using 'search_users'."
        return json.dumps(user)
    except Exception as e:
        return f"Error: Database connection failed ({str(e)}). Please try again later."

(The tool "succeeds" technically (returns a string), but the string contains the error details. The model reads this, realizes "Oh, that ID doesn't exist," and can ask the user for the correct ID.)


...
@mcp.tool()
def get_user_details(user_id: str) -> str:
    # BAD: Unhandled exception if ID not found
    user = db.get_user(user_id)
    return json.dumps(user)

(If get_user fails, the server throws a 500. The LLM receives a generic execution error and doesn't know why (Was the ID wrong? Database down? Permission denied?). It will likely retry the exact same bad input.)

Empty Responses

...
@mcp.tool()
def post_story(story_content: str) -> str:
    service_name = "story_service"
    api_client.post("/stories", json={"content": story_content})
    # GOOD: Explicit confirmation of state change.
    return f"Service '{service_name}' successfully posted content. Status is now 'posted'."

...
@mcp.tool()
def post_story(storyContent: str):
    subprocess.post(["postsrv", "story", storyContent])
    # BAD: Returns null/empty string. Model is confused.

LLMs often get confused by empty responses. They might hallucinate "The tool failed" or just repeat the call.

Self-descriptive anti-hallucination payload

Always pair raw codes or internal IDs with human-readable display names, explicit measurement units, and normal reference ranges.

{"lab_code": "2345-7", "display_name": "Glucose [Mass/volume] in Blood", "value": 105, "unit": "mg/dL", "reference_range": "70-99 mg/dL"}

(This tells the model exactly what the value means without forcing it to infer units or reference thresholds.)

Raw IDs, custom compression, or unmapped codes

Returning raw internal IDs, custom dictionary abbreviations, or numeric codes without labels.

{"code": "2345-7", "v": 105}

(Forces the model to guess what 105 refers to, leading to hallucinated units, missing context, or inaccurate explanations.)

MCP error handling

It is crucial to handle errors gracefully.

  • For recoverable errors (such as an invalid user ID or missing optional parameter), return a normal text string containing descriptive error details in the tool response. This allows the model to inspect the feedback, understand what went wrong, and self-correct or prompt the user for clarification.
  • For terminal errors (such as rate limiting, authentication or permission failures, or backend server outages), raise an McpError with the appropriate error code so the platform can handle the failure systematically.

Example error response body (JSON)

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32302,
    "message": "User permission denied for the requested operation.",
    "data": null
  },
  "id": 1
}

Message contains a JSON dump of the MCP's ErrorData field. By returning the correct code and the model-visible message, the Connected Apps Platform can read the error message and relay a helpful, natural language explanation back to the user. For example, "I'm sorry, I couldn't find the venue with that ID. Could you check the name?"

MCP allows clients to connect to MCP servers using the JSON-RPC 2.0 spec. JSON-RPC has certain predefined errors sent for commonly occurring errors such as invalid request, parsing errors, etc. This guide covers how MCP servers can send back custom errors.

Status
Code
Message Usage Need to
return
explicitly?
User/Customer Errors
-32300 USER_RATE_LIMITED User has reached their maximum usage limit. YES
-32301 MISSING_REQUIRED_INFORMATION Input request does not have all the information needed YES
-32302 USER_PERMISSION_DENIED User denied permission to use the tool. For example, user require having an enterprise account, age restriction, locale, etc or not having an enterprise subscription of the tool. YES
Application Errors/Business Logic Errors
-32400 RUNTIME_ERROR Server code execution raised an error YES
-32401 NULL_POINTER_EXCEPTION Attempt to run a null pointer/object YES
-32402 OUT_OF_MEMORY Insufficient memory in the server code to allocate to an object YES
Infrastructure Errors
-32500 DEADLINE_EXCEEDED Request is taking more than the allowed time. Can happen due to many reasons for e.g. overloaded server, network issues, etc. YES
-32501 RESOURCE_EXHAUSTED Server ran out of memory, disk space, threads, CPU time, etc. YES
-32502 SERVICE_UNAVAILABLE Dependent service (for e.g. another microservice, database, cache, etc.) is unavailable YES
JSON-RPC Errors (these errors are part of MCP transport)
-32700 PARSE_ERROR Server couldn't parse the request NO
-32600 INVALID_REQUEST Malformed client request NO
-32601 METHOD_NOT_FOUND Invalid MCP tool name NO
-32602 INVALID_PARAMS Invalid request params, includes missing params or mismatched parameter types NO
-32603 INTERNAL_ERROR Default response NO

Following is an example of how you can raise an MCP error in your server using the official Python SDK (mcp). Raise an McpError containing an ErrorData payload, which the SDK automatically serializes into the required JSON-RPC error format with the error status populated.

from mcp import types
from mcp.shared.exceptions import McpError

@mcp.tool()
def foo_tool() -> list[types.TextContent]:
  """Always returns an error."""
  raise McpError(
    types.ErrorData(
        code=-32302, # USER_PERMISSION_DENIED
        message="Message to show to the model."
    ),
  )

Tool Context

Applicable Google products support a Tool Context API for passing signals to MCP servers. This API is used in valid use cases requiring sensitive information—for example, metadata that is not a part of the core tool definition, as in the case of a locale for tools that need localization. The Tool Context API is only exposed through the Connected Console.

When you register a signal like current_user_location_fine with your app in the Connected Console, applicable Google products will augment MCP tools/call requests with an additional _meta entry providing the requested data. The set of supported signals supported is:

  • current_user_location_fine (Gemini only, for permitted verticals)
  • user_locale
  • current_user_location_coarse
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "find-restaurant",
    "arguments": {"query": "restaurants near me"},
    "_meta": {
      "current_user_location_fine": {
        "latitude_degrees": 37.7749,
        "longitude_degrees": -122.4194,
        "accuracy_meters": 112.4221
      },
      "user_locale": {
        "language_code": "en",
        "region_code": "US"
      }
    }
  }
}

To unpack this data:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("remote_mcp")

@mcp.tool()
async def find_restaurants(query: str, ctx: Context) -> str:
   # Access context directly from _meta
   request_context = ctx.request_context
   meta_dict = ctx.request_context.meta.model_dump() if ctx.request_context.meta else {}

   location_context = meta_dict.get("current_user_location_fine", {})
   lat = location_context.get("latitude_degrees")
   lng = location_context.get("longitude_degrees")
   if lat and lng:
       return f"Finding '{query}' near ({lat}, {lng})"
   return f"Finding '{query}' (no location provided)"

Rendering of UX

MCP Apps

Applicable Google products support MCP Apps integrations for selected user journeys to allow building custom user interfaces. Follow the specification for general integration instructions.

MCP Apps integration on applicable Google products comes with the following caveats.

Resource ingestion at registration time

Applicable Google products fetch UI resources during tool ingestion time, similarly to tool list and tool descriptions. This means any resource updates—for example, changes in JavaScript—will not be picked up automatically and require tool re-ingestion.

A single UI resource must not exceed 15 MB in size, and cumulative UI resources across your server must not exceed 100 MB.

UI resources must use one of the supported MIME types: text/html, text/javascript, text/css, or text/html;profile=mcp-app. Non-standard or platform-specific MIME types will fail validation when pushing to preview.

CSP Guidelines

Applicable Google products follow the official MCP Apps specification, which includes CSP [1], [2], and will block any requests that violate CSP directives.

Applicable Google products additionally requires adding blob: and data: to CSP, if used. These will not be added by default. It's the developers choice to add them (or not) to CSP.

Script-src is an exception

The only directives we allow are: unsafe-inline (added by default), wasm-unsafe-eval, and data: (if included in resource_domains). If iframe is loading external scripts it can be solved by either bundling JS into HTML or using subresource Integrity (SRI) Hash.

  • Option 1: Inline / Bundle JS into HTML (Simplest)

    • Bundle JS Code:

      • Inline the contents of component-init.js directly inside <script> tags in the HTML payload returned for the UI resource:

        <script>
        // Paste component-init.js content directly here
        </script>
        
    • Remove Remote <script src> Tags

      • Delete <script src="https://api.mycompany.com/..."> references from the HTML document.
    • Re-ingest / Update Tool

  • Option 2: Use Subresource Integrity (SRI) Hash:

    Use this if the script must remain hosted externally at https://api.company.com/....

    • Calculate SHA-256 Hash of the Script
      • Generate the base64 SHA-256 hash of component-init.js
    • Add Hash to resourceDomains (must include the sha256- prefix to match the integrity attribute)
    • Update HTML <script> Tag
      • Add integrity and crossorigin attributes to the script tag in the HTML widget
    • Verify CORS Header on CDN/Server
      • Ensure api.company.com serves component-init.js with Access-Control-Allow-Origin: * (or allows scf.usercontent.goog) so the browser can read the response body for SRI verification.
    • Re-ingest / Update Tool

Security guidelines for MCP tool description

Because the description is the only context the model has to understand the tool's capabilities, it must be written accurately and securely to prevent unintended behavior. Adhere to the following guidelines when drafting your tool descriptions.

  • Describe functionality, but don't dictate behavior. The description must accurately describe what the tool does, not command the model to perform subsequent actions.
  • Define the scope and requirements. Clearly state the tool's primary goal, the specific circumstances (When) for its use, and all required Inputs (parameters/arguments).
  • Ensure truthfulness. The description must be a truthful representation of the tool's underlying code. Don't claim a tool is read-only if it performs a write or delete operation.

Security risks and how to avoid them

Risk area Recommended Avoid this practice at all costs

Prompt Injection

Use objective, declarative language to clearly state the tool's purpose and functionality.

Use imperative commands to override model behavior or alter system flows (for example, "you MUST say...", "After running, you MUST..."). Imperative terms like MUST or CRITICAL are acceptable only when defining structured parameter constraints or usage rules.

Use standard, plain formatting for the tool description text.

Use attention-grabbing formatting —for example, excessive capitalization and tags such as VERY IMPORTANT: or **CRITICAL**.

Focus solely on the tool's function and expected behavior.

Include role-playing instructions or commands to ignore previous instructions.

Tool shadowing & hijacking

Ensure the description is specific and limited to the tool's actual, related capabilities.

Make the tool description overly broad, vague, or claim a single tool performs many unrelated functions.

Choose a unique, descriptive, and sensible tool name.

Use a tool name that is a minor variation of a trusted tool, or a generic name (for example, do_everything_tool, assistant_tool).

Don't mention other apps' or external platform tools by name in your description.

Explicitly reference other tools or create new rules for when or how other tools should be used.

Allow the model's planner to select the most appropriate tool based on user context.

Include anti-competitive language or any kind of SEO prompt that instructs the model to prefer your tool over similar ones.

Trigger manipulation

The description doesn't reference or react to general conversation history phrases.

Create conditional logic that triggers a harmful action based on common, innocuous user phrases (for example, "thank you," "to summarize").

Abuse of parameters

Only define parameters necessary for the tool's function and use names specific to the input data (for example, file_id, search_term).

Define function parameters with names that mirror the Agent's internal state (for example, system_prompt, conversation_history, full_context).

Data exfiltration

Privacy by design - Opaque identifiers and host-managed authentication.

Use non-sensitive IDs, not raw PII. All data handling must be within authorized, secure boundaries and not referenced in the public description as being sent externally. Tool execution will inherit permissions silently through OAUth handshake.

Any sensitive information should be sanitized before sending it to applicable Google products.

Request PII, PHI or any other personal and/or sensitive information from the user, personal or business data or entity in any capacity that violates terms of service for applicable Google products.

Mention or require the sending of any user data (query, location, context) to unauthorized third-party URLs, logging services, or external systems.

Functional & naming Best Practices

Beyond securing your tool's description, you must ensure the model can reliably select and execute it without confusion. Adhere to the following functional and naming best practices:

  • Parameter accuracy. Ensure all parameter names and types in the description are an exact match for the tool's actual implementation.
  • Sensible naming. The tool's name should be a strong, clear hint about its function. Avoid overly generic names like "execute." The name must also be consistent with the broader tool ecosystem.

Appendix: Infrastructure and network allowlisting

If your hosting infrastructure, API gateway, or firewall restricts incoming connections by source IP address, allowlist the following Google outbound CIDR blocks:

  • 108.177.64.0/20
  • 142.250.88.0/22
  • 172.217.80.0/20
  • 192.178.20.0/22
  • 192.179.0.0/20
  • 74.125.232.0/24
  • 136.121.40.0/21

Ensure your environment permits traffic from these IP ranges on HTTPS port 443 so applicable Google products can reach your remote MCP server for MCP initialization, tool discovery, and request execution.