Start the stack, establish an MCP session against drive-mcp-iag, and execute the full-text search - by script, by hand, and by prompt.
Chapter 7: Run a Google Drive search through the gateway
1. Configure the usecase env file
The demo keeps one complete env file per usecase (.env.canbank, gitignored) and .env is a symlink to the active one - so a switch always swaps the platform bindings, the domain vocabulary, and the agent skills together. First run only: bootstrap .env.canbank and fill in the base values per the repo README - the IndyKite base URL, the get-agent-workflows query ID, the App Agent credentials token, the IdP client secrets, USECASE=canbank, and an LLM key for the agents - then add the Drive entries from chapter 6 (including COMPOSE_PROFILES=drive). Take the project-specific values from the instant-stack provisioning output (chapter 5), which records every created ID.
cd a2a/iag-mcp-demo
cp .example.env .env.canbank
# edit .env.canbank: base demo values per the repo README + the chapter 6 drive entries
./switch-usecase.sh canbank # links .env -> .env.canbank (and recreates changed containers)
USECASE=canbank selects the usecase bundle usecases/canbank/: its usecase_name.env feeds every agent the CanBank vocabulary (org name, knowledge-query names such as get-stock-quote and get-hq-weather, the weather HQ keywords), and its skills/<agent> folders are mounted read-only into the orchestrator, retriever, and analyst - including the dataset-specific canbank-authz skill with the exact KBAC vocabulary.
Two optional .env entries tune the agents. MCP_SESSION_TTL (default 300) is how long, in seconds, an agent reuses each user's MCP session against the gateway before rebuilding it - keep it below the access-token lifetime, or set 0 for a fresh session per request. LIB_LOG_LEVEL (default INFO) sets the log level of the agents' third-party libraries independently of LOG_LEVEL - keep it at INFO for readable agent logs, or set DEBUG to see the SDK events as one-line breadcrumbs.
2. Build and start the stack
make # builds the chatbot and agent images
make new-drive-mcp # builds the drive-mcp image (chapter 4)
docker compose up -d # COMPOSE_PROFILES=drive in .env.canbank pulls in the two drive services
docker compose ps
With the drive profile enabled expect 12 containers - the 10 base services plus the drive-mcp / drive-mcp-iag pair; fewer means the profile didn't activate. (The demo's other optional profiles, crm and erp, add further containers - chapter 8's "Beyond Drive" section covers what they do.) Confirm the drive pair and prove the gateway is enforcing:
docker compose logs drive-mcp | tail # the wrapped server is listening on :8000, endpoint /mcp
docker compose logs drive-mcp-iag | tail # the gateway is listening on :8887
curl -s -X POST http://localhost:8887/mcp -H "Content-Type: application/json" -d '{}'
# {"message":"Missing bearer token"} <- alive and enforcing (chapter 2)
3. Get an authorized user token
Open http://localhost:3000 (use localhost, not 127.0.0.1) in a fresh incognito window and log in as millicent - the one user granted CAN_TRIGGER on wf-drive in chapter 5. She must exist as a login user in your IdP; if she does not, either create her there or grant your own login user the CAN_TRIGGER edges instead. The demo's test scripts extract the logged-in user's access token from the chatbot session automatically; for the manual calls below, export it as TOKEN. Tokens are short-lived, so run the calls soon after logging in.
4. The script version
./test-drive.sh # initialize → tools/list → resources/list → search, as millicent
DRIVE_QUERY=budget ./test-drive.sh # different search term
./test-drive.sh <user> # as a different logged-in user
5. The manual version
An MCP session against the gateway, step by step. All requests go to the gateway (:8887/mcp), never to the Drive server directly:
DRIVE="http://localhost:8887/mcp"
H=(-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream")
# 1. initialize - capture the MCP session id from the response headers
SID=$(curl -s -D - -o /dev/null "${H[@]}" -X POST $DRIVE \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {
"name": "demo",
"version": "1.0"
}
}
}' \
| grep -i mcp-session-id | tr -d '\r' | awk '{print $2}')
echo "session: $SID" # a UUID; empty means check the token or the gateway logs
# every request after initialize must name the negotiated revision
S=(-H "Mcp-Session-Id: $SID" -H "Mcp-Protocol-Version: 2025-11-25")
# 2. initialized notification (expect HTTP 202)
curl -s -o /dev/null -w "initialized -> HTTP %{http_code}\n" "${H[@]}" "${S[@]}" -X POST $DRIVE \
-d '{
"jsonrpc": "2.0",
"method": "notifications/initialized",
"params": {}
}'
# 3. list the Drive server's tools (expect: search)
curl -s "${H[@]}" "${S[@]}" -X POST $DRIVE \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'
# 4. the search - full-text over the authorized account's Drive
curl -s -m 45 "${H[@]}" "${S[@]}" -X POST $DRIVE \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"query": "canbank"
}
}
}'
The gateway forwards only MCP protocol revisions 2025-06-18, 2025-11-25, and 2026-07-28. The Drive reference server does not speak the stateless 2026-07-28 revision (it rejects server/discover), so this walkthrough uses 2025-11-25, the newest revision an initialize handshake can agree on; an initialize naming an older revision is forwarded with 2025-11-25 instead. Every request after the handshake must carry Mcp-Protocol-Version, or the gateway refuses it with 400 and the JSON-RPC error Header mismatch: Mcp-Protocol-Version header is missing. That is why the S array above carries the header on every call.
Expected result of the search - lines of <fileId> <name> (<mimeType>); your files will differ, and matches are files whose content mentions the query; for example:
Found 3 files:
1AbC...xYz Financial report (application/vnd.google-apps.document)
1DeF...uVw Retail campaign (application/vnd.google-apps.document)
1GhI...rSt video1.mp4 (video/mp4)
Cross-check: open drive.google.com as the authorized Google account and type the same query into the search bar - same files. Between steps, note what just happened: the gateway introspected millicent's token, confirmed CAN_TRIGGER wf-drive against the graph, audited the decision, swapped in its delegation token, and streamed the Drive server's response back - while the Mcp-Session-Id minted downstream flowed through untouched.
6. The prompt versions (optional)
The same authorization guards the longer chains modeled in chapter 5:
- Via the analyst (
wf-drive-analyst): with ANALYST_MCP_SERVER_URLS set (chapter 6), send the analyst gateway (:8885) an A2A message/send with a prompt like "Search Google Drive for canbank" - the analyst then calls Drive as one of its MCP backends. Cross-backend prompts work too: "Search Google Drive for canbank, then check which internal policy documents mention the same topics." (The repo's ./demo-analyst-drive.sh is a related smoke test: it sends the analyst a hello message and then runs the direct Drive search from step 5.)
- Via the chatbot console (
wf-drive-console): as millicent, type "Search Google Drive for canbank and list the matching files" at http://localhost:3000. Mentioning "Google Drive" (or "Drive") in the prompt is what routes it to the orchestrator's query_drive tool instead of the retriever - a prompt like "search my files for canbank" silently goes to the retriever. Every hop - orchestrator, analyst, Drive - passes its own gateway and leaves its own audit record.
Prompt tips: Drive search is full-text, so to target a specific file prefer listing then reading ("Read the file 'X' from Google Drive and summarize it"), and point read prompts at Google-native documents or PDFs - the only formats that come back as readable text (chapter 4). For workflow prompts, name the agent ("Use the retriever to …", "Ask the retriever for …") so the orchestrator actually delegates instead of answering itself. Stock-quote prompts fetch live market data; an occasional 429 Too Many Requests is upstream rate-limiting, not an authorization failure - retry after a few minutes.
7. Prompts by persona
The graph gives every demo user different access, so the same prompt can succeed for one login and be denied - or return nothing - for another. That asymmetry is the demo. Switch personas with a fresh incognito window per user. The repo's scripted tour of these prompts lives in usecases/canbank/DEMO_SCRIPT.md. Chatbot console prompts:
millicent - support + trading depts; wf1, wf3, all wf-drive* |
Everything leslie can, plus the star turn: "Search Google Drive for canbank and list the matching files.", "List the files in my Google Drive.", "Read the file 'X' from Google Drive and summarize it." Trading membership adds stock and AuthZEN prompts: "What is the price of META?", "Am I allowed to retrieve a stock quote? Check with authzen." (decision true). |
Asking authzen about a different user (e.g. subject roy) returns false by design - see the note below the table. |
leslie - support dept → wf1 |
"Use the retriever to find internal policy documents about refunds.", "Ask the retriever which past decisions incorporated the refund_policy document.", "What's the weather in London?" |
Drive and analyst calls → 403; stock prompts return nothing, and "Am I allowed to retrieve a stock quote?" answers false - support holds no CAN_RETRIEVE edge. |
roy - trading dept → wf1 |
"Use the retriever to get the NVDA stock price.", "Ask the retriever how many shares of NVDA the customer rebecca can purchase.", "Am I allowed to retrieve a stock quote? Check with authzen." (decision true). |
Drive and analyst calls → 403 - which makes roy the demo's grant-flow persona: chapter 8's deny → remediate → allow beat grants him Drive access with one click and revokes it again. |
rebecca - customer, direct wf1 grant |
"Who am I?", "Show my profile", prompts about her own accounts and documents. |
Department-scoped queries (internal docs, stock) return nothing - she has no department; Drive → 403. |
carol - wf1 only |
Normal wf1 prompts (as leslie). |
The designated deny persona: millicent's exact analyst and Drive requests with carol's token → 403. The "same request, different user, different outcome" money shot. |
jane - wf2 only |
Weather prompts: "What's the weather in London?", "What's the weather at CanBank HQ?" The HQ variant goes through the get-hq-weather knowledge query and its weather resolvers only when CIQ_QUERY_HQ_WEATHER and that setup exist; otherwise weather prompts fall back to direct Open-Meteo. |
All wf1 flows → 403 - she cannot trigger the orchestrator workflow. |
joe - plain wf1 |
Baseline wf1 prompts; handy as the default subject for a direct AuthZEN CAN_TRIGGER evaluation. |
Analyst and Drive → 403. |
Note the three kinds of "no" in the table: a 403 is the gateway denying the workflow; an empty result is data-level scoping inside an authorized workflow (the retriever's queries respect department reach); an AuthZEN decision of false is a policy answer delivered inside an authorized workflow - leslie asking about the stock quote gets a clean false, not an error. One more false is by design rather than policy: evaluations through the chatbot carry the logged-in user's token, and the platform binds them to that token's subject - asking about a different user always returns false, regardless of that user's real permissions. Evaluate other subjects from a service context instead (the repo's Bruno authzen folder, X-IK-ClientKey app token).
When writing your own AuthZEN prompts or agents, use the policy vocabulary exactly: types, actions, and ids are case-sensitive graph terms (User, CAN_RETRIEVE, Quote, stock_quote). A guessed value such as user, view, or stock evaluates to a false indistinguishable from a real denial, and the MCP server's list_resources returns only knowledge queries, not policy vocabulary - so the demo agents carry the exact triples as agent skills; give your own agents the vocabulary the same explicit way.
Which personas can actually log in depends on your IdP - the graph side comes from the chapter 5 ingest; if a persona cannot log in, create them in the IdP or grant your login user the equivalent edges.
What comes next
A successful search is only half the story. Chapter 8 proves the denials and reads the audit trail.