Sanity Context

Connect Sanity Context with Google ADK

Connect Sanity Context to a Python or TypeScript agent built with Google's Agent Development Kit (ADK).

Google's Agent Development Kit (ADK) is a framework for building AI agents in Python and TypeScript. This guide shows you how to connect an ADK agent to Sanity Context. You'll build an agent that loads your schema or Knowledge Base outline into its instruction, connects to your Context MCP endpoint over Streamable HTTP, and answers questions about your content. The same code works whether the endpoint uses Knowledge Base sources, a dataset source, or both.

Prerequisites

To follow this guide, you need:

  • Context MCP endpoint URL: Shown on the MCP endpoint in the Context app in the Sanity Dashboard. To create an MCP endpoint, see Configure a Context MCP endpoint, or expand Create the endpoint with the Sanity CLI (optional) at the end of this section.
  • Organization API token: Create one in Manage under API > Tokens at the organization level. Under Organization permissions, select Context with the Viewer role. Project tokens don't work.
  • Gemini API key: ADK reads it from GOOGLE_API_KEY. Get one from Google AI Studio. To use Vertex AI instead, set GOOGLE_GENAI_USE_VERTEXAI=TRUE, GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION.
  • Node.js 22.18 or later for TypeScript, or Python 3.11 or later for Python. Node.js 22.18 and later run .ts files directly. On macOS with Python 3.14 or later, see the watchdog entry in Troubleshoot common errors before you install.
Create the endpoint with the Sanity CLI (optional)

You can create a Knowledge Base and an MCP endpoint from the Sanity CLI instead of the Dashboard. If you haven't signed in, run npx sanity@latest login. You still create the organization token in Manage. Replace ORGANIZATION_ID, PROJECT_ID, and KNOWLEDGE_BASE_ID with your own values:

mcp create prints the endpoint URL to use as SANITY_CONTEXT_MCP_URL. To serve a live dataset through a dataset source instead, replace --knowledge-base KNOWLEDGE_BASE_ID with -p PROJECT_ID -d production. See the Context CLI command reference for every flag.

Install dependencies

@modelcontextprotocol/sdk is an optional peer dependency of @google/adk, so install it yourself. @google/adk-devtools provides the adk command. Set the module type in package.json so Node.js loads agent.ts as an ES module:

If you type-check with tsc, set "skipLibCheck": true in tsconfig.json. ADK's own type files report errors, though the example code type-checks.

Set environment variables

Create a .env file. For TypeScript, put it next to agent.ts. For Python, put it inside the agent folder (sanity_agent/.env), where both adk run and load_dotenv() look. Replace each placeholder with your own value:

Create the agent

Create the agent in agent.ts (TypeScript) or sanity_agent/agent.py (Python). The file exports the agent as rootAgent (TypeScript) or root_agent (Python) so the adk command can load it. The agent works in three stages:

  • Fetch initial context from the /initial-context HTTP endpoint and add it to the instruction. It's the schema overview for a dataset source, the Knowledge Base outline for Knowledge Base sources, or both if the endpoint has both, plus the endpoint's Instructions. The agent writes accurate queries from the first turn and saves a tool call per conversation. In Python, a 401 or 403 stops the script here, because ADK would otherwise run the agent without the toolset that failed to load. For details, see Inline initial context into your system prompt.
  • Connect to the MCP endpoint with MCPToolset (TypeScript) or McpToolset (Python) over Streamable HTTP, authenticated with your organization token. The tool filter hides initial_context only when its content is already in the instruction. If the fetch failed, the agent keeps the tool and calls it instead. In Python, timeout=30 raises the 5-second default for connecting.
  • Run the agent with a function instruction. ADK replaces {name} placeholders in string instructions with session state, and GROQ projections in the initial context, such as {title}, would raise an error. In TypeScript, model errors such as an invalid API key arrive as events, so the loop checks event.errorMessage.

Run the agent

Pass a question as the first argument, or leave it out to ask "What content do we have?" To chat in the terminal instead, use adk run.

The script prints the agent's answer and exits. With the initial context in the instruction, a question like "What content do you have access to?" gets an answer without a tool call first. If the run fails, match the message against the list in Troubleshoot common errors.

Dataset and Knowledge Base sources

The same code works with an endpoint that serves Knowledge Bases. What changes is what the endpoint returns:

Dataset sourceKnowledge Base source
Content sourceA datasetOne or more Knowledge Bases
Initial contextSchema overviewOutline of each Knowledge Base, with its ID
Tools after filteringschema_explorer, groq_query, array_field_readerknowledge_base_search, knowledge_base_read

An endpoint can have one dataset source and several Knowledge Base sources. If it has both types, it serves the tools from both columns, and initial context contains both the schema overview and the Knowledge Base outline. See Context MCP tools and Context MCP source types.

Troubleshoot common errors

These are the most common errors and how to fix them:

  • Error: Set SANITY_CONTEXT_MCP_URL and SANITY_ORGANIZATION_TOKEN. (TypeScript) or KeyError: 'SANITY_CONTEXT_MCP_URL' (Python): The .env file is missing or wasn't loaded. In TypeScript, run with --env-file=.env. In Python, put it inside sanity_agent/.
  • [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///…/agent.ts is not specified and it doesn't parse as CommonJS. (TypeScript): The agent still runs, but package.json doesn't set the module type. Run npm pkg set type=module.
  • Failed to create MCP session: ... (HTTP 401) (TypeScript) or RuntimeError: Sanity Context rejected the token (401) (Python): The token is missing, invalid, or belongs to another organization. Check the organization ID in the URL too.
  • 403 from the MCP connection with JSON-RPC error -32007 on an endpoint with a dataset source, or -32006 on an endpoint with only Knowledge Base sources, or the same 403 from the /initial-context fetch with code contextGrantRequired or knowledgeBaseAccessDenied. The messages are "Serving a dataset requires an organization API token with Context access ('sanity.knowledge-base.read'). Create one under your organization's API settings in Manage." (dataset source) and "No access to knowledge base '<knowledge base ID>'. Requires one of: sanity.knowledge-base.read." (Knowledge Base sources only; <knowledge base ID> is a placeholder for the Knowledge Base's ID): On an endpoint with a dataset source, the token isn't an organization API token with Context Viewer permissions. On an endpoint with only Knowledge Base sources, the token has no read access to the Knowledge Base the message names. Project tokens are refused however broad their permissions. Create an organization token with the Context permission.
  • Error: API key must be provided via constructor or the GOOGLE_GENAI_API_KEY, GOOGLE_API_KEY or GEMINI_API_KEY environment variable. (TypeScript) or ValueError: No API key was provided. (Python): Add GOOGLE_API_KEY to the .env file.
  • Error: Context variable not found: `title`. (TypeScript) or KeyError: "Context variable not found: `title` in agent 'sanity_context_agent'." (Python): The instruction was changed to a plain string. Keep it a function.
  • Failed building wheel for watchdog: ADK pins watchdog 6, which has no prebuilt macOS wheel for Python 3.14 or later, so pip has to compile it. watchdog 7 installs without compiling and works with ADK. Install ADK with uv and override the pin: create overrides.txt containing watchdog>=7, then run uv pip install "google-adk[mcp]" --override overrides.txt.

Python may also log Failed to configure mTLS using AsyncAuthorizedSession. It's harmless; set GOOGLE_API_USE_CLIENT_CERTIFICATE=false to silence it. TypeScript ADK prints its own log lines to stdout alongside the answer, so filter them if you pipe the output.

Next steps

Was this page helpful?