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 theinitializehandshake 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 standardHTTP POST. - SSE streaming within streamable HTTP. Legacy multi-endpoint SSE
transport is unsupported, but Server-Sent Events such as
text/event-streamare supported within Streamable HTTP. Outgoing requests from applicable Google products includeAccept: application/json, text/event-stream, meaning your server can either return a standardapplication/jsonpayload or stream incremental responses usingContent-Type: text/event-streamdirectly in theHTTP POSTresponse body. - GET stream & 405 Method Not Allowed. Clients may attempt an optional
GETstream on the endpoint for server-to-client notifications. If your server is stateless or request-response only, returningHTTP 405 Method Not Allowedis 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
|
This server has tools for GitHub. You should use the
|
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_caseorcamelCasefor tool function names defined in your MCP schema (for example,get_user_informationorpublishDraft). - 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. |
Tool Name: manage_user Description: "Use this tool to create, update, delete, or retrieve users." |
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: trueandannotations.openWorldHint: trueas key flags to trigger runtime user confirmation prompts. DeclaredestructiveHint: trueon state-changing, write, or destructive actions, and setopenWorldHint: truefor 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: trueon query tools that only fetch data without modifying backend state. - Explicit annotation defaults: according to the Model Context Protocol
specification, omitting
readOnlyHintcauses clients to assumereadOnlyHint: falseanddestructiveHint: trueby 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".
Provide the link
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 (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.) |
This description acts as a mini-manual. It handles the Source (where to get the ID), the Constraints (valid enums), and the Action. |
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." |
|
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 |
|
|
Catch and inform (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.) |
(If |
|
Empty Responses |
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. (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. (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
McpErrorwith 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_localecurrent_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.jsdirectly 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.
- Delete
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
- Generate the base64 SHA-256 hash of
- Add Hash to
resourceDomains(must include thesha256-prefix to match theintegrityattribute) - Update HTML
<script>Tag- Add
integrityandcrossoriginattributes to the script tag in the HTML widget
- Add
- Verify CORS Header on CDN/Server
- Ensure
api.company.comservescomponent-init.jswithAccess-Control-Allow-Origin: *(or allowsscf.usercontent.goog) so the browser can read the response body for SRI verification.
- Ensure
- Re-ingest / Update Tool
- Calculate SHA-256 Hash of the Script
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 |
|
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, |
|
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, |
Define function parameters with names that mirror the Agent's internal state (for example, |
Data exfiltration |
Privacy by design - Opaque identifiers and host-managed authentication. |
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. |
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/20142.250.88.0/22172.217.80.0/20192.178.20.0/22192.179.0.0/2074.125.232.0/24136.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.