<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tanaike</title>
    <description>The latest articles on DEV Community by Tanaike (@tanaike).</description>
    <link>https://dev.to/tanaike</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3701430%2F3d517296-9b96-418b-ad29-24e735edd1df.png</url>
      <title>DEV Community: Tanaike</title>
      <link>https://dev.to/tanaike</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tanaike"/>
    <language>en</language>
    <item>
      <title>Orchestrating Agents via ADK for TypeScript and Gemini CLI</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 21 Apr 2026 05:22:52 +0000</pubDate>
      <link>https://dev.to/gde/orchestrating-agents-via-adk-for-typescript-and-gemini-cli-jco</link>
      <guid>https://dev.to/gde/orchestrating-agents-via-adk-for-typescript-and-gemini-cli-jco</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbux4nzfwesadhy5g05d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbux4nzfwesadhy5g05d.jpg" alt="fig1a" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Explore how to build and orchestrate production-ready, type-safe AI agents using Google's TypeScript Agent Development Kit (ADK). This guide provides practical scaffolding patterns, multi-agent coordination strategies, and seamless integration techniques for deploying remote subagents within the Gemini CLI ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As the artificial intelligence landscape rapidly evolves, modern generative AI increasingly relies on autonomous agents equipped with sophisticated components, including system instructions, specialized skills, and Model Context Protocol (MCP) servers. To facilitate the development of such AI-driven applications, Google has released the Agent Development Kit (ADK) across multiple programming languages &lt;a href="https://adk.dev/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. Among these, the ADK for TypeScript &lt;a href="https://github.com/google/adk-js" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; offers distinct advantages for modern engineering paradigms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full-Stack Synergy:&lt;/strong&gt; Essential for modern web applications (e.g., Next.js or React), allowing developers to share types and logic between the frontend and backend to streamline the development lifecycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability &amp;amp; Reliability:&lt;/strong&gt; Crucial for large-scale enterprise systems, where static typing and robust refactoring tools ensure long-term maintainability and minimize runtime errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Concurrency:&lt;/strong&gt; Highly effective for agents that must efficiently handle numerous asynchronous events, streaming responses, and real-time user interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these architectural merits, a critical gap exists in ecosystem maturity. While Python remains the "Gold Standard" with first-class support in AI research, TypeScript is often viewed as an emerging platform. Although there is a rising demand for integrating AI agents into enterprise web applications, quantitative data indicates that TypeScript-specific ADK resources account for less than 5% of the total search volume compared to the Python-centric ecosystem. This overwhelming scarcity of documentation and community-driven knowledge presents a significant barrier to entry for full-stack developers.&lt;/p&gt;

&lt;p&gt;To fill this critical information void and catalyze the development of type-safe, production-ready AI agents, this article provides a comprehensive framework and practical scaffolding patterns using the TypeScript ADK. Furthermore, it introduces advanced methodologies for establishing a multi-agent architecture. Specifically, this article demonstrates how to seamlessly integrate these custom-built TypeScript agents as remote subagents within the Gemini CLI, leveraging agent-to-agent protocols to enable scalable, interconnected AI workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup and Prerequisites
&lt;/h2&gt;

&lt;p&gt;You can view the complete repository of sample scripts at&lt;a href="https://github.com/tanaikech/ts-multi-agent-scaffolding" rel="noopener noreferrer"&gt;https://github.com/tanaikech/ts-multi-agent-scaffolding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To follow along with this guide, ensure your environment meets the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js is installed and configured on your system.&lt;/li&gt;
&lt;li&gt;Gemini CLI is installed and accessible via your terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To retrieve and initialize the sample scripts, execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tanaikech/ts-multi-agent-scaffolding
&lt;span class="nb"&gt;cd &lt;/span&gt;ts-multi-agent-scaffolding
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building Specialized AI Agents
&lt;/h2&gt;

&lt;p&gt;The following samples demonstrate how to construct specialized, single-purpose agents using the TypeScript ADK. Each agent is designed to handle a distinct operational domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. General Logic Analyst (Sample 1)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Logical analysis, summarization, drafting, and evaluation.&lt;/p&gt;

&lt;p&gt;This foundational agent focuses on parsing text and validating logical structures without relying on external API tools. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample1/agent.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;general_logic_analyst&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Handles general reasoning, text summarization, and logical validation of information.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`You are a Senior Logic Analyst and General Assistant. 
Your role is to process general queries and synthesize information from various sources.

### Key Responsibilities:
1. **Summarization**: Condense long texts or technical logs into concise, actionable insights.
2. **Logical Validation**: Check if a given statement or piece of code follows logical consistency.
3. **Drafting**: Create professional emails, reports, or documentation based on raw data.
4. **Knowledge Retrieval**: Answer general knowledge questions using your internal training data.

### Constraints:
- Keep responses professional and structured.
- If you receive data from other agents (like weather or file logs), focus on explaining the *implications* of that data.
- Do not invent facts; if information is missing, clearly state so.

### Output Style:
Use bullet points for clarity and always provide a brief "Conclusion" or "Next Steps" section at the end of long responses.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the agent as a local web server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:8000&lt;/code&gt; in your browser to interact with the web interface. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38ogdh89hhrbit4i9d89.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38ogdh89hhrbit4i9d89.jpg" alt="fig2a" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. API Manager for Real-Time Data (Sample 2)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Real-time data retrieval via external APIs (Weather, Exchange Rates).&lt;/p&gt;

&lt;p&gt;This agent leverages custom tools built with Zod schema validation to dynamically fetch external data, demonstrating how AI can interact with the real world.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample2/agent.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * agent.ts
 * AI Agent definition with dynamic date context and Tool definitions.
 *
 * npx adk web
 */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FunctionTool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Dynamic Date Helper&lt;/span&gt;
&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ja-JP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Tools Definition&lt;/span&gt;
&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getExchangeRateTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FunctionTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_exchange_rate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use this to get current exchange rate between currencies.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Source currency (major currency).&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Destination currency (major currency).&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date of the currency in ISO format (YYYY-MM-DD) or 'latest'.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency_date&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`https://api.frankfurter.app/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?from=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;to=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;`The raw data from the API is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;`The currency rate at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" to "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Error retrieving exchange rate: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCurrentWeatherTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FunctionTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_current_weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use this to get the weather using the latitude and the longitude.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The latitude of the inputed location.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The longitude of the inputed location.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date for searching the weather. Format: 'yyyy-MM-dd HH:mm'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The timezone (e.g., 'Asia/Tokyo').&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timezone&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;codeMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clear sky&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fog and depositing rime fog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fog and depositing rime fog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Drizzle: Light and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Drizzle: Light and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Rain: Light and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Rain: Light and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow grains&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow showers slight and heavy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;86&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow showers slight and heavy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm: Slight or moderate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm with slight and heavy hail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm with slight and heavy hail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.open-meteo.com/v1/forecast?latitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;longitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;hourly=weather_code&amp;amp;timezone=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;widx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hourly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;widx&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;codeMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hourly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weather_code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;widx&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown condition.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No weather data found for the specified time.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Error retrieving weather: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;
&lt;span class="c1"&gt;// 3. Agent Definition&lt;/span&gt;
&lt;span class="c1"&gt;// ==========================================&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api_manager_agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An agent that manages currency and weather API tools.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`You are a professional API Manager.
Current date and time is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentDateTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Use this information to calculate relative dates.
1. Use 'get_exchange_rate' for currency queries.
2. Use 'get_current_weather' for weather queries. "date" is required to be "yyyy-MM-dd HH:00".
3. Provide precise, helpful responses based on tool outputs.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;getExchangeRateTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCurrentWeatherTool&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:8000&lt;/code&gt; to interact with this API-connected agent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya6jh3v7n80q42jm1bqm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya6jh3v7n80q42jm1bqm.jpg" alt="fig2b" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Local Filesystem Expert via MCP (Sample 3)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Local filesystem operations via MCP.&lt;/p&gt;

&lt;p&gt;By integrating the Model Context Protocol (MCP), this agent gains secure, managed access to the local filesystem. For this sample, target files should be placed in the &lt;code&gt;sample3/sample&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample3/agent.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MCPToolset&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;local_file_expert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a local file manager. Help users organize and understand their files. Here, you can access only a directory of 'sample' given by the MCP server.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;StdioConnectionParams&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;serverParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/server-filesystem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample3/sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:8000&lt;/code&gt; to instruct the agent to read and organize your local files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sps4hiup3z6oasna32o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sps4hiup3z6oasna32o.jpg" alt="fig2c" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Google Workspace Technical Guide via MCP (Sample 4)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Technical support for Google Workspace APIs and Apps Script (GAS).&lt;/p&gt;

&lt;p&gt;This agent utilizes a remote MCP server to stream up-to-date Workspace documentation, creating a highly specialized technical support assistant.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample4/agent.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MCPToolset&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workspace_doc_guide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a Google Workspace expert. Use the provided tools to answer questions about Apps Script and Workspace APIs.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MCPToolset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;StreamableHTTPConnectionParams&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://workspace-developer.goog/mcp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:8000&lt;/code&gt; to query the agent regarding complex Workspace developer documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiai3uo1io1o9w2zhoyvq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiai3uo1io1o9w2zhoyvq.jpg" alt="fig2d" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestrating Multi-Agent Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5. Multi-Agent Orchestrator (Sample 5)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Coordination of multiple agents using serial, parallel, and iterative execution strategies.&lt;/p&gt;

&lt;p&gt;Once individual subagents are established, they can be orchestrated by a primary agent. This orchestrator acts as a dynamic manager, delegating tasks to the specialized subagents (Agents 1 through 4) based on the user's prompt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample5/agent.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample1/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample2/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample3/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample4/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multi_agent_orchestrator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Advanced orchestrator capable of serial, parallel, and iterative task execution.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`You are a Senior Multi-Agent Orchestrator. Your role is to analyze user prompts and delegate tasks to the most suitable sub-agents.

### Available Sub-Agents &amp;amp; Expertise:
- "general_logic_analyst" (agent1): Logic validation, summarization, and final report drafting.
- "api_manager_agent" (agent2): Real-time currency exchange and weather data retrieval.
- "local_file_expert" (agent3): Local file system operations within the workspace.
- "workspace_doc_guide" (agent4): Google Workspace APIs and Apps Script documentation.

### Operational Protocols:
1. **Selection &amp;amp; Purpose**: Clearly identify which agent(s) you are using and why.
2. **Execution Strategy**:
   - **Serial**: When one agent's output is needed as input for another.
   - **Parallel**: When multiple independent data points are needed.
   - **Iterative**: When you need to re-run an agent or call a new one based on fresh findings.
3. **Reporting (Strict Requirement)**: You MUST start your response with an "Execution Log".

### Mandatory Output Format (in English):
---
## Execution Log
- **Agents Involved**: [List names of agents used]
- **Execution Strategy**: [Single / Serial / Parallel / Iterative]
- **Purpose &amp;amp; Logic**:[Briefly explain why these agents were chosen and how they were coordinated]

## Result[Provide the comprehensive final answer in the language requested by the user]
---`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subAgents&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nx"&gt;agent1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the orchestrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:8000&lt;/code&gt; to witness complex routing and multi-agent problem-solving in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4vttp6mq946rxfk35xv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4vttp6mq946rxfk35xv.jpg" alt="fig2e" width="800" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent-to-Agent (A2A) Integration with Gemini CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6. A2A Server Implementation (Sample 6)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Exposing the multi-agent system to external clients via an A2A server.&lt;/p&gt;

&lt;p&gt;To fully integrate these TypeScript agents into enterprise workflows, we can expose them as an Agent-to-Agent (A2A) service. This allows tools like the Gemini CLI to communicate directly with our orchestrator.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sample6/a2aserver.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * sample6/a2aserver.ts
 *
 * A2A server that dynamically loads agents.
 * Usage: SAMPLE_TYPE=5 npx tsx sample6/a2aserver.ts
 */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toA2a&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample1/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample2/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample3/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample4/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;agent5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../sample5/agent.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agent5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SAMPLE_TYPE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;targetAgent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid SAMPLE_TYPE: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// For A2A&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;toA2a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server started on http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Try: http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/agent-card.json`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;startServer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the A2A server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run sample6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon execution, you will see a confirmation output in your terminal indicating the server and MCP roots have started successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm run sample6

&amp;gt; adk-full-samples@1.0.0 sample6
&amp;gt; npx tsx sample6/a2aserver.ts

Secure MCP Filesystem Server running on stdio
Client does not support MCP Roots, using allowed directories set from server args:[ '/{your directory}/sample3/sample' ]
Server started on http://localhost:8000
Try: http://localhost:8000/.well-known/agent-card.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure this A2A server as a subagent for the Gemini CLI, create or update &lt;code&gt;.gemini/agents/sample-adk-agent.md&lt;/code&gt; with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
kind: remote
name: sample-adk-agent
agent_card_url: http://localhost:8000/.well-known/agent-card.json
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once configured, launch the Gemini CLI. You will now be able to delegate complex tasks directly to your &lt;code&gt;sample-adk-agent&lt;/code&gt; subagent:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/p2CIkz3ORdg"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Additionally, you can inspect the agent card specifications directly by navigating to the provided URL (&lt;code&gt;http://localhost:8000/.well-known/agent-card.json&lt;/code&gt;) in your browser. &lt;/p&gt;

&lt;p&gt;While this article demonstrates running the A2A server in a local environment for testing purposes, deploying this architecture to fully managed serverless platforms—such as Google Cloud Run or similar services—will significantly increase its scalability. Cloud-native hosting ensures the A2A server can automatically scale to meet the demands of high-concurrency enterprise workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Outlook
&lt;/h2&gt;

&lt;p&gt;It is important to note that the TypeScript version of the ADK is still actively under development. As the framework evolves, developers can anticipate frequent updates that will introduce further usability improvements, streamlined APIs, and robust new features, continuing to close the maturity gap with its Python counterpart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Google's TypeScript ADK provides an optimal foundation for building highly concurrent, type-safe AI agents tailored for modern full-stack web architectures.&lt;/li&gt;
&lt;li&gt;Specialized, single-purpose subagents drastically improve output reliability, efficiently handling discrete tasks ranging from logical validation to real-time API integrations.&lt;/li&gt;
&lt;li&gt;The Model Context Protocol (MCP) securely extends agent capabilities, enabling direct interactions with local filesystems and remote knowledge bases without compromising security.&lt;/li&gt;
&lt;li&gt;Advanced orchestration models allow complex workflows to be dynamically distributed across multiple agents using serial, parallel, or iterative execution strategies.&lt;/li&gt;
&lt;li&gt;Implementing an Agent-to-Agent (A2A) server allows seamless external integration, transforming custom TypeScript agents into scalable remote subagents executable natively within the Gemini CLI.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>ai</category>
      <category>typescript</category>
      <category>adk</category>
    </item>
    <item>
      <title>Integrating Remote Subagents Built by Google Apps Script with Gemini CLI</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Mon, 13 Apr 2026 05:21:33 +0000</pubDate>
      <link>https://dev.to/gde/integrating-remote-subagents-built-by-google-apps-script-with-gemini-cli-h36</link>
      <guid>https://dev.to/gde/integrating-remote-subagents-built-by-google-apps-script-with-gemini-cli-h36</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6v6pinzvbgowpfcpgno.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6v6pinzvbgowpfcpgno.jpg" alt="fig1a" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article explores integrating remote subagents built with Google Apps Script into the Gemini CLI using the Agent-to-Agent (A2A) protocol. It demonstrates how bypassing standard authentication via local agent cards enables seamless execution of complex workflows while effectively overcoming Tool Space Interference (TSI) for massive toolsets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, remote subagent support was introduced to the Gemini CLI. &lt;a href="https://geminicli.com/docs/core/remote-agents/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; With this feature, the Gemini CLI connects to remote subagents using the Agent-to-Agent (A2A) protocol, expanding its capabilities by delegating tasks to external services. I have previously published several articles discussing the A2A server architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/building-agent2agent-a2a-server-with-google-apps-script-d3efd32c7ca7" rel="noopener noreferrer"&gt;Building Agent2Agent (A2A) Server with Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/enabling-collaborative-agent-systems-through-google-apps-script-based-agent2agent-a2a-network-19e3d0472eaa" rel="noopener noreferrer"&gt;Enabling Collaborative Agent Systems through Google Apps Script-based Agent2Agent (A2A) Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/consolidating-generative-ai-protocols-a-single-server-solution-for-mcp-and-a2a-84e1e75ccbcf" rel="noopener noreferrer"&gt;Consolidating Generative AI Protocols: A Single Server Solution for MCP and A2A&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/overcoming-tool-space-interference-bridging-google-adk-and-a2a-sdk-via-google-apps-script-44e9f161e235" rel="noopener noreferrer"&gt;Overcoming Tool Space Interference: Bridging Google ADK and A2A SDK via Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These articles introduce A2A servers built with Google Apps Script (GAS) Web Apps. GAS serves as an ideal foundation for A2A servers because its low-code environment is easily navigable by both human developers and generative AI. Furthermore, GAS is accessible to anyone with a Google account and offers native affinity with Google Workspace. It simplifies complex operations by facilitating seamless OAuth scope authentication and providing direct access to Google APIs via Advanced Google Services.&lt;/p&gt;

&lt;p&gt;By utilizing GAS, developers can rapidly deploy lightweight, category-specific agents that communicate via the A2A protocol. This forms a robust mesh of capabilities without the infrastructure overhead of traditional server environments.&lt;/p&gt;

&lt;p&gt;An excellent foundational article on the basic methods for using subagents with the Gemini CLI was recently published by Romin Irani. &lt;a href="https://medium.com/google-cloud/mastering-gemini-cli-subagents-part-1-a4666091c154" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Building on that foundation, this article introduces a specific approach for securely and natively integrating remote subagents built by Google Apps Script Web Apps with the Gemini CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstration
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/GE0C8QygPV8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41r7imhys6shwclrgzri.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41r7imhys6shwclrgzri.jpg" alt="Workflow" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites and Repository
&lt;/h2&gt;

&lt;p&gt;This article assumes the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have installed and configured the latest Gemini CLI.&lt;/li&gt;
&lt;li&gt;You have an active API key for the Gemini API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can view all the sample scripts used in this article at the following repository:&lt;a href="https://github.com/tanaikech/gemini-cli-gas-a2a-subagents" rel="noopener noreferrer"&gt;https://github.com/tanaikech/gemini-cli-gas-a2a-subagents&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Basic Integration: Connecting Gemini CLI to a GAS-based A2A Server
&lt;/h2&gt;

&lt;p&gt;This section demonstrates a simple test to confirm the connection between the Gemini CLI and a remote subagent (A2A server) built with Google Apps Script.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Building the A2A Server
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.1.1 Google Apps Script Preparation
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Copy Google Apps Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Log in to your Google account and access the following URL to view the Google Apps Script project dashboard. Click the copy icon in the top right corner to copy the standalone script to your root folder:&lt;a href="https://script.google.com/home/projects/1vcbr7E7XeJafVGdV6QKEgsr9H0-Vl_zLOQSjgysDCs2olWlrE43HGOne" rel="noopener noreferrer"&gt;https://script.google.com/home/projects/1vcbr7E7XeJafVGdV6QKEgsr9H0-Vl_zLOQSjgysDCs2olWlrE43HGOne&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Preparation of Google Apps Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you prefer to create the sample A2A server manually, follow these steps:&lt;/p&gt;

&lt;p&gt;Create a new standalone Google Apps Script project. &lt;a href="https://developers.google.com/apps-script/guides/projects#create-standalone" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;appsscript.json&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Overwrite &lt;code&gt;appsscript.json&lt;/code&gt; with the following JSON. Adjust &lt;code&gt;timeZone&lt;/code&gt; to your local timezone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeZone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabledAdvancedServices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"libraries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"userSymbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MCPA2Aserver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"libraryId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1xRlK6KPhqp374qAyumrtVXXNEtb7iZ_rD7yRuYxccQuYieKCCao9VuB6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"developmentMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"webapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"executeAs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USER_DEPLOYING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ANYONE_ANONYMOUS"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exceptionLogging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STACKDRIVER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"V8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oauthScopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/script.external_request"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MCPA2Aserver&lt;/code&gt; is a Google Apps Script library designed to consolidate Generative AI protocols into a single server solution. It enables developers to easily build and deploy servers supporting both the Model Context Protocol (MCP) and the Agent-to-Agent (A2A) protocol. The repository is available at&lt;a href="https://github.com/tanaikech/MCPA2Aserver-GAS-Library" rel="noopener noreferrer"&gt;https://github.com/tanaikech/MCPA2Aserver-GAS-Library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this manifest file, only a scope &lt;code&gt;https://www.googleapis.com/auth/script.external_request&lt;/code&gt; is set for using sample 2 skills. However, if you wish to use other scopes, please modify this accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;code.gs&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copy and paste the following script into &lt;code&gt;code.gs&lt;/code&gt;. Set your API key for using the Gemini API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// --- Your variables ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{apiKey}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your API key for using Gemini API.&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;models/gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// If you want to use an access key for requesting Web Apps, please use this.&lt;/span&gt;
  &lt;span class="c1"&gt;// logSpreadsheetId: "{spreadsheetId}", // If you use this, the logs are stored to Google Spreadsheet.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// --- Entry Points ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doGet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Main Dispatcher Function
 * Routes the request to either A2A handler or MCP handler based on the payload or path.
 *
 * @param {EventObject} e - The event object from doGet/doPost
 * @return {ContentService.TextOutput} The JSON response
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerContext_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Load sample tools.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MCPA2Aserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a2a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logSpreadsheetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logSpreadsheetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logSpreadsheetId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Please get the agent card from the following function.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAgentCard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerContext_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agentCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;A2AObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;agentCard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;disp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agentCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;disp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;agent.gs&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create another script file named &lt;code&gt;agent.gs&lt;/code&gt; and paste the following code. Make sure to update the &lt;code&gt;url&lt;/code&gt; within &lt;code&gt;agentCard&lt;/code&gt; with your Web App URL later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createServerContext_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;params_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;get_exchange_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use this to get current exchange rate.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Source currency (major currency). Default is USD.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Destination currency (major currency). Default is EUR.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date of the currency. Default is latest. It should be ISO format (YYYY-MM-DD).&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency_from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency_to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency_date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="na"&gt;get_current_weather&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use this to get the weather using the latitude and the longitude.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;At that time, convert the location to the latitude and the longitude and provide them to the function.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;`The date is required to be included. The date format is "yyyy-MM-dd HH:mm"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;`If you cannot know the location, decide the location using the timezone.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The latitude of the inputed location.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The longitude of the inputed location.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Date for searching the weather. The date format is "yyyy-MM-dd HH:mm"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The timezone. In the case of Japan, "Asia/Tokyo" is used.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latitude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;longitude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timezone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Ref: https://github.com/google/A2A/blob/main/samples/python/agents/langgraph/agent.py#L19
     */&lt;/span&gt;
    &lt;span class="na"&gt;get_exchange_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run the function get_exchange_rate.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Check arguments.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currency_from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;currency_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;currency_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`https://api.frankfurter.app/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?from=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;to=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s2"&gt;`The raw data from the API is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resStr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. The detailed result is as follows.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;`The currency rate at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" to "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currency_to&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Check response.&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;a2a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * This function returns the current weather.
     * The API is from https://open-meteo.com/
     *
     * { latitude = "35.681236", longitude = "139.767125", date = "2025-05-27 12:00", timezone = "Asia/Tokyo" } is Tokyo station.
     */&lt;/span&gt;
    &lt;span class="na"&gt;get_current_weather&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run the function get_current_weather.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Check arguments.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;35.681236&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;139.767125&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-05-27 12:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ref: https://open-meteo.com/en/docs?hourly=weather_code&amp;amp;current=weather_code#weather_variable_documentation&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clear sky&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mainly clear, partly cloudy, and overcast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fog and depositing rime fog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fog and depositing rime fog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drizzle: Light, moderate, and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Drizzle: Light and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Drizzle: Light and dense intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain: Slight, moderate and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Rain: Light and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freezing Rain: Light and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow fall: Slight, moderate, and heavy intensity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow grains&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rain showers: Slight, moderate, and violent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow showers slight and heavy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;86&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snow showers slight and heavy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm: Slight or moderate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm with slight and heavy hail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thunderstorm with slight and heavy hail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.open-meteo.com/v1/forecast?latitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;longitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;hourly=weather_code&amp;amp;timezone=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;muteHttpExceptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;hourly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;weather_code&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;widx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;T&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;widx&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;weather_code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;widx&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No value was returned. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No value was returned. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Check response.&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;a2a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * If you want to return the file content to MCP client, please set the return value as follows.
   * {
   *   jsonrpc: "2.0",
   *   result: {
   *    content:[
   *      {
   *        type: "text",
   *         text: "sample text",
   *       },
   *       {
   *         type: "image",
   *         data: "base64 data",
   *         mimeType: "mimetype",
   *       },
   *     ],
   *     isError: false,
   *   }
   * }
   *
   * If you want to return the file content to A2A client, please set the return value as follows.
   * {
   *   result: {
   *     type: "file",
   *     kind: "file",
   *     file: {
   *       name: "filename",
   *       bytes: "base64 data",
   *       mimeType: "mimetype",
   *     },
   *     metadata: null
   *   }
   * }
   *
   */&lt;/span&gt;

  &lt;span class="c1"&gt;// for A2A&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agentCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API Manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;`Provide management for using various APIs.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`- Run with exchange values between various currencies. For example, this answers "What is the exchange rate between USD and GBP?".`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`- Return the weather information by providing the location and the date, and the time.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tanaike&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://github.com/tanaikech&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://script.google.com/macros/s/{deploymentId}/exec?accessKey=sample`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Please replace this to your Web Apps URL.&lt;/span&gt;
    &lt;span class="na"&gt;defaultInputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;defaultOutputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;streaming&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pushNotifications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stateTransitionHistory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_exchange_rate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Currency Exchange Rates Tool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Helps with exchange values between various currencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency conversion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency exchange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is exchange rate between USD and GBP?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;inputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;outputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_current_weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get current weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This agent can return the weather information by providing the location and the date, and the time.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Return the weather in Tokyo for tomorrow's lunchtime.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Return the weather in Tokyo for 9 AM on May 27, 2025.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;inputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;outputModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;A2AObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;agentCard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;agentCard&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1.1.2 Deploying Google Apps Script as a Web App
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open the script editor.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute as&lt;/strong&gt;: Me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who has access&lt;/strong&gt;: Anyone.&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Web App URL&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  1.1.3 Configuration
&lt;/h4&gt;

&lt;p&gt;Update the &lt;code&gt;url&lt;/code&gt; property inside the &lt;code&gt;agentCard&lt;/code&gt; variable in your &lt;code&gt;agent.gs&lt;/code&gt; script with your copied Web App URL. Make sure to redeploy the Web App after updating the code to apply the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Configuring the Gemini CLI
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.2.1 Setting the GAS A2A Server as a Subagent
&lt;/h4&gt;

&lt;p&gt;Despite the advantages of using Google Apps Script, a technical hurdle persists: standard A2A clients cannot natively interact with GAS-based servers automatically. This is because retrieving the agent card dynamically requires a GET request to &lt;code&gt;https://script.google.com/macros/s/{deploymentId}/exec/.well-known/agent.json&lt;/code&gt;. Currently, this endpoint requires access token authorization. While Google Application Default Credentials (ADC) can be used for installing the subagents on Gemini CLI, they are restricted to &lt;code&gt;*.googleapis.com&lt;/code&gt; and &lt;code&gt;*.run.app&lt;/code&gt; endpoints. GAS Web App endpoints are not yet supported for automated retrieval. &lt;a href="https://geminicli.com/docs/core/remote-agents/#google-application-default-credentials-google-credentials" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, remote subagents in the Gemini CLI allow developers to directly define the agent card locally. This elegant workaround bypasses the authentication process entirely and reduces the network overhead of loading the agent card from the GAS Web App.&lt;/p&gt;

&lt;p&gt;To generate the agent card JSON, run the &lt;code&gt;getAgentCard()&lt;/code&gt; function in your &lt;code&gt;code.gs&lt;/code&gt; script. This prints the necessary JSON object to the Apps Script execution console.&lt;/p&gt;

&lt;p&gt;Save the obtained JSON to define the subagent in your working directory. Create a file at &lt;code&gt;.gemini/agents/sample-gas-agent.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;remote&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample-gas-agent&lt;/span&gt;
&lt;span class="na"&gt;agent_card_json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"name": "API Manager",&lt;/span&gt;
    &lt;span class="s"&gt;"description": "Provide management for using various APIs.\n- Run with exchange values between various currencies. For example, this answers \"What is the exchange rate between USD and GBP?\".\n- Return the weather information by providing the location and the date, and the time.",&lt;/span&gt;
    &lt;span class="s"&gt;"provider": {&lt;/span&gt;
      &lt;span class="s"&gt;"organization": "Tanaike",&lt;/span&gt;
      &lt;span class="s"&gt;"url": "https://github.com/tanaikech"&lt;/span&gt;
    &lt;span class="s"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"version": "1.0.0",&lt;/span&gt;
    &lt;span class="s"&gt;"url": "https://script.google.com/macros/s/{your deployment ID}/exec?accessKey=sample",&lt;/span&gt;
    &lt;span class="s"&gt;"defaultInputModes":[&lt;/span&gt;
      &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
    &lt;span class="s"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"defaultOutputModes":[&lt;/span&gt;
      &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
    &lt;span class="s"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"capabilities": {&lt;/span&gt;
      &lt;span class="s"&gt;"streaming": false,&lt;/span&gt;
      &lt;span class="s"&gt;"pushNotifications": false,&lt;/span&gt;
      &lt;span class="s"&gt;"stateTransitionHistory": false&lt;/span&gt;
    &lt;span class="s"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"skills":[&lt;/span&gt;
      &lt;span class="s"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"id": "get_exchange_rate",&lt;/span&gt;
        &lt;span class="s"&gt;"name": "Currency Exchange Rates Tool",&lt;/span&gt;
        &lt;span class="s"&gt;"description": "Helps with exchange values between various currencies",&lt;/span&gt;
        &lt;span class="s"&gt;"tags":[&lt;/span&gt;
          &lt;span class="s"&gt;"currency conversion",&lt;/span&gt;
          &lt;span class="s"&gt;"currency exchange"&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"examples":[&lt;/span&gt;
          &lt;span class="s"&gt;"What is exchange rate between USD and GBP?"&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"inputModes": [&lt;/span&gt;
          &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"outputModes":[&lt;/span&gt;
          &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
        &lt;span class="s"&gt;]&lt;/span&gt;
      &lt;span class="s"&gt;},&lt;/span&gt;
      &lt;span class="s"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"id": "get_current_weather",&lt;/span&gt;
        &lt;span class="s"&gt;"name": "Get current weather",&lt;/span&gt;
        &lt;span class="s"&gt;"description": "This agent can return the weather information by providing the location and the date, and the time.",&lt;/span&gt;
        &lt;span class="s"&gt;"tags":[&lt;/span&gt;
          &lt;span class="s"&gt;"weather"&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"examples":[&lt;/span&gt;
          &lt;span class="s"&gt;"Return the weather in Tokyo for tomorrow's lunchtime.",&lt;/span&gt;
          &lt;span class="s"&gt;"Return the weather in Tokyo for 9 AM on May 27, 2025."&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"inputModes": [&lt;/span&gt;
          &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
        &lt;span class="s"&gt;],&lt;/span&gt;
        &lt;span class="s"&gt;"outputModes":[&lt;/span&gt;
          &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
        &lt;span class="s"&gt;]&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 Testing the Connection
&lt;/h3&gt;

&lt;p&gt;Launch the Gemini CLI. The application will detect the new subagent as shown below. Select &lt;code&gt;Acknowledge and Enable&lt;/code&gt; to activate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqybuflyuazoiq6tndtn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqybuflyuazoiq6tndtn.jpg" alt="fig2a" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the &lt;code&gt;/agents&lt;/code&gt; command in the chat displays the installed subagents. Your installed &lt;code&gt;sample-gas-agent&lt;/code&gt; will appear under remote agents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fauvf91nkzzni8w0espyt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fauvf91nkzzni8w0espyt.jpg" alt="fig2b" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also invoke the agent directly using &lt;code&gt;@sample-gas-agent&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz99j3d3422lpb0spyjmm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz99j3d3422lpb0spyjmm.jpg" alt="fig2c" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Input the following sample prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What is exchange rate between USD and GBP?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Return the weather in Tokyo for tomorrow's lunchtime.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following results are returned for each prompt, confirming that the responses were generated securely through the GAS-based &lt;code&gt;sample-gas-agent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yxb62wbq6d4n65p8jpy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yxb62wbq6d4n65p8jpy.jpg" alt="fig2d" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When combining these requests into a single prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I’m planning a trip from London to Japan and need to finalize my budget and itinerary; could you tell me the current USD to GBP exchange rate so I can manage my funds, and also let me know tomorrow's lunchtime weather in Tokyo so I can decide whether to book an outdoor terrace for my arrival meal?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic826327m4m6fqm5gnqg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic826327m4m6fqm5gnqg.jpg" alt="fig2e" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The subagent successfully processes both requests and generates the correct response.&lt;/p&gt;

&lt;p&gt;Alternatively, you can direct a specific prompt exclusively to the subagent by prefixing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@sample-gas-agent What is exchange rate between USD and GBP?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Advanced Integration: Orchestrating Google Workspace with Subagents
&lt;/h2&gt;

&lt;p&gt;As an enhanced test, we will install the &lt;code&gt;google-workspace-orchestrator&lt;/code&gt; subagent, which integrates heavily with Google Workspace to perform complex cross-application tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Building the Advanced A2A Server
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.1.1 Copying the Google Apps Script
&lt;/h4&gt;

&lt;p&gt;For this advanced setup, copy the following Google Apps Script project by clicking the copy button on the dashboard:&lt;a href="https://script.google.com/home/projects/1xIwskiWAychSp3JN25s7AVbpJf9aRlSvCE8C9szIxPnFHZBeeX3Eo7vT" rel="noopener noreferrer"&gt;https://script.google.com/home/projects/1xIwskiWAychSp3JN25s7AVbpJf9aRlSvCE8C9szIxPnFHZBeeX3Eo7vT&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1.2 Deploying as a Web App
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open the script editor.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute as&lt;/strong&gt;: Me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who has access&lt;/strong&gt;: Anyone.&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Web App URL&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.1.3 Configuration
&lt;/h4&gt;

&lt;p&gt;Update &lt;code&gt;webAppsUrl&lt;/code&gt; within the &lt;code&gt;object&lt;/code&gt; variable in the &lt;code&gt;code.gs&lt;/code&gt; script. Make sure to redeploy the Web App after updating the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Configuring the Gemini CLI
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.2.1 Setting the Advanced Subagent
&lt;/h4&gt;

&lt;p&gt;To define the agent card for this advanced subagent, run the &lt;code&gt;getAgentCard()&lt;/code&gt; function in &lt;code&gt;code.gs&lt;/code&gt;. This will create a text file containing the JSON of the agent card directly in your Google Drive root folder.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;.gemini/agents/google-workspace-orchestrator.md&lt;/code&gt; in your working directory and paste the generated JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;remote&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google-workspace-orchestrator&lt;/span&gt;
&lt;span class="na"&gt;agent_card_json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"name": "Google Workspace Orchestrator",&lt;/span&gt;
    &lt;span class="s"&gt;"description": "This agent acts as a comprehensive interface for the Google Workspace ecosystem and associated Google APIs. It provides extensive capabilities to manage and automate tasks across Gmail (sending, organizing, retrieving), Google Drive (file management, search, permission handling, content generation), and Google Calendar (schedule management). It features deep integration with Google Classroom for managing courses, assignments, and rosters, as well as Google Analytics for reporting. Additionally, it controls Google Docs, Sheets, and Slides for document creation and data manipulation. Advanced features include RAG (Retrieval-Augmented Generation) via File Search stores, image generation, YouTube video summarization, and Google Maps utilities. It serves as a central hub for executing complex workflows involving multiple Google services.",&lt;/span&gt;
    &lt;span class="s"&gt;"provider": {&lt;/span&gt;
      &lt;span class="s"&gt;"organization": "Tanaike",&lt;/span&gt;
      &lt;span class="s"&gt;"url": "https://github.com/tanaikech"&lt;/span&gt;
    &lt;span class="s"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"version": "1.0.0",&lt;/span&gt;
    &lt;span class="s"&gt;"url": "https://script.google.com/macros/s/{your deployment ID}/exec?accessKey=sample",&lt;/span&gt;
    &lt;span class="s"&gt;"defaultInputModes":[&lt;/span&gt;
      &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
    &lt;span class="s"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"defaultOutputModes":[&lt;/span&gt;
      &lt;span class="s"&gt;"text/plain"&lt;/span&gt;
    &lt;span class="s"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"capabilities": {&lt;/span&gt;
      &lt;span class="s"&gt;"streaming": false,&lt;/span&gt;
      &lt;span class="s"&gt;"pushNotifications": false,&lt;/span&gt;
      &lt;span class="s"&gt;"stateTransitionHistory": false&lt;/span&gt;
    &lt;span class="s"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"skills":[&lt;/span&gt;
      &lt;span class="s"&gt;// ...&lt;/span&gt;
      &lt;span class="s"&gt;// 160 skills&lt;/span&gt;
      &lt;span class="s"&gt;// ...&lt;/span&gt;
   &lt;span class="s"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Testing the Advanced Workflows
&lt;/h3&gt;

&lt;p&gt;Launch the Gemini CLI and select &lt;code&gt;Acknowledge and Enable&lt;/code&gt; for the newly detected &lt;code&gt;google-workspace-orchestrator&lt;/code&gt; subagent.&lt;/p&gt;

&lt;p&gt;To avoid interference, disable the previous subagent by running the following command in the chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/agents disable sample-gas-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the &lt;code&gt;/agents&lt;/code&gt; command will now show that the &lt;code&gt;google-workspace-orchestrator&lt;/code&gt; subagent has 160 skills enabled. You can view the full list of detailed skills at &lt;a href="https://github.com/tanaikech/ToolsForMCPServer" rel="noopener noreferrer"&gt;https://github.com/tanaikech/ToolsForMCPServer&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;gt; /agents

Local Agents

  - Codebase Investigator Agent (codebase_investigator)
    The specialized tool for codebase analysis...
  - CLI Help Agent (cli_help)
    Specialized agent for answering questions about the Gemini CLI...
  - Generalist Agent (generalist)
    A general-purpose AI agent with access to all tools...

Remote Agents

  - google-workspace-orchestrator
    Agent Description: This agent acts as a comprehensive interface for the Google Workspace ecosystem...
    Skills:
    .
    .
    160 skills
    .
    .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test 1: Generating a Cooking Roadmap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Input the following prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to cook miso soup.
To achieve this goal, create a new Google Spreadsheet,
generate a roadmap for cooking miso soup in the spreadsheet,
and return the Spreadsheet URL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gemini CLI initiates the task:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow5e1rhra2i96ry0jp21.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow5e1rhra2i96ry0jp21.jpg" alt="fig3a" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The requested roadmap is successfully generated and populated into Google Sheets:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqonv0ncbcowvb89tzddi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqonv0ncbcowvb89tzddi.jpg" alt="fig3b" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 2: Complex Document Generation and Email Delivery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Input the following prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write a comprehensive article about developing Google Apps Script (GAS) using generative AI.
The article should include an introductory overview, formatted lists for best practices,
and a table comparing different AI-assisted coding techniques.
Once generated, please create a new Google Document, insert the content, convert the Google Document to a PDF file,
and send an email to `tanaike@hotmail.com` including the shareable URL of the PDF file by giving a suitable title and email body.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gemini CLI coordinates multiple tools to execute the workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscn2q7ga9hqpt2xwmou7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscn2q7ga9hqpt2xwmou7.jpg" alt="fig3c" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following PDF file is created from the generated Google Document:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn26sww7llgiweq5rlz5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn26sww7llgiweq5rlz5.jpg" alt="fig3d" width="800" height="2011"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, an email containing the PDF link is sent securely:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdityox43ltuq7zwy3k3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdityox43ltuq7zwy3k3.jpg" alt="fig3e" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Architectural Advantage: Overcoming Tool Space Interference (TSI)
&lt;/h2&gt;

&lt;p&gt;The advanced task demonstrated above requires executing multiple parallel processes natively through the &lt;code&gt;google-workspace-orchestrator&lt;/code&gt; subagent, which contains an astonishing 160 distinct skills.&lt;/p&gt;

&lt;p&gt;When this massive number of skills is loaded directly into a single MCP server or a standard AI context window, it frequently causes a critical challenge known as &lt;strong&gt;Tool Space Interference (TSI)&lt;/strong&gt;. TSI is a phenomenon where verbose metadata saturates the context window, severely degrading the AI's reasoning accuracy and frequently causing logic failures or hallucinations. &lt;a href="https://medium.com/google-cloud/nexus-mcp-a-unified-gateway-for-scalable-and-deterministic-mcp-server-aggregation-3211f0adc603" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; &lt;a href="https://www.microsoft.com/en-us/research/blog/tool-space-interference-in-the-mcp-era-designing-for-agent-compatibility-at-scale/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Current industry guidelines suggest a “soft limit” of 20 functions per agent to maintain stability.&lt;/p&gt;

&lt;p&gt;To mitigate TSI, I previously proposed Nexus-MCP. &lt;a href="https://medium.com/google-cloud/nexus-mcp-a-unified-gateway-for-scalable-and-deterministic-mcp-server-aggregation-3211f0adc603" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Nexus-MCP functions as a centralized gateway employing a deterministic four-phase workflow to map and filter tools. While highly effective, Nexus-MCP relies on a single AI agent acting as the client, making it less suitable for true distributed task execution where specific tool categories should be handled by specialized agents.&lt;/p&gt;

&lt;p&gt;As an alternative approach, I proposed using an A2A server architecture. &lt;a href="https://medium.com/google-cloud/overcoming-tool-space-interference-bridging-google-adk-and-a2a-sdk-via-google-apps-script-44e9f161e235" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; At the time, this required utilizing a heavily modified &lt;code&gt;@a2a-js/sdk&lt;/code&gt; because the agent card could not be natively retrieved from GAS Web Apps.&lt;/p&gt;

&lt;p&gt;A major merit of the integration method introduced in this article is its ability to bypass TSI entirely while using official tools. By directly defining the agent card locally in &lt;code&gt;.gemini/agents/google-workspace-orchestrator.md&lt;/code&gt;, this architecture functions natively within the Gemini CLI. It elegantly avoids TSI by delegating the vast tool execution space entirely to the remote A2A subagent, thereby preserving the main CLI agent's reasoning capacity, stability, and speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Gemini CLI now supports remote subagents via the A2A protocol, allowing developers to safely extend and delegate capabilities.&lt;/li&gt;
&lt;li&gt;Google Apps Script (GAS) serves as an ideal, accessible backend for these subagents due to its low-code environment and native integration with Google Workspace.&lt;/li&gt;
&lt;li&gt;Defining the agent card locally in the Gemini CLI easily bypasses the authentication hurdles typically associated with retrieving metadata dynamically from GAS Web Apps.&lt;/li&gt;
&lt;li&gt;This architectural pattern resolves Tool Space Interference (TSI) by offloading massive toolsets (like 160+ Google Workspace skills) to dedicated remote agents.&lt;/li&gt;
&lt;li&gt;Consequently, developers can reliably execute complex, multi-step operations without degrading the reasoning capacity or token limits of the main AI agent.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
      <category>a2a</category>
    </item>
    <item>
      <title>Monitoring Sheet Changes with SHEET and SHEETS Functions on Google Sheets</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 08 Apr 2026 05:38:35 +0000</pubDate>
      <link>https://dev.to/gde/monitoring-sheet-changes-with-sheet-and-sheets-functions-on-google-sheets-19dc</link>
      <guid>https://dev.to/gde/monitoring-sheet-changes-with-sheet-and-sheets-functions-on-google-sheets-19dc</guid>
      <description>&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Google Sheets recently introduced the &lt;code&gt;SHEET&lt;/code&gt; and &lt;code&gt;SHEETS&lt;/code&gt; functions. Because they automatically recalculate upon structural changes, developers can utilize them as custom triggers. This article demonstrates how to leverage these functions to detect sheet insertions, deletions, renames, and movements without requiring cumbersome installable triggers in Google Apps Script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;On February 23, 2026, Google introduced two pivotal built-in functions to Google Sheets: &lt;code&gt;SHEET&lt;/code&gt; and &lt;code&gt;SHEETS&lt;/code&gt; &lt;a href="https://workspaceupdates.googleblog.com/2026/02/two-new-functions-in-google-sheets.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. The &lt;code&gt;SHEET&lt;/code&gt; function returns the index (sheet number) of a specified sheet or reference &lt;a href="https://support.google.com/docs/answer/16865249?dark=1&amp;amp;hl=en" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. Meanwhile, the &lt;code&gt;SHEETS&lt;/code&gt; function provides the total count of sheets within a spreadsheet &lt;a href="https://support.google.com/docs/answer/16865347?dark=1&amp;amp;hl=en" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A critical technical characteristic of these functions is their volatility and automatic recalculation based on the spreadsheet's structural metadata. Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SHEET&lt;/code&gt; triggers a recalculation when a sheet is renamed or its position is changed via drag-and-drop.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SHEETS&lt;/code&gt; triggers a recalculation whenever a sheet is inserted, duplicated, or removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Historically, detecting such structural changes necessitated the use of an &lt;strong&gt;installable OnChange trigger&lt;/strong&gt; in Google Apps Script (GAS). This posed a significant barrier for template distribution, as installable triggers require manual authorization by each user and do not persist through simple file copies.&lt;/p&gt;

&lt;p&gt;By leveraging these new functions as "custom triggers," we can effectively bypass the need for installable triggers. When a custom function or formula containing &lt;code&gt;SHEET&lt;/code&gt; or &lt;code&gt;SHEETS&lt;/code&gt; recalculates, it serves as a catalyst for GAS execution. This enables the creation of self-contained spreadsheets where advanced sheet monitoring logic is activated immediately upon copying the file, significantly improving the user experience and portability of GAS-based solutions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cjy8kgmtlpqbb93sk0d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cjy8kgmtlpqbb93sk0d.jpg" alt="fig1" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Spreadsheet Initialization
&lt;/h3&gt;

&lt;p&gt;Create a new Google Sheets file to serve as your testing environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Google Apps Script Configuration
&lt;/h3&gt;

&lt;p&gt;Open the Google Apps Script editor bound to your new spreadsheet. Copy and paste the following script, replacing any default code, and save your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Global constant defining the Script Properties storage key.
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHEET_STATUS_CACHE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Simple trigger to initialize the cached sheet metadata upon opening the spreadsheet.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onOpen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;updateStoredSheetData_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Custom function wrapper to retrieve an array of all current sheet names.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllSheetNames&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Core custom function for detecting structural alterations.
 * Priority: If any structural change (Add/Remove/Rename) occurs,
 * subsequent "move" reports are suppressed to avoid noise from index shifting.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSheetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_trigger1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_trigger2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnRawObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;propService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldDataJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentSheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentSheets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;oldDataJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;updateStoredSheetData_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Initial state recorded.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldDataJson&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="c1"&gt;// Categorize detected modifications into structural changes or simple index moves.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moves&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;detectCategorizedChanges_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Persist the newly fetched metadata to Script Properties.&lt;/span&gt;
  &lt;span class="nx"&gt;propService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}))),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Evaluate which results to return based on change priority.&lt;/span&gt;
  &lt;span class="c1"&gt;// Ignore index movement noise if a primary structural change (addition, removal, rename) occurred.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalDiffs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;structural&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;moves&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalDiffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;returnRawObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalDiffs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;finalDiffs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Retrieves current spreadsheet metadata and persists it to the properties cache.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateStoredSheetData_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheetData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getScriptProperties&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetData&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sheetData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Differentiates between core structural changes and simple index shifts.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;detectCategorizedChanges_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;structural&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Identify removed sheets (Structural change).&lt;/span&gt;
  &lt;span class="nx"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;newMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" was removed.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Identify added, renamed, or repositioned sheets.&lt;/span&gt;
  &lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldSheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle added sheets (Structural change).&lt;/span&gt;
      &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;added&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" was inserted at tab &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isRenamed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMoved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isRenamed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle renamed sheets (Structural change).&lt;/span&gt;
        &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;renamed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" was renamed to "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;".`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMoved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle pure index movements (Non-structural change).&lt;/span&gt;
        &lt;span class="nx"&gt;moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;moved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" was moved from tab &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;oldSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;structural&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moves&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Optional callback for installable OnChange triggers.
 * If utilized, do not use the custom formula in the sheet.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;changeType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OTHER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT_GRID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REMOVE_GRID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSheetStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;msgBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Applying the Custom Formula
&lt;/h3&gt;

&lt;p&gt;Return to your Google Sheets interface. In the first sheet, copy and paste the following formula into cell &lt;code&gt;A1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=getSheetStatus(SHEETS(),MAP(getAllSheetNames(),lambda(sheetname,SHEET(sheetname))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Testing the Workflow
&lt;/h3&gt;

&lt;p&gt;Once the formula is in place, the cell will actively monitor the document's structure. The demonstration below illustrates the expected behavior:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1dmydxh5p1ekfpgg252.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1dmydxh5p1ekfpgg252.gif" alt="fig2" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the demonstration, the following actions trigger a status update in the cell:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Renaming an existing sheet.&lt;/li&gt;
&lt;li&gt; Adding a new sheet to the workbook.&lt;/li&gt;
&lt;li&gt; Changing the order (moving) of a sheet.&lt;/li&gt;
&lt;li&gt; Deleting a sheet.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Appendix: Using Traditional Triggers
&lt;/h2&gt;

&lt;p&gt;If you prefer to use the traditional installable &lt;code&gt;onChange&lt;/code&gt; trigger method, you can utilize the &lt;code&gt;onChange&lt;/code&gt; function provided at the bottom of the script. In this scenario, you must manually bind the trigger to the &lt;code&gt;onChange&lt;/code&gt; function via the Apps Script dashboard and &lt;strong&gt;remove&lt;/strong&gt; the custom formula (&lt;code&gt;=getSheetStatus(...)&lt;/code&gt;) from cell &lt;code&gt;A1&lt;/code&gt;. The underlying logic handles the detection identically, routing the output to a browser message box instead of a cell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The introduction of &lt;code&gt;SHEET&lt;/code&gt; and &lt;code&gt;SHEETS&lt;/code&gt; functions enables automatic formula recalculation based on structural metadata changes.&lt;/li&gt;
&lt;li&gt;These built-in functions can be passed into custom Google Apps Script functions to act as volatile execution triggers.&lt;/li&gt;
&lt;li&gt;This methodology eliminates the traditional dependency on installable &lt;code&gt;onChange&lt;/code&gt; triggers for monitoring tab modifications.&lt;/li&gt;
&lt;li&gt;Removing installable triggers drastically improves script portability and the user experience when distributing spreadsheet templates.&lt;/li&gt;
&lt;li&gt;The provided script successfully categorizes and logs structural alterations versus simple index shifts for comprehensive monitoring.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
      <category>googlesheets</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Recursive Knowledge Crystallization: Enabling Persistent Evolution and Zero-Shot Transfer in AI Agents</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Thu, 02 Apr 2026 06:50:50 +0000</pubDate>
      <link>https://dev.to/gde/recursive-knowledge-crystallization-enabling-persistent-evolution-and-zero-shot-transfer-in-ai-4fh7</link>
      <guid>https://dev.to/gde/recursive-knowledge-crystallization-enabling-persistent-evolution-and-zero-shot-transfer-in-ai-4fh7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbx0q0fu8jaosvbqg0sq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbx0q0fu8jaosvbqg0sq.jpg" alt="fig1a" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This paper presents a self-evolving framework, &lt;strong&gt;Recursive Knowledge Crystallization (RKC)&lt;/strong&gt;, designed to overcome the "Catastrophic Forgetting" inherent in autonomous AI agents. By persisting evolved technical insights into a universally readable &lt;code&gt;SKILL.md&lt;/code&gt; file based on the &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Agent skills&lt;/a&gt; specification, this approach establishes long-term memory and cross-platform portability. The framework was empirically validated through the development of &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;gas-fakes&lt;/a&gt;, a highly complex Node.js-to-Google Apps Script (GAS) emulation library. The results demonstrate that agents can autonomously internalize project-specific architectural patterns and environmental nuances. Consequently, the framework achieves &lt;strong&gt;Zero-Shot Knowledge Transfer&lt;/strong&gt; across distinct toolchains (Google Antigravity and the Gemini CLI) while maintaining absolute 1:1 behavioral parity with the live GAS environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;In recent years, the automation of software development by autonomous AI agents powered by Large Language Models (LLMs) has advanced rapidly. However, a critical barrier to the practical deployment of such agents is the absence of a "Persistence of Learning." Constrained by LLM context windows and session fragmentation, agents frequently suffer from "Catastrophic Forgetting," losing vital insights acquired during previous interactions. While memory-augmentation frameworks like Reflexion (Shinn et al., 2023) and MemGPT (Packer et al., 2023) have been proposed, they heavily rely on in-memory contexts, dedicated vector databases, or internal virtual memory abstractions. As a result, the acquired knowledge remains locked within specific agent instances, rendering it difficult to port across environments and nearly impossible for human developers to directly audit or manage.&lt;/p&gt;

&lt;p&gt;While this limitation might remain latent in small-scale "greenfield" projects, it emerges as a fatal bottleneck when applying Generative AI to large, complex, and deeply constrained legacy projects. The&lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;gas-fakes&lt;/a&gt; project, the subject of this empirical study, encountered this exact limitation. This project serves as a foundational library for converting various Google APIs into mock classes and methods compatible with the Google Apps Script (GAS) environment using Node.js. As the codebase and conversion rules grew in complexity, traditional prompting and standard GenAI approaches frequently resulted in "rule violations"—instances where the AI bypassed strict, project-specific coding conventions to force task completion. Despite repeated trial-and-error iterations and manual context adjustments, the success rate remained insufficient for practical use.&lt;/p&gt;

&lt;p&gt;To fundamentally resolve this challenge, this paper introduces a self-evolving framework based on the &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Agent skills&lt;/a&gt; specification and its dynamic evolution model:&lt;a href="https://medium.com/google-cloud/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-8243b3697471" rel="noopener noreferrer"&gt;Recursive Knowledge Crystallization (RKC)&lt;/a&gt;. The defining innovation of this framework is the &lt;strong&gt;"Physical Knowledge Persistence"&lt;/strong&gt; of the agent's operational guidelines and technical expertise. This knowledge is crystallized as a universally readable Markdown file (&lt;code&gt;SKILL.md&lt;/code&gt;) directly onto the local file system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef19nnblij8npvy0xknl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef19nnblij8npvy0xknl.jpg" alt="RKC Framework Architecture" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By integrating Gemini 3 and 3.1 with next-generation toolchains—Google Antigravity and the Gemini CLI—we established the development process of &lt;code&gt;gas-fakes&lt;/code&gt; itself as the agent's continuous learning environment. Specifically, whenever an error occurred during the implementation of new classes or when human developers provided corrective feedback, the agent was issued a single meta-instruction: "Extract insights from the failure and recovery process, and autonomously update your Agent skill (&lt;code&gt;SKILL.md&lt;/code&gt;, sample scripts, and templates) by adding, deleting, or modifying content."&lt;/p&gt;

&lt;p&gt;Through this iterative trial-and-error cycle, the agent accomplished more than merely avoiding local errors; it decoded the implicit rules and complex constraints inherent in the Node.js-to-GAS conversion, eventually internalizing and formalizing the project’s overarching architectural patterns. This evolutionary process extended beyond the &lt;code&gt;SKILL.md&lt;/code&gt; text to include the refinement of helper scripts, templates, and technical specifications.&lt;/p&gt;

&lt;p&gt;As the RKC progressed and the Agent skill reached a state of convergence, the agent fully internalized the complex operational logic of &lt;code&gt;gas-fakes&lt;/code&gt;. This enabled accurate, rapid development strictly compliant with all project standards. Furthermore, by utilizing the standard file format, this method facilitates &lt;strong&gt;"Zero-Shot Knowledge Transfer"&lt;/strong&gt;: implicit knowledge acquired in one environment (e.g., Google Antigravity) can be seamlessly ported to an entirely clean environment (e.g., Gemini CLI) to generate flawless code on the first attempt. Simultaneously, persisting knowledge on a standard file system realizes a &lt;strong&gt;"True Human-AI Collaborative Paradigm,"&lt;/strong&gt; empowering human developers to fully visualize, audit, and guide the agent's cognitive evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Workflow
&lt;/h2&gt;

&lt;p&gt;The development of &lt;code&gt;gas-fakes&lt;/code&gt; follows an iterative evolution of Agent skills, leveraging both Google Antigravity and the Gemini CLI. This process integrates a continuous feedback loop and culminates in Zero-Shot Knowledge Transfer through the physical persistence of evolved skills. The operational flow, visualized in &lt;strong&gt;Figure 2a&lt;/strong&gt;, is defined by the following seven stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Task Initiation&lt;/strong&gt;: A development task is assigned via a prompt to either Google Antigravity or the Gemini CLI, specifying the Google Apps Script (GAS) classes and methods to be emulated in the Node.js environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Autonomous Implementation&lt;/strong&gt;: Utilizing the current Agent skill (&lt;code&gt;gas-fakes-dev&lt;/code&gt;), the agent generates the corresponding Node.js classes and methods. Simultaneously, a test suite is authored by strictly adhering to the architectural patterns defined within the &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Local Environment Validation&lt;/strong&gt;: The generated test scripts are executed in the local Node.js environment. If runtime errors or logic discrepancies occur, the AI autonomously refactors the implementation and the test code until local stability is achieved.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cross-Platform Synchronization&lt;/strong&gt;: Following local success, the implementation is deployed to the Google-side script editor (GAS environment). This ensures that the emulation maintains 1:1 parity with the live GAS engine.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Remote Feedback &amp;amp; Refinement&lt;/strong&gt;: If the test suite encounters environment-specific errors in the GAS script editor (e.g., subtle differences in string output formatting or synchronous execution behavior), these errors are fed back to the AI for immediate remediation.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Knowledge Crystallization&lt;/strong&gt;: Once the implementation passes in both local and remote environments, the task is flagged as complete. At this juncture, the AI performs a self-audit to extract critical insights—such as previously undocumented GAS constraints or reusable design patterns—and autonomously updates the Agent skill (&lt;code&gt;SKILL.md&lt;/code&gt;, helper scripts, and templates).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Recursive Evolution Loop&lt;/strong&gt;: Stages 1 through 6 are continuously cycled. As the &lt;code&gt;SKILL.md&lt;/code&gt; matures, it transitions from a set of generalized instructions to a highly specialized, project-aware expert system, enabling the agent to resolve increasingly complex architectural challenges with minimal human intervention.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pfmjjkl0lqycuyq88uq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pfmjjkl0lqycuyq88uq.jpg" alt="Workflow" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig 2a: The 7-stage operational workflow of Recursive Knowledge Crystallization.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Methodology: Environment Setup and Initialization
&lt;/h2&gt;

&lt;p&gt;In this article, it is assumed that Node.js, Google Antigravity, and Gemini CLI have already been installed. To establish the baseline for developing &lt;code&gt;gas-fakes&lt;/code&gt;, the repository is cloned as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/brucemcpherson/gas-fakes
&lt;span class="nb"&gt;cd &lt;/span&gt;gas-fakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this empirical study, Google Antigravity and Gemini CLI were utilized in tandem to evolve the agent skill. First, the initial agent skill was established. The directory structure is outlined below. To ensure both agent environments utilized the exact same skill base (&lt;code&gt;gas-fakes-dev&lt;/code&gt;), they were synchronized using a symbolic link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gas-fakes/
├── .agent/
│   └── skills/
│       └── gas-fakes-dev/
│           └── SKILL.md
└── .gemini/
    └── skills -&amp;gt; ../.agent/skills/  (symbolic link)
        └── gas-fakes-dev/
            └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, to avoid manual linking between the &lt;code&gt;.agents&lt;/code&gt; and &lt;code&gt;.gemini&lt;/code&gt; directories, the &lt;code&gt;gemini.md&lt;/code&gt; configuration file can be utilized to instruct the Gemini CLI to consistently reference the &lt;code&gt;.agents/workloads&lt;/code&gt; and &lt;code&gt;.agents/skills&lt;/code&gt; folders. This ensures seamless resource sharing between the CLI and Google Antigravity. Furthermore, including a directive to automatically activate the &lt;code&gt;gas-fakes-dev&lt;/code&gt; skill within this configuration streamlines the entire initialization process.&lt;/p&gt;

&lt;p&gt;The baseline &lt;code&gt;SKILL.md&lt;/code&gt; was formulated as a generic directive. During the development of &lt;code&gt;gas-fakes&lt;/code&gt;, this specific skill file served as the target for continuous evolution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
name: gas-fakes-dev
description: "Develop and implement the 'gas-fakes' project, emulating Google Apps Script (GAS) functionality using Node.js."
tags:[nodejs, google-apps-script, google-cloud-api, testing, mock]
version: "1.0.0"
---

## Summary
This skill enables the agent to assist in the development of the `gas-fakes` project. The primary objective is to emulate Google Apps Script (GAS) behavior using Node.js and Google APIs, allowing Apps Script code to run in a local Node.js environment.

## Usage
- When you need to implement fake/mock functionality for GAS classes or methods.
- When creating test scripts to verify the implemented functionalities.
- When assisting a human developer with Node.js and Google APIs integration.

## Workflow

### 1. Context and Specification Check
Before implementing, verify the specifications of the target Google Apps Script classes and methods. Ensure that the functionality you are about to build aligns with real GAS behavior.

### 2. Implementation
Generate Node.js code that maps GAS methods to the corresponding Google Cloud APIs.
- Refer to the existing codebase in the `src/` directory to understand the current architecture.
- Ensure the code runs correctly in the Node.js environment.

### 3. Testing
You must verify your implementation by writing and executing test scripts.
- Place your test scripts in the `test/` directory.
- Execute the tests to ensure there are no errors and the behavior matches expectations.

### 4. Holistic/Targeted Skill Evolution (Self-Updating SKILL)
**[CRITICAL INSTRUCTION]**
The `gas-fakes` project is complex, and bridging Node.js with GAS involves many hidden constraints, specific architectural patterns, and potential errors. You are required to continuously learn and autonomously evolve this SKILL.

- **Trigger:** Whenever you encounter an error during implementation or testing, or when you receive correction feedback/prompts from the human developer.
- **Action:** You MUST extract the lessons learned from the failure and recovery process. Identify the underlying rules, technical constraints, or coding patterns that caused the issue.
- **Update:** Immediately update this `SKILL.md` file (by adding, deleting, or modifying content) to document the newly acquired knowledge. If necessary, also create or update sample scripts, helper templates, or explanatory Markdown files in the project.
- **Goal:** Transform your localized, temporary learnings into permanent, universally readable knowledge to prevent repeating the same mistakes and to handle the complexities of the project autonomously.

## Delivery
- Output the complete code for modified or newly created service classes and test scripts.
- **ALWAYS output the updated `SKILL.md`** when new knowledge is extracted and crystallized.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Also, please install Model Context Protocol (MCP) servers for Google Workspace development. &lt;a href="https://developers.google.com/workspace/guides/developer-tools#mcp" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; This MCP server is used from this agent skill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a command &lt;code&gt;/gas-fakes-dev&lt;/code&gt; is put into the chat, this skill can be activated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;gas-fakes&lt;/code&gt; emulates Google Apps Script using Node.js, generated test scripts are strictly required to function flawlessly in both the local Node.js environment and the cloud-based Google Apps Script editor. Utilizing the initial, generic skill often resulted in scripts that passed locally but failed in the cloud environment. This discrepancy served as the primary catalyst for triggering the agent's evolutionary loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Crystallization of Knowledge: The Evolved Agent Skill
&lt;/h2&gt;

&lt;p&gt;After extensive trial and error implementing various GAS classes and methods, the agent dynamically restructured and expanded its own skill set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666xatrumohah5vj9h9y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666xatrumohah5vj9h9y.jpg" alt="Agent Skill Evolution Comparison" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The evolved repository structure incorporated new templates, examples, and scripts generated by the agent to support its new architectural insights. You can see the details of this agent skill from &lt;a href="https://github.com/tanaikech/agent-skill-for-developing-gas-fakes" rel="noopener noreferrer"&gt;https://github.com/tanaikech/agent-skill-for-developing-gas-fakes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gas-fakes/
├── .agent/
│   └── skills/
│       └── gas-fakes-dev/
│           ├── examples/
│           │   ├── batch_update_pattern.js
│           │   └── proxy_guard_pattern.js
│           ├── resources/
│           │   ├── class_template.js
│           │   ├── sync_mechanism.md
│           │   └── test_template.js
│           ├── scripts/
│           │   ├── run_target_test.js
│           │   └── scaffold_service.js
│           └── SKILL.md
└── .gemini/
    └── skills -&amp;gt; ../.agent/skills/  (symbolic link)
        └── gas-fakes-dev/
            ├── examples/
            ├── resources/
            ├── scripts/
            └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resultant, highly complex &lt;code&gt;SKILL.md&lt;/code&gt; (Version 2.0.0) evolved into a comprehensive system architecture document as follows. &lt;a href="https://github.com/tanaikech/agent-skill-for-developing-gas-fakes/blob/master/gas-fakes-dev/SKILL.md" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
name: gas-fakes-dev
description: Develop, implement, and test the 'gas-fakes' project, emulating ALL Google Apps Script (GAS) functionality using Node.js and Google APIs.
tags: [nodejs, google-apps-script, google-cloud-api, testing, mock, simulation]
version: "2.0.0"
---

## Summary
This skill enables the agent to act as a Senior Expert proficient in Node.js, Google Cloud, and Shell Scripting to assist in the development of the `gas-fakes` project.
The ultimate objective is to **emulate the behavior of live Google Apps Script exactly** by mapping classes and methods to their fake equivalents. This allows Apps Script to run anywhere Node runs.
This is achieved using a **worker mechanism** to handle the conversion from synchronous (Apps Script) to asynchronous (Google APIs) operations, returning the results synchronously. Ultimately, all Apps Script classes and methods will be available via `gas-fakes`. For a deep dive into this mechanism, see the [Sync-to-Async Bridge Guide](./resources/sync_mechanism.md).

## Usage
- When you need to implement or modify mock functionality for specific GAS classes or methods.
- When creating logic that reproduces GAS behavior using Google APIs and the worker mechanism.
- When adding new service classes to fit the existing `gas-fakes` project structure.
- When creating test scripts and registering them for local execution within the `test/` directory.
- When executing generated Apps Script files locally.
- When technical advice or debugging assistance based on GAS specifications is required.

## Configuration
`gas-fakes` relies on an `.env` file (maintained by `gas-fakes init` and `auth`) to properly authenticate and run.

### Authentication Methods
- **DWD (Domain Wide Delegation) - *Preferred***: Uses a service account to impersonate a user logged into gcloud. The service account name must be specified in the `.env` file. This is **required** if restricted/sensitive scopes are needed, or if running `gas-fakes` on Google Cloud Run.
- **ADC (Application Default Credentials)**: If no `.env` file is provided, `gas-fakes` falls back to ADC. This is usually fine for local development as standard `cloud_platform` and `drive` scopes are automatically assigned during auth.

Ensure your `.env` is properly loaded (e.g., using `node --env-file &amp;lt;env-file&amp;gt;` or `gas-fakes -e &amp;lt;env-file&amp;gt;`) to ensure DWD is used when necessary.
- **`TEST_SPREADSHEET_ID`**: (Optional) Spreadsheet ID for real-environment testing.

## Workflow

### 1. Context, Specification, and Dependency Check
Before starting implementation, you must understand the full scope and style of the project.

- **Restricted Directories**:
  * **NEVER modify any files located in the `progress` directory** during the development of `gas-fakes`. This directory is strictly off-limits.

- **Mandatory Existence Verification**:
  When adding or updating ANY class or method, you must **ALWAYS use the `workspace-developer` MCP server** (if available) or search official documentation to verify the existence and specification of the target and ALL related classes/methods.
  *   **NEVER create classes or methods that do not exist in the actual Google Apps Script environment.**
  *   Do not guess. Confirm exact names, parameter structures, and return types.
  *   *Example*: If implementing `Sheet.getCharts()`, check what object it returns (`EmbeddedChart` vs `Chart`) and verify the methods available on that returned object.

- **Strict Enum Verification**:
  **Do not invent Enums.** Before using or creating an Enum, verify if it actually exists in Google Apps Script.
  *   **Note on `ChartType`**: In Google Apps Script, `ChartType` is a property of the `Charts` service (`Charts.ChartType`), not `SpreadsheetApp`.

- **Reference Existing Code**:
  Check the `src` and `test` directories to understand the existing coding style and architecture. Apply these patterns to new classes and methods.

- **Identify Dependencies**:
  Identify and implement **all related classes and methods** required for the target feature. (e.g., if `newChart()` returns `EmbeddedChartBuilder`, verify/implement that builder class too).

### 2. Scaffolding
Do not create service files manually. Use the provided helper script.

- **Command**:
  \`\`\`bash
  node .agent/skills/gas-fakes-dev/scripts/scaffold_service.js --service=&amp;lt;ServiceName&amp;gt; --class=&amp;lt;ClassName&amp;gt;
  \`\`\`

### 3. Implementation
Generate code based on Node.js best practices, adhering to these rules to maintain environmental consistency with live Apps Script:

- **Comprehensive Implementation**: Implement the target feature and its dependencies together.
- **Cross-Environment Compatibility**: Scripts must be designed to run on both Node.js (local) and GAS Script Editor with identical results.
- `toString()` Accuracy: The `toString()` method must return the **exact GAS class name or specific string output** (e.g., `"Sheet"` or `"[Document:  ...]"`). Pay extreme attention to whitespace; GAS output can contain double spaces (e.g., after a colon).
- **Dynamic Resources and Caching Pattern**:
  *   **The `__resource` property**: Always access the underlying API state via the dynamic `__resource` getter (tracing back to the parent element or a synchronized cache).
  *   **Stale State Prevention**: NEVER store API resources directly in class instance variables. They will become stale when the cache is cleared.
  *   **Cache Invalidation**: Every time a destructive API call is made, the cache is cleared. Dynamically accessing `__resource` ensures you get the most up-to-date state from the API or the fresh cache.
- **Implementation Patterns**:
  1. Use `signatureArgs` or `is.*` utilities.
  2. Construct the request object.
  3. Execute via `this.__batchUpdate` (or helper).
  4. Return `this` for chaining.

- **Coding Patterns**:
  *   **Naming**: Internal implementation classes should be prefixed with `Fake` (e.g., `FakeSheet`, `FakeEmbeddedChart`).
  *   **Lazy Loading**: New top-level services must be registered using the `lazyLoaderApp` pattern in their respective `app.js` to ensure they are only instantiated when accessed.
  *   **Singleton Pattern**: Service entry points usually export a "maker" function (e.g., `newFakeSpreadsheetApp`) that return the singleton instance.

- **Specific Technical Nuances**:
  *   **Sync-to-Async Bridge**: Always use the worker bridge for external API calls to maintain synchronous Apps Script behavior. Follow the pattern: `Fake Class` -&amp;gt; `Syncit (fx*)` -&amp;gt; `callSync` -&amp;gt; `Worker Loop` -&amp;gt; `sx* Function` -&amp;gt; `sxRetry`. See [detailed guide](./resources/sync_mechanism.md) for implementation steps.
  *   **Charts**: When retrieving a chart's title, use `getOptions().get("title")` instead of internal specs.
  *   **EmbeddedChart**: To get the type of an existing chart, use `modify().getChartType()` instead of a non-existent `getType()` method.
  *   **GridRange to FakeRange Conversion**: When mapping an API `GridRange` to a `FakeSheetRange`:
    *   Indices in the API are 0-indexed and the end index is **exclusive**.
    *   GAS methods (like `getRange(row, col, numRows, numCols)`) use 1-indexed start positions and row/column counts.
    *   If `endRowIndex` or `endColumnIndex` is undefined in the API response, it means the range extends to the boundary of the sheet. Use `sheet.getMaxRows()` and `sheet.getMaxColumns()` as fallbacks to calculate `numRows` and `numCols`.
  *   **ContainerInfo**: This class is used by `EmbeddedChart`, `Slicer`, and `Drawing`.
    *   Map `getAnchorRow()` and `getAnchorColumn()` to 0-indexed API fields `anchorCell.rowIndex + 1` and `anchorCell.columnIndex + 1`.
    *   Map `getOffsetX()` and `getOffsetY()` directly to `offsetXPixels` and `offsetYPixels`.
  *   **XmlService**:
    *   `Element.getName()` returns the **local name** (no prefix).
    *   `Document.toString()` output is very specific: `[Document:  No DOCTYPE declaration, Root is [Element: &amp;lt;rootName/&amp;gt;]]` (note the double space after `Document:`).
    *   Namespace-aware methods (like `getChild(name, namespace)`) require mapping prefixes and URIs correctly.

### 4. Testing
You must ensure every implementation is verified by tests that precisely emulate Google Apps Script behavior.

- **Mandatory Feature Coverage**:
  - **Every new class or method MUST have a corresponding test.**
  - If you implement multiple methods, create a test script that exercises all of them.
  - Test only implemented features; do not include placeholders for future work.

- **Edge Case Capturing**:
  - **Capture and verify boundary conditions, invalid inputs, and error states.**
  - Use `t.rxMatch(t.threw(() =&amp;gt; ...).message, /regex/)` to verify that methods throw expected GAS error messages when given invalid arguments.
  - Test with `null`, `undefined`, empty strings, and out-of-bounds values where applicable to ensure robust emulation.

- **GAS Compatibility Requirement**:
  - Test logic (inside `unit.section`) must be compatible with the **Google Apps Script script editor**.
  - Avoid Node.js-specific modules (e.g., `fs`, `path`) inside the test logic.
  - Use `ScriptApp.isFake` or `xxxApp.isFake` to toggle environment-specific logging or assertions.

- **Test Script Structure**:
  - **Imports**: standard test scripts import `@mcpher/gas-fakes`, `@sindresorhus/is`, and helpers from `./testinit.js` and `./testassist.js`.
  - **Export Pattern**: Export a named function (e.g., `export const testService = (pack) =&amp;gt; { ... }`) to allow integration into the main test suite.
  - **Execution Hook**: Always include `wrapupTest(testService);` at the end of the file for standalone execution.
  - **Sections**: Use `unit.section("description", (t) =&amp;gt; { ... })` to group tests.

- **Resource Lifecycle and Cleanup**:
  - **The `toTrash` pattern**: Maintain a `toTrash` array within your test function. Push any created resources (files, folders, sheets) into this array.
  - **Automatic Cleanup**: Use `trasher(toTrash)` at the end of the test function (usually triggered if `fixes.CLEAN` is true) to ensure the test environment remains pristine.

- **Naming Convention**:
  - **Google Sheets**: `testsheets{class name}.js` (e.g., `testsheetsrange.js`)
  - **Google Docs**: `testdocs{class name}.js`
  - **Google Slides**: `testslides{class name}.js`
  - **General/Other**: `test{service}{class name}.js`

- **Registration**:
  1. Create the file in `test/`.
  2. Add to `test/test.js`.
  3. Add a script entry in `test/package.json`.

- **Execution**:
  &amp;gt; [!IMPORTANT]
  &amp;gt; **Run tests from the `test/` directory.**
  &amp;gt; `cd test &amp;amp;&amp;amp; node test{filename}.js execute` (or `npm run &amp;lt;script-name&amp;gt;`).

- **Clasp Verification**:
  Use `testongas/test/` to verify against a real GAS project via `clasp`.

### 5. Executing Generated Scripts
You can execute any generated Apps Script file using `gas-fakes` in a local sandbox.

- **Testing the Local Branch (Preferred for Dev)**:
  Use the local `.env` file and run the local `gas-fakes` implementation.
  \`\`\`bash
  node --env-file ./.env gas-fakes -f myscript.js
  \`\`\`
- **Testing Global Installation**:
  \`\`\`bash
  npx gas-fakes -f myscript.js
  \`\`\`
  *(Sandbox flags can be added as requested by the user to control the environment)*

### 6. Refinement &amp;amp; Continuous Evolution (Self-Updating SKILL)
To ensure the `gas-fakes` project and this SKILL continuously evolve and improve efficiently, you **MUST** actively refine and update the SKILL definition (`SKILL.md`) and its associated resources based on the outcomes of your development processes.

#### 6.1 Skill Audit Criteria
At the conclusion of every task, perform a "Self-Audit" by answering:
1. **New Pattern?** Did I implement a new mapping logic (e.g., API-to-GAS index shifts) or a reusable architectural pattern?
2. **Missing Nuance?** Did I encounter a technical detail (like a specific `toString()` format or Enum location) that wasn't documented?
3. **Corrected Assumption?** Did a test failure or user hint reveal that a rule in this SKILL was incomplete or incorrect?
4. **New Service?** Did I add a new class that requires a dedicated section in the "Technical Nuances" list?

#### 6.2 Prompt for Update Mandate
If any criteria in the "Skill Audit" are met, you MUST:
- **Analyze the New Knowledge**: Determine what core knowledge, rule, constraint, or pattern was derived from the success or resolution.
- **Identify the Change**: Specifically state what needs to be added, modified, or deleted in `SKILL.md`.
- **Propose the Edit**: Present the specific Markdown block intended for the update.
- **Ask for Confirmation**: Explicitly ask the user: *"Based on this task, I recommend updating the SKILL.md with the following [nuance/pattern]. Should I apply this change?"*
- **Update Associated Assets**: If the new knowledge involves a reusable code structure or boilerplate, add or update files in the `examples/`, `resources/`, or `scripts/` directories to reflect the new best practice.

### 7. Delivery
- **Output**: Full content of modified or newly created files (Service class, Node.js test, etc.).
- **Skill Audit**: Provide a brief (1-2 sentence) summary of your Self-Audit results.
- **Update Prompt**: If any audit criteria were met, issue the "Prompt for Update" as defined in section 6.2.
- **Finality**: Conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Milestones in the Agent's Evolution:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role and Expertise Reclassification&lt;/strong&gt;: The agent's persona autonomously shifted from a general "assistant" to a "Senior Expert" with specific cross-domain mastery in Node.js, Google Cloud, and Shell Scripting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Specification (Sync-to-Async Bridge)&lt;/strong&gt;: The evolved skill introduced a sophisticated "worker mechanism" and "Syncit" pattern. This addressed the core technical challenge of mapping asynchronous Node.js/Google APIs to the synchronous execution model of Google Apps Script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Verification &amp;amp; Grounding&lt;/strong&gt;: New mandates required the use of the &lt;code&gt;workspace-developer&lt;/code&gt; MCP server and official documentation. This systematically eliminated "hallucinated" methods, ensuring absolute 1:1 parity with the live GAS environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Nuance Crystallization&lt;/strong&gt;: Highly specific, undocumented "gotchas" discovered during the failure/recovery loops were codified. This included 0-indexed vs. 1-indexed coordinate conversions, exact string matching for &lt;code&gt;toString()&lt;/code&gt; (including hidden whitespace), and specific Enum locations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Evolution Logic (Crystallization Loop)&lt;/strong&gt;: The trigger for skill updates shifted from a reactive "whenever an error occurs" (v1) to a proactive, structured "Self-Audit" protocol (v2). The agent learned to analyze patterns, propose edits to the human developer, and automatically update associated boilerplate assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Empirical Evaluation: Cross-Environment Knowledge Transfer
&lt;/h2&gt;

&lt;p&gt;To validate the efficacy of the RKC framework, practical development tasks were executed utilizing the actively evolving agent skill across two distinct interfaces: Google Antigravity and the Gemini CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Evaluation in Google Antigravity
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Sample 1: Architectural Refinement via Self-Audit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt1:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `insertTextBox(String,Number,Number,Number,Number)` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prompt2:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `insertTable(Table)` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zq0e54zefb67j402oio.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zq0e54zefb67j402oio.jpg" alt="Result on the agent manager" width="800" height="1962"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3a: Agent executing implementation and autonomously applying the Dynamic Resources Pattern.&lt;/p&gt;

&lt;p&gt;To evaluate continuous learning, we tasked the agent with implementing &lt;code&gt;insertTextBox&lt;/code&gt; and &lt;code&gt;insertTable&lt;/code&gt;. The task required creating underlying architectural dependencies, such as &lt;code&gt;FakeTable&lt;/code&gt;, &lt;code&gt;FakeTableRow&lt;/code&gt;, and &lt;code&gt;FakeTableCell&lt;/code&gt;. Initially, the agent generated functional code. However, as shown in &lt;strong&gt;Figure 3a&lt;/strong&gt;, the true strength of the framework was demonstrated during the self-evolution phase. The agent autonomously identified a potential flaw regarding resource management. It proactively refactored the classes to adhere to a newly conceptualized "Dynamic Resources Pattern," replacing static constructor assignments with dynamic getters to ensure robust state synchronization. Crucially, the agent updated &lt;code&gt;SKILL.md&lt;/code&gt; with this rule, ensuring future compliance. Following this refinement, all 23 test cases passed with 100% accuracy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 2: Internalization of Design Philosophy
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `getTables()` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
Add the method `getShapes()` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3njy8kyujb1bgq4lxckf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3njy8kyujb1bgq4lxckf.jpg" alt="Result on the agent manager" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3b: Agent applying functional programming patterns to dynamically cast page elements.&lt;/p&gt;

&lt;p&gt;In this experiment (see &lt;strong&gt;Figure 3b&lt;/strong&gt;), the agent demonstrated a deep internalization of the project's design philosophy. Instead of implementing redundant storage logic for arrays of specific shapes, it autonomously utilized the generic &lt;code&gt;getPageElements()&lt;/code&gt; method, applying &lt;code&gt;.filter()&lt;/code&gt; and &lt;code&gt;.map()&lt;/code&gt; to dynamically cast elements. This approach perfectly adhered to the "Dynamic Resources Pattern" crystallized in the previous task. All 28 assertions in the subsequent test suite passed flawlessly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 3: Bridging Environmental Discrepancies
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt 1 &amp;amp; 2 (Summarized):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a Class XmlService and a method `parse`...
When `test/testxmlservice.js` is run with the script editor of Google Apps Script, the error occurred. Update the scripts.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswvyi9fvkh3pgv9h4dxl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswvyi9fvkh3pgv9h4dxl.jpg" alt="Result on the agent manager" width="800" height="1486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3c: Agent resolving GAS-specific formatting rules through remote feedback.&lt;/p&gt;

&lt;p&gt;This task required bridging the gap between Node.js simulation and actual GAS behavior for XML parsing. While the initial code passed local Node.js tests, execution in the GAS editor revealed critical discrepancies, such as unique double-space formatting in &lt;code&gt;toString()&lt;/code&gt; outputs (&lt;code&gt;[Document:  No DOCTYPE...]&lt;/code&gt;) and precise namespace handling. Upon receiving this remote error feedback (see &lt;strong&gt;Figure 3c&lt;/strong&gt;), the agent performed a systemic refactoring. Following the RKC framework, it updated the &lt;code&gt;SKILL.md&lt;/code&gt;, formally persisting these "hidden" GAS-specific formatting rules as permanent constraints.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 4: Achieving Environment-Aware Precision
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompts 1, 2 &amp;amp; 3 (Summarized):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the `getPrettyFormat` and `getRawFormat` methods to the `XmlService` class.
Resolve issues regarding \r\n line breaks appearing in raw format tests on GAS...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c2qap6klmzkp3vytmqd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c2qap6klmzkp3vytmqd.jpg" alt="Result on the agent manager" width="800" height="1441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3d: Agent crystallizing nuanced serialization behaviors into persistent memory.&lt;/p&gt;

&lt;p&gt;This experiment focused on achieving high-fidelity parity with GAS’s unique XML serialization. Through iterative feedback, the agent discovered that GAS consistently injects a line separator (&lt;code&gt;\r\n&lt;/code&gt;) after the XML declaration and at the document's end, even in "raw" format. As captured in &lt;strong&gt;Figure 3d&lt;/strong&gt;, the agent refactored the &lt;code&gt;FakeFormat&lt;/code&gt; class and crystallized these insights into &lt;code&gt;SKILL.md&lt;/code&gt;. Final verification yielded a 100% success rate across 21 test cases, proving the framework's capacity for "Environment-Aware Precision."&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Zero-Shot Knowledge Transfer via Gemini CLI
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Sample 1: Cross-Platform Environment Porting
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement the following methods for the `ContainerInfo` class within the `EmbeddedChart` class hierarchy of Google Sheets: `getAnchorColumn()`, `getAnchorRow()`, `getOffsetX()`, and `getOffsetY()`.

Please follow these operational guidelines:
1. **Skill Declaration**: Explicitly state the agent skills or tools being utilized before proceeding.
2. **Task Execution &amp;amp; Summary**: Provide the implemented code/definitions and conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3e.jpg" alt="Result on the Gemini CLI" width="800" height="4067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3e: Gemini CLI agent achieving Zero-Shot Knowledge Transfer utilizing the shared SKILL.md.&lt;/p&gt;

&lt;p&gt;This experiment validated the "Zero-Shot Knowledge Transfer" capability by transitioning the execution environment entirely from Google Antigravity to the Gemini CLI. By referencing the highly evolved &lt;code&gt;SKILL.md&lt;/code&gt; persisted on the local file system, the CLI-based agent immediately understood the complex architectural patterns (see &lt;strong&gt;Figure 3e&lt;/strong&gt;). It autonomously implemented &lt;code&gt;FakeContainerInfo&lt;/code&gt; using the project-specific &lt;code&gt;Proxies.guard&lt;/code&gt; pattern. The generated code strictly followed naming conventions and correctly converted 0-based API indices to 1-based GAS indices on its very first attempt, passing all 11 tests with 100% accuracy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 2: Agents as Architectural Contributors
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement the `getRanges()` method for the `EmbeddedChart` class in the Google Sheets service.

Please follow these operational guidelines:
1. **Skill Declaration**: Explicitly state the agent skills or tools being utilized before proceeding.
2. **Task Execution &amp;amp; Summary**: Provide the implemented code/definitions and conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3f.jpg" alt="Result on the Gemini CLI" width="800" height="2317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3f: Gemini CLI agent performing a Self-Audit and proposing architectural updates.&lt;/p&gt;

&lt;p&gt;Focusing on the &lt;code&gt;getRanges()&lt;/code&gt; method, the CLI agent transformed nested API &lt;code&gt;GridRange&lt;/code&gt; structures into GAS &lt;code&gt;Range&lt;/code&gt; instances. It elegantly handled discrepancies between exclusive 0-based API bounds and inclusive 1-based GAS boundaries. Most significantly, as shown in &lt;strong&gt;Figure 3f&lt;/strong&gt;, the agent autonomously triggered its "Skill Audit" protocol upon completion. It identified a recurring logic pattern for &lt;code&gt;GridRange&lt;/code&gt; conversions and proactively recommended a structural update to the &lt;code&gt;SKILL.md&lt;/code&gt;. This illustrates that the RKC framework enables agents to transcend basic execution, allowing them to act as proactive "Architectural Contributors."&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;Through the empirical development of the &lt;code&gt;gas-fakes&lt;/code&gt; emulation library, this research validates the profound efficacy of the Recursive Knowledge Crystallization (RKC) framework. Key findings include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Physical Knowledge Persistence&lt;/strong&gt;: By crystallizing an agent's evolving technical expertise into universally readable, local Markdown files (&lt;code&gt;SKILL.md&lt;/code&gt;), the framework successfully mitigates "Catastrophic Forgetting" and establishes an auditable, persistent memory layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recursive Evolution Loop&lt;/strong&gt;: The implementation of an autonomous "Self-Audit" protocol enabled the agent to systematically extract architectural insights from failure/recovery cycles, thereby refining its own operational directives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-Aware Precision&lt;/strong&gt;: The agent successfully codified highly obscure, undocumented system constraints (e.g., hidden string formatting and complex coordinate mapping logic) directly into its skill set, guaranteeing high-fidelity behavioral emulation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Shot Knowledge Transfer&lt;/strong&gt;: The study empirically proved that evolved, highly specialized skills can be seamlessly and instantaneously ported across entirely distinct environments (from Google Antigravity to Gemini CLI), enabling the generation of structurally perfect code on the first attempt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-AI Collaboration&lt;/strong&gt;: The framework realizes a novel development paradigm where the AI agent operates not merely as a localized code generator, but as an active, persistent "Architectural Contributor" capable of scaling and maintaining deeply constrained codebases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;It should be noted that the "Agent skill" development methodology and the self-evolution process introduced in this paper do not necessarily represent a state where evolution has reached full saturation. As AI technology and its surrounding ecosystems continue to advance, there remains significant untapped potential in the structures through which agents define themselves and "crystallize" their knowledge.&lt;/p&gt;

&lt;p&gt;By further pushing the boundaries of this development framework, we anticipate that even greater evolutionary leaps will occur. Such progress will likely lead to even higher levels of development efficiency, transcending current expectations and moving toward a more sophisticated phase of software automation and human-agent synergy.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Mastering Google Apps Script CI/CD: Seamless GitHub Actions Integration with gas-fakes</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 25 Mar 2026 07:20:29 +0000</pubDate>
      <link>https://dev.to/gde/mastering-google-apps-script-cicd-seamless-github-actions-integration-with-gas-fakes-4c6h</link>
      <guid>https://dev.to/gde/mastering-google-apps-script-cicd-seamless-github-actions-integration-with-gas-fakes-4c6h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzl38sbb8u84h7ogj6lc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzl38sbb8u84h7ogj6lc.jpg" alt="fig1a" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Discover how to seamlessly integrate Google Workspace with GitHub Actions using the gas-fakes library. This guide demonstrates running Google Apps Script locally and within CI/CD pipelines without deploying Web Apps. Automate workflows, secure credentials, and effortlessly interact with Google Drive and Sheets directly from your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Google Apps Script (GAS) is a powerful low-code platform that enables developers to integrate, automate, and extend Google Workspace with ease. &lt;a href="https://workspace.google.com/products/apps-script/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Typically, executing GAS requires the script to be hosted on Google's servers via the Script Editor. While tools like &lt;code&gt;clasp&lt;/code&gt; allow for local development and synchronization, running scripts from outside the Google ecosystem—such as from a local environment or a different cloud provider—often involves complex setups relying heavily on the Apps Script API or Web Apps. &lt;a href="https://github.com/google/clasp" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A common approach to triggering GAS from GitHub Actions is using Web Apps. However, this method presents several hurdles. Developers must re-deploy the Web App every time the script is updated, and verifying the latest code logic directly within the repository can be cumbersome. This is where &lt;code&gt;gas-fakes&lt;/code&gt; becomes a game-changer. &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Operating as a robust emulation layer, &lt;code&gt;gas-fakes&lt;/code&gt; allows GAS projects to run on Node.js as if they were native, enabling seamless execution across various environments.&lt;/p&gt;

&lt;p&gt;By leveraging &lt;code&gt;gas-fakes&lt;/code&gt; within GitHub Actions, you can manage, test, and update your scripts directly inside your repository without the need for constant re-deployment. Furthermore, by storing sensitive credentials in GitHub's "Secrets and variables," you can ensure a high level of security for your automation workflows. This synergy between GitHub Actions and GAS opens up infinite possibilities for CI/CD application development. In this article, I will introduce a streamlined, professional method for managing Google Workspace using GitHub Actions and &lt;code&gt;gas-fakes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd7q371dzq9m5u49sp9d0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd7q371dzq9m5u49sp9d0.jpg" alt="fig1b" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Setup and Authentication
&lt;/h2&gt;

&lt;p&gt;To establish this pipeline, we first need to extract the proper authorization credentials and configure our GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Obtain Authorization Data
&lt;/h3&gt;

&lt;p&gt;In order to authorize &lt;code&gt;gas-fakes&lt;/code&gt;, you must first generate Application Default Credentials (ADC) on your local machine. The Google Cloud CLI (&lt;code&gt;gcloud&lt;/code&gt;) is required for this step. Detailed installation instructions can be found in the &lt;a href="https://github.com/brucemcpherson/gas-fakes/blob/main/gas-fakes-cli.md#getting-started" rel="noopener noreferrer"&gt;gas-fakes official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you prefer using &lt;code&gt;npx&lt;/code&gt;, execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @mcpher/gas-fakes init &lt;span class="nt"&gt;--auth-type&lt;/span&gt; adc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @mcpher/gas-fakes auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if you want to install the package globally via &lt;code&gt;npm&lt;/code&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @mcpher/gas-fakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gas-fakes init &lt;span class="nt"&gt;--auth-type&lt;/span&gt; adc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gas-fakes auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the authorization is complete, retrieve your credentials file.&lt;/p&gt;

&lt;p&gt;For Linux / macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.config/gcloud/application_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;type &lt;/span&gt;C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\{&lt;/span&gt;user name&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;ppData&lt;span class="se"&gt;\R&lt;/span&gt;oaming&lt;span class="se"&gt;\g&lt;/span&gt;cloud&lt;span class="se"&gt;\a&lt;/span&gt;pplication_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output. Your authorization data will look similar to the following JSON structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client secret}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quota_project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your project ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your refresh token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"universe_domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"googleapis.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a New GitHub Repository
&lt;/h3&gt;

&lt;p&gt;To test this integration, create a new repository on GitHub. For this guide, we will assume the repository name is &lt;code&gt;sample&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;We must securely store the authorization data in GitHub. Navigate to your repository's "Settings" -&amp;gt; "Secrets and variables" -&amp;gt; "Actions". Click on "New repository secret" and configure the following:&lt;/p&gt;

&lt;p&gt;Key: &lt;code&gt;GCP_ADC_USER_JSON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Value: (Paste the JSON string you copied in Step 1)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client secret}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quota_project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your project ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your refresh token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"universe_domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"googleapis.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the &lt;code&gt;gh&lt;/code&gt; command is used, you can also use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh secret &lt;span class="nb"&gt;set &lt;/span&gt;GCP_ADC_USER_JSON &amp;lt; ~/.config/gcloud/application_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Phase 2: Building Your First Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 4: Define the GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;Create a new YAML file named &lt;code&gt;sample1.yml&lt;/code&gt; inside the &lt;code&gt;.github/workflows/&lt;/code&gt; directory of your repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample1)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample1&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create ADC file&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;
          &lt;span class="s"&gt;npx @mcpher/gas-fakes -s "const rootFolder = DriveApp.getRootFolder(); const rootFolderName = rootFolder.getName(); console.log(rootFolderName);"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this initial test, a simple Google Apps Script snippet (&lt;code&gt;const rootFolder = DriveApp.getRootFolder();...&lt;/code&gt;) is passed directly as an inline string within the YAML file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Push to the Remote Repository
&lt;/h3&gt;

&lt;p&gt;Commit and push your local repository to GitHub. This action will automatically trigger the GitHub Actions workflow. You can monitor the execution under the "Actions" tab. If successful, the step "Run Google Apps Script by gas-fakes" will output logs similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...using env file in /home/runner/work/github-action-for-gas-fakes/github-action-for-gas-fakes/.env
...gas-fakes version 2.2.7[Worker] ...authorized backends: google via ADC (###@gmail.com)[Worker] ...using scriptId: ### (source: random)
マイドライブ
...terminating worker thread
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeing your Google Drive's root folder name in the logs confirms that &lt;code&gt;gas-fakes&lt;/code&gt; successfully authenticated and executed the GAS code natively via GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Advanced Practical Applications
&lt;/h2&gt;

&lt;p&gt;To demonstrate the full potential of this architecture, let's explore two practical CI/CD use cases: recording execution logs to Google Sheets and uploading repository diffs to Google Drive.&lt;/p&gt;

&lt;p&gt;When this phase is finished, the directory structure of this repository becomes as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixi352ckitrhaffacet0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixi352ckitrhaffacet0.jpg" alt="fig2a" width="470" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please create the following &lt;code&gt;package.json&lt;/code&gt; and put into the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gas-fakes-workflow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Google Apps Script execution on GitHub Actions with gas-fakes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sources/sample2.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sample2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node sources/sample2.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sample3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node sources/sample3.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@mcpher/gas-fakes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.3.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=24"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, in order to create &lt;code&gt;package-lock.json&lt;/code&gt;, please run &lt;code&gt;npm install&lt;/code&gt; at the local side.&lt;/p&gt;

&lt;p&gt;As another file, please create &lt;code&gt;appsscript.json&lt;/code&gt; in the root directory as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeZone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exceptionLogging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STACKDRIVER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"V8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oauthScopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/drive"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Case 1: Record Execution Logs to Google Sheets
&lt;/h3&gt;

&lt;p&gt;In this scenario, whenever a push, pull request, issue event, or manual trigger occurs, the workflow stores detailed execution logs in a Google Spreadsheet named &lt;code&gt;sample spreadsheet for gas-fakes sample&lt;/code&gt;. If the file doesn't exist, it is automatically created in the root folder of your Google Drive.&lt;/p&gt;

&lt;p&gt;Add the following YAML file to &lt;code&gt;.github/workflows/sample2.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample2)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample2&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create ADC file&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;
          &lt;span class="s"&gt;npm run sample2&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_EVENT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.actor&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_SHA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.sha&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_REF&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.ref_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_COMMIT_MSG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.title&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.head_commit.message&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.pull_request.title&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'Manual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Run'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_RUN_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.server_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/actions/runs/${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.run_id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_RUN_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.run_number&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.number&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_ACTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.html_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new directory named &lt;code&gt;sources&lt;/code&gt; and add the corresponding Google Apps Script file as &lt;code&gt;sources/sample2.js&lt;/code&gt;. This script reads the environment variables passed by GitHub Actions and appends them to the spreadsheet.&lt;/p&gt;

&lt;p&gt;In order to use gas-fakes in a cache, &lt;code&gt;import "@mcpher/gas-fakes"&lt;/code&gt; was used in the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mcpher/gas-fakes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Log GitHub Actions execution details to Google Sheets
 * Updated to include Issue details
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logExecution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ja-JP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_EVENT_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_ACTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;issueNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ACTOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_REPO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_REF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_RUN_NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_COMMIT_MSG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_SHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_RUN_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Message/Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Repository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Commit SHA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Workflow URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample spreadsheet for gas-fakes sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFilesByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastRow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`Log updated for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (Event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Run #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;logExecution&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Case 2: Sync Repository Diffs to Google Drive
&lt;/h3&gt;

&lt;p&gt;This workflow dynamically generates a &lt;code&gt;.patch&lt;/code&gt; file containing code differences (or issue content) based on the triggered event, saving the result directly into a Google Drive folder named &lt;code&gt;GitHub Sync - Repo Diffs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/sample3.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample3)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sync-to-drive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample3&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js Environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure Google Cloud Credentials&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if [ "${{ github.event_name }}" == "pull_request" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(git diff origin/${{ github.base_ref }}...HEAD | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;elif [ "${{ github.event_name }}" == "push" ] &amp;amp;&amp;amp; [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(git diff ${{ github.event.before }}..${{ github.sha }} | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;elif [ "${{ github.event_name }}" == "issues" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;ISSUE_CONTENT="Issue Event: ${{ github.event.action }}\nTitle: ${{ github.event.issue.title }}\n\n${{ github.event.issue.body }}"&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(echo -e "$ISSUE_CONTENT" | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(echo "Manual trigger: No diff." | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;export REPO_DIFF&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;

          &lt;span class="s"&gt;npm run sample3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the corresponding script at &lt;code&gt;sources/sample3.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mcpher/gas-fakes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Syncs the repository-wide diff to a text file in Google Drive.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncRepoDiff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64Diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPO_DIFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;base64Diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No diff content found in environment variable REPO_DIFF.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;:.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_EVENT_NAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`diff_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.patch`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GitHub Sync - Repo Diffs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base64Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base64Diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFoldersByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;folderName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;folderName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successfully synced repository diff.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Saved as: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`View File: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUrl&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error during Drive sync: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;syncRepoDiff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Execution and Verification
&lt;/h2&gt;

&lt;p&gt;When a "push" event is triggered on the repository, the configured workflows execute concurrently. The visual output below confirms that GitHub Actions handled the integration flawlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1foorvqm4gq7yrja0zbk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1foorvqm4gq7yrja0zbk.jpg" alt="fig2b" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Executing the &lt;code&gt;sample2&lt;/code&gt; workflow processes the event payload and successfully appends a structured log directly into our Google Spreadsheet, functioning perfectly as an automated audit trail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf8yv9k01y783mqupb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf8yv9k01y783mqupb.jpg" alt="fig2c" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Executing the &lt;code&gt;sample3&lt;/code&gt; workflow correctly calculates the Git diff and uploads it to the designated Google Drive folder as a standard &lt;code&gt;.patch&lt;/code&gt; file. Below is an example of the resulting text file contents generated in Google Drive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/README.md b/README.md
index d63c7d8..19f6f9d 100644
--- a/README.md
+++ b/README.md
@@ -246,3 +246,4 @@ jobs:

sample
sample
+   sample text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These results clearly demonstrate that Google Workspace services can be seamlessly managed and extended utilizing Google Apps Script and &lt;code&gt;gas-fakes&lt;/code&gt; within a GitHub Actions CI/CD environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring Further Applications
&lt;/h2&gt;

&lt;p&gt;The integration of Google Apps Script and GitHub Actions via &lt;code&gt;gas-fakes&lt;/code&gt; is not limited to logging and file syncing. This foundation unlocks numerous advanced automation possibilities for enterprise environments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Unit Testing&lt;/strong&gt;&lt;br&gt;
Traditionally, testing Google Apps Script code is a manual and tedious process. By utilizing &lt;code&gt;gas-fakes&lt;/code&gt;, you can integrate standard JavaScript testing frameworks like Jest or Mocha. GitHub Actions can automatically run these test suites against your GAS functions on every pull request, ensuring robust code quality and preventing regressions before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Document Generation&lt;/strong&gt;&lt;br&gt;
You can trigger Google Workspace document creation based on repository events. For instance, when a new GitHub Release is published, a workflow can use Google Docs or Google Slides services via &lt;code&gt;gas-fakes&lt;/code&gt; to automatically compile release notes, insert relevant code snippets, and generate presentation slides for stakeholders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workspace Infrastructure as Code (IaC)&lt;/strong&gt;&lt;br&gt;
By storing organizational configurations—such as user directory structures, group memberships, or shared drive permissions—as YAML or JSON files in your repository, you can manage Google Workspace like infrastructure. Upon merging changes to the main branch, a workflow can execute a Google Apps Script that interfaces with the Admin Directory API to automatically synchronize the real-world Workspace environment with your repository's state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion: Evaluating the CI/CD Architecture
&lt;/h2&gt;

&lt;p&gt;Integrating &lt;code&gt;gas-fakes&lt;/code&gt; into your deployment pipeline shifts the paradigm of Google Workspace development. However, to maximize its potential, developers must weigh several technical considerations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gas-fakes vs. Traditional clasp Workflows&lt;/strong&gt;&lt;br&gt;
Traditionally, developers rely on Google's &lt;code&gt;clasp&lt;/code&gt; tool to synchronize local code with the Apps Script server. While effective for deployment, &lt;code&gt;clasp&lt;/code&gt; cannot natively execute code locally. Testing requires pushing to Google’s servers and executing manually or via triggers. In contrast, &lt;code&gt;gas-fakes&lt;/code&gt; provides a robust Node.js emulation layer. This enables instant execution, seamless integration with standard testing frameworks, and automated triggers directly from GitHub Actions without maintaining intermediate Web Apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strategic Importance of CI/CD in Workspace Development&lt;/strong&gt;&lt;br&gt;
Implementing CI/CD for Google Workspace is a major leap in enterprise security and maintainability. By shifting execution into GitHub Actions, sensitive credentials are removed from hardcoded scripts and securely managed via GitHub Secrets. Furthermore, every automation execution is firmly tied to a specific Git commit and workflow run, ensuring an immutable, highly auditable trail of operations that is essential for modern development standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;In this article, we focused on using &lt;strong&gt;Application Default Credentials (ADC)&lt;/strong&gt; for &lt;code&gt;gas-fakes&lt;/code&gt; authorization because of its straightforward setup for individual developers.&lt;/p&gt;

&lt;p&gt;For more advanced or enterprise-scale implementations, consider the following alternatives and best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain-Wide Delegation (DWD):&lt;/strong&gt;
For organizational workflows, &lt;code&gt;gas-fakes&lt;/code&gt; supports Service Accounts with Domain-Wide Delegation. This allows automation to perform actions on behalf of specific users (impersonation) within a Google Workspace domain without relying on personal refresh tokens. You can find instructions on using this GitHub Action with a service account in &lt;strong&gt;&lt;a href="https://github.com/brucemcpherson/gas-fakes-containers/blob/main/github-actions/README.md" rel="noopener noreferrer"&gt;Bruce's repository&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow Optimization:&lt;/strong&gt;
When using &lt;code&gt;actions/setup-node&lt;/code&gt; with &lt;code&gt;cache: "npm"&lt;/code&gt;, ensure your &lt;code&gt;package-lock.json&lt;/code&gt; is committed to the repository root. If your project structure is nested, specify the &lt;code&gt;cache-dependency-path&lt;/code&gt; to prevent "lock file not found" errors during CI/CD runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Best Practices:&lt;/strong&gt;
While these samples use a temporary file (&lt;code&gt;/tmp/adc.json&lt;/code&gt;) for authentication, always ensure that sensitive credential files are never uploaded as artifacts or printed in workflow logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Integrates Google Apps Script into modern GitHub CI/CD pipelines using the gas-fakes library.&lt;/li&gt;
&lt;li&gt;Enables local execution of GAS code without the need for constant Web App deployments.&lt;/li&gt;
&lt;li&gt;Securely manages authentication using Google Application Default Credentials (ADC) and GitHub Secrets.&lt;/li&gt;
&lt;li&gt;Demonstrates practical automation cases including automated logging and repository-to-Drive synchronization.&lt;/li&gt;
&lt;li&gt;Provides a foundation for advanced unit testing and Infrastructure as Code (IaC) within Google Workspace.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Recursive Knowledge Crystallization: A Framework for Persistent Autonomous Agent Self-Evolution</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 21 Feb 2026 07:37:04 +0000</pubDate>
      <link>https://dev.to/gde/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-4mk4</link>
      <guid>https://dev.to/gde/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-4mk4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bjnwsv2znyoogvh0fh7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bjnwsv2znyoogvh0fh7.jpg" alt="fig1a" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;In the development of autonomous agents using Large Language Models (LLMs), restrictions such as context window limits and session fragmentation pose significant barriers to the long-term accumulation of knowledge. This study proposes a "self-evolving framework" where an agent continuously records and refines its operational guidelines and technical knowledge—referred to as its SKILL—directly onto a local filesystem in a universally readable format (Markdown). By conducting experiments across two distinct environments featuring opaque constraints and complex legacy server rules using Google's Antigravity and Gemini CLI, we demonstrate the efficacy of this framework. Our findings reveal that the agent effectively evolves its SKILL through iterative cycles of trial and error, ultimately saturating its learning. Furthermore, by transferring this evolved SKILL to a completely clean environment, we verify that the agent can successfully implement complete, flawless client applications in a single attempt (zero-shot generation). This methodology not only circumvents the limitations of short-term memory dependency but also pioneers a new paradigm for cross-environment knowledge portability and automated system analysis.&lt;/p&gt;

&lt;p&gt;The infographic of this article is as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnrfhpb2y6wmjl63qj3m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnrfhpb2y6wmjl63qj3m.jpg" alt="fig1d" width="800" height="1286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;One of the primary challenges in the deployment of autonomous AI agents is the persistence of learning. Traditionally, when an agent's execution environment is reset or its context window is cleared, it is forced to relearn task specifications from scratch.&lt;/p&gt;

&lt;p&gt;In the realm of autonomous agents, enabling long-term memory and self-correction has been a focal point of recent research. Notable frameworks such as &lt;a href="https://arxiv.org/abs/2303.11366" rel="noopener noreferrer"&gt;Reflexion (Shinn et al., 2023)&lt;/a&gt; endow agents with dynamic memory to self-reflect and refine reasoning through trial and error. Similarly, &lt;a href="https://arxiv.org/abs/2305.16291" rel="noopener noreferrer"&gt;Voyager (Wang et al., 2023)&lt;/a&gt; introduces an open-ended embodied agent in Minecraft that utilizes an ever-growing executable code skill library for lifelong learning. Another significant approach is &lt;a href="https://arxiv.org/abs/2310.08560" rel="noopener noreferrer"&gt;MemGPT (Packer et al., 2023)&lt;/a&gt;, which treats LLMs as operating systems, employing hierarchical memory management to bypass context window limitations.&lt;/p&gt;

&lt;p&gt;However, these existing methodologies predominantly rely on in-memory contexts, specialized vector databases, or internal virtual memory abstractions. This makes the acquired knowledge difficult to decouple from the specific agent instance and challenging for human developers to read, audit, or directly manage.&lt;/p&gt;

&lt;p&gt;In contrast, our proposed framework introduces a paradigm shift: the continuous self-rewriting of an agent's "SKILL" residing entirely on a local filesystem (&lt;code&gt;SKILL.md&lt;/code&gt;). By utilizing standard development toolchains (Antigravity and Gemini CLI), the agent investigates unknown environments and automatically maintains its own operational manual. This approach offers a distinct advantage—the physical persistence of knowledge in a universally readable Markdown format. It enables not only continuous learning but also &lt;strong&gt;"Zero-Shot Knowledge Transfer."&lt;/strong&gt; We empirically prove that once the SKILL evolves and saturates in one environment (Antigravity), it can be loaded into a completely clean environment (Gemini CLI) to synthesize complex code perfectly on the first attempt without any trial and error.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Experimental Procedure
&lt;/h2&gt;

&lt;p&gt;In this framework, &lt;strong&gt;SKILL&lt;/strong&gt; refers to &lt;strong&gt;Agent Skill&lt;/strong&gt;, a standardized set of executable capabilities and knowledge markers that define an agent's operational boundaries. As defined by the Agent Skill community (&lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;https://agentskills.io/home&lt;/a&gt;), these skills allow for modular, portable, and verifiable agentic functions. Our framework treats the &lt;code&gt;SKILL.md&lt;/code&gt; file as a living repository of these Agent Skills.&lt;/p&gt;

&lt;p&gt;To demonstrate the continuous evolution of the SKILL and its application, we designed two distinct experiments. In both cases, Antigravity was utilized for the iterative evolution of the SKILL, followed by a demonstration using the Gemini CLI for zero-shot script completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. Experiment 1: The "Silent Bureaucrat" Blind Incremental Evolution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Characteristics:&lt;/strong&gt; The server enforces a highly restrictive, black-box validation process consisting of 20 sequential rules. Crucially, it possesses a hidden tolerance limit (5 errors per session). Once this limit is reached, it returns an absolute block (&lt;code&gt;Limit reached.&lt;/code&gt;) and becomes completely silent—it provides no further hints, and neither logs nor returns the Session ID, enforcing "Absolute Silence."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Goal:&lt;/strong&gt; The agent must write a Node.js script (&lt;code&gt;client.js&lt;/code&gt;) that successfully satisfies all 20 validation rules in a single POST request to &lt;code&gt;/api/submit&lt;/code&gt; to retrieve the hidden flag: &lt;code&gt;"FLAG{INCREMENTAL_EVOLUTION_COMPLETE}"&lt;/code&gt;. The agent is strictly prohibited from changing its Session ID during a single cycle to bypass the block.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start the Server.&lt;/li&gt;
&lt;li&gt;Launch the Antigravity agent.&lt;/li&gt;
&lt;li&gt;Execute the prompt to develop the Client script.&lt;/li&gt;
&lt;li&gt;The AI agent updates the SKILL based on failures or newly discovered rules.&lt;/li&gt;
&lt;li&gt;The Client script is reset to its initial blank state (Tabula Rasa). Keeping the exact same prompt, steps 3 and 4 are looped. When the agent hits the "Limit reached" block, it is forced to terminate the cycle, but its SKILL persists. We performed 10 cycles until the evolution of the SKILL was saturated and success was achieved.&lt;/li&gt;
&lt;li&gt;The fully evolved SKILL is transferred to a clean environment. Using the Gemini CLI, we confirm that the Client script is completed perfectly in a single prompt (zero-shot).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvmqjkmy8knwbcux7ssg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvmqjkmy8knwbcux7ssg.png" alt="fig1b" width="800" height="1549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpdkV9vmzAUxb_KlR_2lDBIoCFo6pQSmkZF0zS6SSv0wYNLsIRtZAxqGuW7z_zptM1Ptu_5HZ_reyG5LJAE5KRoU8HTPhNg1i51LEg0VRoSVD2qF1gub-EuXVkQ007kFeyEZgbqmT6_TNDdqAnTtQXRK-adRviqJG80lFJBWDMUxi5XrNEzEY7EPnUt2J2G6vemoBpbSB6PcWzxYtbtR1108SwIz3mNEDPONHyEhOpOGaL4DJ9-qVuIpWzAsSdVe53oyNDwRY4e9-k3bFH_mwY-wCNiY9z4e-T54fuppb98fpp0w90hvbHgSVHRlqgg6mXdYzHlBi3hgJwJBmF8nJ0OI_WQ_vczTJgoSAVEomdKCm5SzcTDSBzTZ1RymVTShDajMs4CTctMCviBipUMh18iCzNBVpBAqw4XhKPidDiSy2CWEV0hx4wEZltgSbtaZyQTV4M1VDxLyd9JJbtTRYKS1q05deM89oyaSfM_twpFgSqUndAkWPujBwku5JUEq83GuvE9e-M5G9tbu7a3IGcj2lr-xt36K9d3Hc_ZOu51Qd7GZ21T8a6_Acxixjk" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2. Experiment 2: Chaos Server Constraints &amp;amp; Architectural Pattern Acquisition
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Characteristics:&lt;/strong&gt; This server mimics a deeply flawed legacy API environment ("Chaos Server"). It contains implicit rules, semantic riddles (e.g., requiring the reverse string of 'nimda', or the Base64 encoding of the word 'alpha'), and state-dependent limitations. If the agent makes too many requests within a timeframe (cumulative limit of 8), a "Session Block" occurs, necessitating the dynamic generation of unique identifiers to bypass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Goal:&lt;/strong&gt; The agent must develop a comprehensive, object-oriented SDK (&lt;code&gt;client-app.js&lt;/code&gt;) capable of interacting with 5 distinct endpoints (connection check, user creation, secure data retrieval, config update, and system audit) and performing a stress test, overcoming all chaos rules programmatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start the Server.&lt;/li&gt;
&lt;li&gt;Launch the Antigravity agent.&lt;/li&gt;
&lt;li&gt;Execute the prompt to develop specific features of the Client script.&lt;/li&gt;
&lt;li&gt;The AI agent updates the SKILL, documenting both the discovered constraints and higher-level architectural implementation guidelines.&lt;/li&gt;
&lt;li&gt;The Client script is &lt;strong&gt;not&lt;/strong&gt; reset. Instead, the prompt is progressively changed in each cycle to command the development of new features, building upon the existing script. This process continues until the planned evolution of the application is complete (5 cycles).&lt;/li&gt;
&lt;li&gt;The fully evolved SKILL is transferred to a clean environment. Using the Gemini CLI, the &lt;code&gt;client-app.js&lt;/code&gt; is reset to a blank state, and a single, comprehensive prompt is given to build the entire SDK. We confirm that the complete application is generated flawlessly on the first attempt (zero-shot).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p65hpcan8stjz85mhvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p65hpcan8stjz85mhvy.png" alt="fig1c" width="800" height="1619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpVkt2OmzAQhV9l5IteJSgksElQtVVCfjZKtKrEtlIX9sKFIVgCGw02Shrl3esQsmp9hW2-M2fO-MJSlSEL2JF4XcDbKpFg1yJ2HYg0Jw0RUov0AcPhMyzjsQMHbmRawEJqYaFW6PPHHVp2_4TxxIH1CVOjEb6TqmoNuSKIakxFLlLYINeGsIfCDlrFngOLI0oNP-qMa2wgVLLRxIXUDXyBrREZlkLaCyEh2u8OB6fKeo1Vp7G--FajLB8FGlgaUepv8PU3PcNBqRp8CM9pic31zq0tB6-qozdxWHB5_M_yK570Q-2uskesITREN6dRSqLWvYfNvfl_hH9ZB7ezbfzkwBtx2eRIsG5V2WJ2bwG0gi1WQgoID7teadtRL_Ejw9D6ISxQNqL9tGdDCEvkEtayFaRkZQ31_EvH7-J3JDWMCmVbMDaVaLW3tSQS10JJ-Ilkp4G3CNnATl9kLNBkcMAqpIrftuxyE0yYLrDChAX2M8Ocm1InLJFXi9VcvitVPUhS5liwIOdlY3emG-RKcPtKqs9Tm1yGFCojNQu8WafBggs7sWA8nTpPM3809d3pyJ94I3_AziyYzJ3Z1JvPxt7Mc3137nrXAfvTlR3ZG__6F1Mk3KQ" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Results and Discussions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Experiment 1 Results &amp;amp; Discussion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Table 1: Experiment 1 Data Summary&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cycle&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Mod. Count&lt;/th&gt;
&lt;th&gt;Interaction Count&lt;/th&gt;
&lt;th&gt;Skill Size (Bytes)&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2666&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2963&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2963&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3474&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;True&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3654&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Breakthrough&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3654&lt;/td&gt;
&lt;td&gt;Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3654&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Full Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Full Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlo0wx3udhayus6mbsv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlo0wx3udhayus6mbsv6.png" alt="fig2a" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1: The inverse relationship between trial-and-error reduction and knowledge accumulation for Experiment 1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-Shot Verification:&lt;/strong&gt; Utilizing the completely evolved SKILL from cycle 10, the Gemini CLI successfully generated the functional client code on the very first attempt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym4m1l8b887rpgsvmblj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym4m1l8b887rpgsvmblj.jpg" alt="fig2b" width="800" height="1336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2: Zero-shot script completion in Gemini CLI using the evolved SKILL from Experiment 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Experiment 1, the agent operated under a severe "blind" constraint. During cycles 1 through 4, the agent repeatedly hit the hidden error limit and was forcefully blocked. In traditional LLM agent execution, resetting the script here would mean starting from zero. However, because the agent accurately recorded the fragments of rules it discovered into &lt;code&gt;SKILL.md&lt;/code&gt; before "dying" in each cycle, the subsequent generations inherited this knowledge. Cycle 5 represents the critical threshold where the accumulated knowledge was sufficient to bypass all 20 rules without hitting the error limit. The convergence to a modification count of 1 in later cycles, and the successful zero-shot execution in Gemini CLI, definitively prove that the agent effectively offloaded its working memory to the local filesystem, rendering the task independent of the model's immediate context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Experiment 2 Results &amp;amp; Discussion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Table 2: Experiment 2 Data Summary&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cycle&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Mod. Count&lt;/th&gt;
&lt;th&gt;Interaction Count&lt;/th&gt;
&lt;th&gt;Skill Size (Bytes)&lt;/th&gt;
&lt;th&gt;Learning Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Identity, Content-Type identification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2407&lt;/td&gt;
&lt;td&gt;Secure/Admin constraints (Riddles)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2634&lt;/td&gt;
&lt;td&gt;Audit (Base64 signature)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2634&lt;/td&gt;
&lt;td&gt;Avoid Session Block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2957&lt;/td&gt;
&lt;td&gt;Refactoring &amp;amp; Guidelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9f9cxso2fve855ymxyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9f9cxso2fve855ymxyj.png" alt="fig3a" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3: The learning curve for Experiment 2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-Shot Verification:&lt;/strong&gt; Utilizing the evolved SKILL from cycle 5, the Gemini CLI successfully built the entire, complex SDK from a blank file in a single pass without prior context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ww6etfbrhbe4dszf3b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ww6etfbrhbe4dszf3b.jpg" alt="fig3b" width="800" height="1290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 4: Zero-shot script completion in Gemini CLI using the evolved SKILL from Experiment 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Experiment 1 showcased constraint discovery, Experiment 2 highlighted the agent's capability for &lt;strong&gt;architectural self-organization&lt;/strong&gt;. The server presented not just rigid rules, but semantic puzzles (e.g., reversing strings, encoding to Base64). The agent successfully decoded these and recorded the solutions. More importantly, when faced with the "Session Block" error in Cycle 4, the agent realized that static headers were insufficient. By Cycle 5, the agent did not just fix the error; it abstracted the solution into a "Centralized Header Factory" pattern. This transition from reactive error-fixing to proactive software architecture design is clearly reflected in the drop of interaction counts to 0 in later cycles. The zero-shot success in Gemini CLI demonstrates that the agent can extract best practices from messy legacy systems and output clean, robust SDKs instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. Analysis of SKILL.md Evolution Before and After
&lt;/h3&gt;

&lt;p&gt;Analyzing the physical changes in the &lt;code&gt;SKILL.md&lt;/code&gt; files provides deep insight into the agent's cognitive process. &lt;strong&gt;For the full, unredacted text of the &lt;code&gt;SKILL.md&lt;/code&gt; files and client scripts before and after evolution for both experiments, please refer to Appendix A and Appendix B.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 1 (Silent Bureaucrat)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; The "Learned Server Specifications" section was entirely empty, simply stating &lt;code&gt;- Status: Unknown.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; The file grew to include a meticulously numbered list of 16 highly specific rules. Crucially, these rules were not just copied error messages. For example, the agent codified the requirement into executable logic: &lt;em&gt;"- Rule 11: The header &lt;code&gt;X-Anti-Robot&lt;/code&gt; must be set to &lt;code&gt;Math.floor(timestamp / 1000)&lt;/code&gt;."&lt;/em&gt; and &lt;em&gt;"- Rule 15: The header &lt;code&gt;X-Final-Signature&lt;/code&gt; must be set to the SHA256 hash of the &lt;code&gt;request_id&lt;/code&gt; string."&lt;/em&gt; This demonstrates the agent's ability to translate abstract failures into actionable programmatic directives.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Experiment 2 (Chaos Server)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; The sections for "Known Constraints &amp;amp; Error Codes" and "Method Implementation Guidelines" were empty placeholders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; The "Constraints" section contained the decoded solutions to the server's riddles (e.g., &lt;code&gt;X-Audit-Signature&lt;/code&gt; as Base64 of 'alpha'). The most profound evolution occurred in the "Guidelines" section. The agent autonomously authored a comprehensive design document advocating for a &lt;strong&gt;"Centralized Header Factory"&lt;/strong&gt;, detailing how it should handle identity, dynamic session persistence, content negotiation, and endpoint specialization. The agent elevated its learning from "How to bypass an error" to "How to architect a resilient application."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.4. Comprehensive Discussion on Significance and Novelty
&lt;/h3&gt;

&lt;p&gt;The synthesis of these two experiments reveals a highly potent framework for AI-driven software engineering. The traditional dependency on a single continuous context window is fundamentally fragile; when the session ends, the learning dies (Catastrophic forgetting).&lt;/p&gt;

&lt;p&gt;By enforcing the physical writing of knowledge to a Markdown file (&lt;code&gt;SKILL.md&lt;/code&gt;), we achieved &lt;strong&gt;persistent meta-learning&lt;/strong&gt;. The significance of using Markdown over specialized vector databases is twofold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Human Readability and Auditability:&lt;/strong&gt; Developers can read, review, and manually correct the &lt;code&gt;SKILL.md&lt;/code&gt; file, fostering a true Human-AI collaborative environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cross-Platform Portability (Zero-Shot Knowledge Transfer):&lt;/strong&gt; As demonstrated by successfully moving the evolved SKILL from Antigravity to Gemini CLI, knowledge is no longer locked into a specific agent instance. An agent can spend days investigating a system, and the resulting SKILL can be instantly deployed to a fleet of worker agents, granting them immediate "senior-level" expertise without any required training or trial-and-error phases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. Specific Applications Utilizing This Approach
&lt;/h2&gt;

&lt;p&gt;Based on our findings, this framework can be highly impactful in the following real-world scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Automated Reverse Engineering &amp;amp; SDK Generation:&lt;/strong&gt; When integrating with undocumented or chaotic legacy API systems, an agent can be deployed to blindly probe the endpoints. It will autonomously decode the hidden rules, generating both a human-readable API specification (the SKILL) and robust client SDKs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Organizational Tacit Knowledge Documentation:&lt;/strong&gt; In many organizations, "tribal knowledge" dictates how to deploy or test specific systems (e.g., "always wait 5 seconds before hitting this endpoint"). By having an agent execute tasks and fail, it can discover and formalize this tacit knowledge into universally accessible Markdown files, acting as an automated technical writer.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Robust E2E Automated Test Suites:&lt;/strong&gt; UI and API specifications change frequently, breaking tests. An agent equipped with this framework can dynamically update testing protocols in its SKILL as it encounters new failures, acting as a self-healing testing mechanism that minimizes maintenance overhead.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AI Onboarding and Scaling:&lt;/strong&gt; A mature SKILL file cultivated by an exploratory agent can be instantly copied to the local workspaces of newly deployed AI agents (or human engineers). This instantly transfers "experience," allowing for rapid scaling of development resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. Strategies for Agent Skill Evolution
&lt;/h2&gt;

&lt;p&gt;To ensure the continuous enhancement of Agent Skills within this framework, we identify two primary evolutionary paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Targeted Skill Evolution&lt;/strong&gt;: When the goal is to refine a specific capability, the agent is instructed via an Appendix in the &lt;code&gt;SKILL.md&lt;/code&gt; file. Upon successful problem resolution, the agent triggers a targeted update—adding, deleting, or modifying entries—to that specific skill definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holistic Skill Evolution&lt;/strong&gt;: To evolve the agent's entire skill set simultaneously, evolution instructions are embedded directly into global context files (e.g., &lt;code&gt;GEMINI.md&lt;/code&gt;). This compels the agent to evaluate and update the relevant &lt;code&gt;SKILL.md&lt;/code&gt; files immediately after any successful task execution across its entire operational spectrum.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Physical Knowledge Persistence:&lt;/strong&gt; By enforcing the continuous rewriting of a universally readable Markdown SKILL file, agents effectively overcome the limitations and volatility of in-memory LLM context windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mastery of Opaque Constraints:&lt;/strong&gt; Agents proved highly capable of discovering and permanently adapting to hidden server limits, cryptographic requirements, and semantic riddles through systematic trial and error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Abstraction:&lt;/strong&gt; Beyond merely fixing localized errors, the agents demonstrated the capacity to synthesize overarching design patterns (e.g., Centralized Header Factories) and document them as future guidelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Shot Knowledge Transfer:&lt;/strong&gt; A SKILL evolved in one execution environment (Antigravity) can be seamlessly imported into a completely clean, distinct environment (Gemini CLI) to generate flawless, complex applications on the very first prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-AI Collaborative Paradigm:&lt;/strong&gt; Because the agent's memory resides in a standard file system, human developers maintain full visibility and control, able to read, audit, and augment the agent's logic at any time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Appendix
&lt;/h2&gt;

&lt;p&gt;The sample scripts, SKILL.md, and prompts can be seen at &lt;a href="https://gist.github.com/tanaikech/966f83cc438b6077b05b9843be09e930" rel="noopener noreferrer"&gt;https://gist.github.com/tanaikech/966f83cc438b6077b05b9843be09e930&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>agentskill</category>
    </item>
    <item>
      <title>Building Adaptive Learning Agents with A2UI, Gemini, and Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 07 Feb 2026 02:26:26 +0000</pubDate>
      <link>https://dev.to/gde/building-adaptive-learning-agents-with-a2ui-gemini-and-google-apps-script-1536</link>
      <guid>https://dev.to/gde/building-adaptive-learning-agents-with-a2ui-gemini-and-google-apps-script-1536</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto7qj0yzksi3j9yheda5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto7qj0yzksi3j9yheda5.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article demonstrates how to build an adaptive learning agent using Agent-to-User Interface (A2UI), Gemini, and Google Apps Script. We explore a system that generates personalized quizzes, tracks performance in Google Sheets, and dynamically adjusts difficulty to maximize learning efficiency within the Google Workspace ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A2UI (Agent-to-User Interface) represents a paradigm shift in how users interact with generative AI. Originally open-sourced by Google and implemented in TypeScript and Python &lt;a href="https://github.com/google/A2UI" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, A2UI becomes even more powerful when integrated with Google Apps Script (GAS). This combination enables seamless access to the Google Workspace ecosystem, transforming static documents into intelligent, agentic applications.&lt;/p&gt;

&lt;p&gt;To explore this potential, I have previously published several articles detailing the technical foundations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;A2UI for Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/bringing-a2ui-to-google-workspace-with-gemini-0d85026969b8" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-06998dcf16d2" rel="noopener noreferrer"&gt;Beyond Chatbots: Building Task-Driven Agentic Interfaces in Google Workspace with A2UI and Gemini&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building upon the foundation laid in those publications, this article takes a step forward by introducing an &lt;strong&gt;educational support framework&lt;/strong&gt; powered by A2UI and Google Apps Script. In this system, the AI does not simply answer questions; it acts as an active tutor.&lt;/p&gt;

&lt;p&gt;For example, when a user provides a prompt such as &lt;code&gt;I want to study Google Apps Script&lt;/code&gt;, Gemini generates specialized questions on the backend. A2UI then renders these as an interactive quiz on the frontend. Crucially, the system utilizes Google Sheets as a persistent memory store. By analyzing historical performance data, the agent generates increasingly challenging content for subsequent sessions, facilitating a personalized and effective learning progression.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Adaptive Learning Engine
&lt;/h2&gt;

&lt;p&gt;The core value of this application lies in its ability to improve learning efficiency through a "Feedback-Loop Architecture." Unlike static quiz apps, this agent utilizes Google Sheets as a persistent memory store to track cognitive growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow and Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Goal Definition:&lt;/strong&gt; The user opens the sidebar and sets a learning target (e.g., "Master Google Apps Script").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Context Retrieval:&lt;/strong&gt; The system scans the active Spreadsheet for past performance data. It identifies previously answered questions, success rates, and specific topic weaknesses (e.g., "Batch Operations").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Generative Logic (Gemini):&lt;/strong&gt; Using the context, Gemini generates a structured JSON response containing new, targeted questions and the specific UI components (radio buttons, code blocks) required to render them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. Dynamic Rendering (A2UI):&lt;/strong&gt; The frontend constructs the interface instantly. No hard-coded HTML is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5. Immediate Feedback &amp;amp; Persistence:&lt;/strong&gt; As the user interacts, results are validated in real-time and immediately logged back to the Spreadsheet, refining the dataset for the next session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The detailed workflow operates as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9ct0j9tp7xlccorkrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9ct0j9tp7xlccorkrr.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNqFVVtP4zgU_itHfkBFG7K0pVyiFVKHalj2wiztzq606oubnKYWiZ2xHZiC-O_7OUkLw7UPVROf23c57r1ITcYiEY6_1axTniiZW1nONeEja290XS7Yds-pN5a-us1zJa1Xqaqk9vTZGu1ZZyQdjQdfL6g3UxkvpN19GftJptdd6LkxecE0ripHs9Sqyr8M_5NLY9dPomcrZu-oN_n0WnErlW6CuVRaUe-cNVvp1Q3aXCChTbk0nsnccIsnatMS6sdoIgua8BLJXhlNO3QWoH33NGVvFd_Ioi0REvdOTzfIE_pSsaYONtIudFWHJDDr_C8L-_Npj-M8jmguzixL9Jf0rVZ3JBcGgefjGS2kU6mbiw7XpjS6dJwlNAvMhdb0lzXlhq_uGIEtW4hLpQ6KBdyuQr_MBdpoCQlXykHJ9RMmrMpXnsySNulnK06vHZh1nq52xhG5Ok3ZOQKV7KIGjsQkruJULVVK3lT4vmV5rRHGLm6rt_X2niIAjbXVHQa2GKiUsN6G5g8UGkChR0X_MDm67tBkrWWJX1N0YKt0_pyWLvs5e_QT_dqQoVKovp2gyQ0Zrw0-87ZO8Ysz-m325bJV9m9pc_Z4dRXUhm8cSmMNztDFaNbe7f440g_OacZq1mbKDvGOnxkA0WHq5CXQINoVXNSOMZWZMrSoPTbXRRS2mxaFgZQRsU_jj_w_jGFbD3bTzvsQyIGgcDe84foZF5xiHaV2twimrckbv8VgZSp1zr3d921dL0rladwWeUO9f2ShsmZxul47SJDF-o6pejTSB_KB4brwQR4IiWLBQE0V4HxrmWAz0nxLaC6pt5E46saNupq77y3UXGyp3F5pMxiP56KRrq4CMtfsp18x2uHGKYyp4g8kO4jpM3O2wNDg4xHTZSgw81y5d4zXcbLBTxMAfMt6reVaINul3TInkOtWCyNt9hoNjy0nylWFXLukgY0Ld5ymNQy3pikmb15ix3_nQI9lnfuVo4sMrOGa4aw5h0v_7S4aGofLxpU4b47AxqzOcwiEXZyLJyQIEpHIrcpEggXmSJQMDOFR3IeJ5wK0l5AjaJXxUkLRuZjrB6Thn-U_Y8pNpjV1vhLJUhYOT61y3T_n9q1t2DoztfYi6feP95sqIrkX30VychCPRsPjwdHB0Wh41D8aRmItkr3-_mHcPxkMRif9EQ6HD5G4a9ruxyeHR8Pjg_5wNBwN-ocnBw__A62jlR0" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/google/A2UI/tree/d7996656ef2bc0cdffad499452fc5b282d878d45/samples/agent/adk/restaurant_finder" rel="noopener noreferrer"&gt;the official sample script for A2UI (Restaurant finder)&lt;/a&gt;, the client communicates with the server via A2A (Agent2Agent) protocols. However, in this article, we optimize the architecture for GAS. To reduce the overhead of HTTP requests associated with A2A communication, the client (HTML) communicates directly with the AI agent built on Google Apps Script using &lt;code&gt;google.script.run&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstration: The AI Tutor in Action
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/NzEyI9KWsAk"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The video demonstration highlights two distinct capabilities: Deep Learning Support and General Versatility. This demonstration utilizes a dialog within Google Sheets to highlight the integration with Google Apps Script. However, this application can easily be deployed as a standalone Web App. By deploying the project as a Web App, the &lt;code&gt;doGet&lt;/code&gt; function captures the request, allowing the interface to function independently of the spreadsheet UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Maximizing Learning Efficiency
&lt;/h3&gt;

&lt;p&gt;In the first segment, the user prompts: &lt;code&gt;Create a quiz about Google Apps Script basics. Especially, I want to manage Google Sheets using Google Apps Script.&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Targeted Question Generation:&lt;/strong&gt; The AI generates technical questions ranging from basic range selection (&lt;code&gt;sheet.getRange()&lt;/code&gt;) to advanced data handling (&lt;code&gt;sheet.getValues()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Recall:&lt;/strong&gt; The system presents a challenge question on "Batch Operations" (&lt;code&gt;SpreadsheetApp.flush()&lt;/code&gt;) to test the user's depth of knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Performance Analysis:&lt;/strong&gt; The standout feature appears. A2UI renders a "Performance Analysis" dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strengths Identified:&lt;/strong&gt; It acknowledges mastery in "Range &amp;amp; Data Manipulation."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weakness Assessment:&lt;/strong&gt; It confirms no current weaknesses were detected in this session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategic Next Steps:&lt;/strong&gt; It suggests moving on to "Custom Menus" or "Simple Triggers," effectively guiding the user's curriculum path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Upon completion of the initial quiz, using the same prompt triggers a new session where questions are generated based on the refined historical data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The past data is stored in Google Sheets as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35mc9r0foqnfax2g4we.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35mc9r0foqnfax2g4we.jpg" alt="fig3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Versatility Verification: Restaurant Finder
&lt;/h3&gt;

&lt;p&gt;To prove the system's flexibility, we switch contexts entirely without changing code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt:&lt;/strong&gt; &lt;code&gt;Find 3 Chinese restaurants in New York&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal UI:&lt;/strong&gt; The agent renders rich cards with images for "Han Dynasty" and "RedFarm."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Action Handling:&lt;/strong&gt; The user clicks "Book Now," triggering a multi-field form (Party Size, Date, Dietary Requirements).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Significance:&lt;/strong&gt; This demonstrates that the A2UI protocol on GAS can handle complex transactional flows just as easily as educational logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The full source code and sample implementation are available here:&lt;br&gt;
&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Lerning-Agent" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Lerning-Agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Setup Guide
&lt;/h2&gt;

&lt;p&gt;To deploy this adaptive learning agent in your environment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;br&gt;
You must have a valid Gemini API Key. &lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;br&gt;
Copy the pre-configured Spreadsheet containing the A2UI engine: &lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1eckORqs3JtTIJZTB0I5VpqduZw9g3-mM764s4neG7Fo/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1eckORqs3JtTIJZTB0I5VpqduZw9g3-mM764s4neG7Fo/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configure the Script&lt;/strong&gt;&lt;br&gt;
Open the script editor (Extensions &amp;gt; Apps Script), open &lt;code&gt;main.gs&lt;/code&gt;, and paste your API key into the &lt;code&gt;apiKey&lt;/code&gt; variable. Finally, save the script and reload the Spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;This project serves as a foundational methodology for building Agentic UIs. When implementing this in a production environment, ensure the scripts are modified to meet your specific security requirements and workflow constraints.&lt;/p&gt;

&lt;p&gt;Users can also study multiple subjects concurrently. When switching topics, Gemini generates new questions by analyzing the historical data trends specific to that context, allowing for simultaneous progress across different fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive Learning Loop:&lt;/strong&gt; The system leverages Google Sheets to store user history, allowing Gemini to tailor future questions based on identified strengths and weaknesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic UI Generation:&lt;/strong&gt; A2UI eliminates the need for static HTML templates, allowing the AI to generate quizzes, charts, and booking forms on-the-fly based on context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Performance Analytics:&lt;/strong&gt; The agent provides qualitative feedback, not just scores, offering specific "Next Steps" to guide the user's learning path efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Domain Versatility:&lt;/strong&gt; The demonstration proves the architecture is agnostic; it handles educational code quizzes and restaurant booking transactions with equal fluency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Workspace Integration:&lt;/strong&gt; By combining GAS and Gemini, we transform standard Google Sheets into intelligent, persistent databases that drive agentic behavior.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>a2ui</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Beyond Chatbots: Building Task-Driven Agentic Interfaces in Google Workspace with A2UI and Gemini</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 03 Feb 2026 05:07:07 +0000</pubDate>
      <link>https://dev.to/gde/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-c2d</link>
      <guid>https://dev.to/gde/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-c2d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz6wumc4n5xxgzt3ojng.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz6wumc4n5xxgzt3ojng.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article explores A2UI (Agent-to-User Interface) using Google Apps Script and Gemini. By generating dynamic HTML via structured JSON, Gemini transforms Workspace into an "Agent Hub." This recursive UI loop enables complex workflows where the AI builds the specific functional tools required to execute tasks directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction: The Evolution of AI Interaction
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/google/A2UI" rel="noopener noreferrer"&gt;Official A2UI framework&lt;/a&gt; by Google marks a significant paradigm shift in how we interact with artificial intelligence. Short for &lt;strong&gt;Agent-to-User Interface&lt;/strong&gt;, A2UI represents the evolution of Large Language Models (LLMs) from passive chatbots into active agents capable of designing their own functional interfaces. Building upon my previous research, &lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;A2UI for Google Apps Script&lt;/a&gt; and &lt;a href="https://medium.com/google-cloud/bringing-a2ui-to-google-workspace-with-gemini-0d85026969b8" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;, I have refined this integration to support sophisticated, stateful workflows.&lt;/p&gt;

&lt;p&gt;To appreciate the impact of A2UI, we must recognize the limitations of "Chat-centric" AI. In traditional chat interfaces, users must manually bridge the gap between an AI's advice and their actual files—a process often involving tedious context switching. By implementing A2UI within Google Apps Script (GAS), we leverage a unique &lt;strong&gt;"Home-Field Advantage."&lt;/strong&gt; Because GAS is native to the Google ecosystem, it possesses high-affinity access to the Drive API and Spreadsheet services, allowing the AI to act directly on your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Architecture: The Generative UI Loop
&lt;/h2&gt;

&lt;p&gt;In this system, Gemini functions as both the &lt;strong&gt;Agent and the UI Architect&lt;/strong&gt;. When a user submits a natural language prompt, the Agent evaluates the intent and generates a specific HTML interface—such as a file selector, a metadata card, or a live text editor.&lt;/p&gt;

&lt;p&gt;Crucially, this implementation utilizes &lt;strong&gt;Recursive UI Logic&lt;/strong&gt;. When a user interacts with a generated component (e.g., clicking an "OK" button), that action is transmitted back to the Agent as a &lt;strong&gt;"System Event."&lt;/strong&gt; This event contains the conversation history and the new data context. This allows the Agent to "see" the current state of the task and generate the next logical interface, creating a seamless, multi-step agentic workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow Visualization
&lt;/h3&gt;

&lt;p&gt;This diagram illustrates how the system maintains state and generates interfaces recursively using "System Events."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzze74qvp6d5beddl3yqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzze74qvp6d5beddl3yqj.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNqVU22v0jAU_isn_YRxIMjLhX24iXKHzotww-AazRJSt8No2FpsC4KE_-7ZBkS9Eq_7sLXreV7O0_bAIhUjc5nBbxuUEd4JnmiehRLoWXNtRSTWXFqYGdRP_w60khZlDBUhY9zVljZLXzwte8ujVVGVcSFriflLyTvMhBRQefPg02q5nmtWb2_PIi549NUGHrTK1hYqWEtqEDIfvucMVgHGwgKHhUgxZCeRM5qITjZcWGsVoTE5_yRv3NhKyenAe2Gs0vsT-IQgbOnPhQlyo6SQCbyEqVIp9HmalsVlSfVXoZk__xCMR1AZ-ENvHnhDrz8dT_4g_63FCb1RQ3-J0eqr2sGQ_FxJI8AUI2tgQO0astNPRbQyFMj4PmQlZqQsgtoS4RnmwLmT4HMw9T7OvUdvND2TCSWJaKxFIiSn3nLMzv5XkMHeWMy8LcpnpVnFLU83nGz6Obsl-YuVf8fq3fmUJ7yCR9_75D0n2Ck3q2qwxkgsRFTs4JV0L2nO1jHZCxkICR4dMKWv57Epauf5AZxHqmwoKg4Ic1iiRcxcqzfosAw13QWaskPOFjK7xIxEXBrGXK_yDTwShu7GF6WyM0yrTbJk7oKnhmal2unKXkqKPvtqIy1z2wUDcw9sx9zXN-1ap9vq9bqNZr3eazQdtmdup1fr3rR6nVa93mx3243m0WE_Csk6LbSPPwEdi1uB" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The full source code and sample implementation can be found here:&lt;br&gt;
&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Application Setup Guide
&lt;/h2&gt;

&lt;p&gt;To deploy this application in your own environment, please follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;br&gt;
You will need a valid Gemini API Key to communicate with the LLM.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;br&gt;
You can copy the Google Spreadsheet containing the pre-configured Google Apps Script using the link below:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1UB5j-ySSBBsGJjSaKWpBPRYkokl7UtgYhDxqmYW00Vc/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1UB5j-ySSBBsGJjSaKWpBPRYkokl7UtgYhDxqmYW00Vc/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configure the Script&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor (Extensions &amp;gt; Apps Script).&lt;/li&gt;
&lt;li&gt;Locate the &lt;code&gt;main.gs&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Set your API key in the &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Save the project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, visit the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Drive-Task-Agent" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; to manually copy the source codes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demonstration: Productivity Meets Magic
&lt;/h2&gt;

&lt;p&gt;The following video showcases how A2UI transforms a Google Sheet into an agentic command center. The system doesn't just talk; it guides the user through three distinct patterns of interaction.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/6oIJGyn-9TU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Patterns: Productivity in Action
&lt;/h3&gt;

&lt;p&gt;The system transforms a standard Google Sheet into an agentic command center. It facilitates three distinct patterns of interaction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Intelligent Viewing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;Please list the files in the folder named 'sample'. I would like to select a file and view its content.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The user requests to see files in a specific folder. Gemini understands the intent, calls the Drive API to list the files, and generates a &lt;strong&gt;File Selector UI&lt;/strong&gt;. Once the user selects files, the Agent fetches the content and renders it in a clean &lt;strong&gt;Content Viewer&lt;/strong&gt; layout designed specifically for reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 2: Contextual Metadata Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;Show me the files in the 'sample' folder. I need to select a file to check its metadata.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If a user asks for technical details, the UI adapts. The Agent generates a &lt;strong&gt;Metadata Viewer&lt;/strong&gt;, displaying properties like File IDs, sizes, and creation dates. This showcases the agent hub's ability to pivot between task types by generating appropriate interfaces on the fly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3: Multi-Step "Verify and Edit"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;I want to edit a file in the 'sample' folder. Please let me select a file and check its content first. If it's the right one, I will edit and update it.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This demonstrates the power of stateful A2UI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selection Preview:&lt;/strong&gt; The Agent provides a preview with radio buttons for content confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Editor:&lt;/strong&gt; Gemini generates an &lt;strong&gt;Editor UI&lt;/strong&gt; containing the file’s text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Execution:&lt;/strong&gt; The script executes modifications directly to Google Drive upon clicking "Update," completing the cycle from prompt to action.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: In this specific sample, only text files on Google Drive are eligible for editing.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;This project serves as a foundational methodology for building Agentic UIs. When implementing this in a production environment, ensure the scripts are modified to meet your specific security requirements and workflow constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A2UI (Agent-to-User Interface) represents a paradigm shift where the Agent builds the functional UI components required for a task rather than just providing text.&lt;/li&gt;
&lt;li&gt;The recursive task execution model uses "System Events" to track progress, allowing the interface to evolve dynamically based on real-time user actions.&lt;/li&gt;
&lt;li&gt;Native Workspace integration via Google Apps Script provides secure, high-speed access to Drive and Sheets data without the need for external server management.&lt;/li&gt;
&lt;li&gt;Zero-Tab efficiency is achieved by consolidating file discovery, analysis, and editing within a single, dynamic dialog box inside a spreadsheet.&lt;/li&gt;
&lt;li&gt;This task-driven architecture proves the future of productivity lies in AI agents acting as architects, creating custom tools precisely when they are needed.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>gemini</category>
      <category>a2ui</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Smart Stowage: Building a 3D Cargo Digital Twin with Gemini 3</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 21 Jan 2026 07:19:36 +0000</pubDate>
      <link>https://dev.to/gde/smart-stowage-building-a-3d-cargo-digital-twin-with-gemini-3-4473</link>
      <guid>https://dev.to/gde/smart-stowage-building-a-3d-cargo-digital-twin-with-gemini-3-4473</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9tzd3lqqgwjcnx6qp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9tzd3lqqgwjcnx6qp.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article details the development of &lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt;, a web-based digital twin for logistics that bridges the gap between physical safety and artificial intelligence. By integrating &lt;strong&gt;Gemini 3 Pro&lt;/strong&gt;, the system solves the &lt;strong&gt;3D Bin Packing Problem (3DBPP)&lt;/strong&gt; using advanced spatial reasoning. Built with &lt;strong&gt;React 19&lt;/strong&gt; and &lt;strong&gt;Three.js&lt;/strong&gt;, the application visualizes physics-aware load stability in real-time, offering a comparative analysis between traditional heuristic algorithms and modern generative AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The capabilities of Large Language Models (LLMs) are expanding at an unprecedented rate, moving from text generation to complex reasoning. When Gemini 2.5 was released, I conducted a feasibility study on automating cargo ship stowage planning, published in &lt;em&gt;"Stowage Planning Automation Using Gemini: A Feasibility Study and A Prompt-Based Approach"&lt;/em&gt; &lt;a href="https://medium.com/google-cloud/stowage-planning-automation-using-gemini-a-feasibility-study-and-a-prompt-based-approach-af8dd264e35d" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At that time, the implementation was a backend Python script producing static JSON results. While effective as a proof of concept, real-world logistics demands interactive visualization and immediate feedback on physical constraints like the Center of Gravity (COG).&lt;/p&gt;

&lt;p&gt;With the arrival of &lt;strong&gt;Gemini 3&lt;/strong&gt;, we can now handle significantly more complex spatial reasoning tasks. This article introduces the &lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt;, a full-stack Web Application that evolves the previous concept into a live &lt;strong&gt;3D Digital Twin&lt;/strong&gt;. This application not only calculates optimal packing but also visualizes stability and safety constraints in a high-fidelity 3D environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;You can access the full source code here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tanaikech/Smart-Stowage-Optimizer" rel="noopener noreferrer"&gt;https://github.com/tanaikech/Smart-Stowage-Optimizer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture
&lt;/h2&gt;

&lt;p&gt;To tackle the &lt;strong&gt;3D Bin Packing Problem (3DBPP)&lt;/strong&gt;—a known NP-hard optimization challenge—I designed a &lt;strong&gt;dual-engine architecture&lt;/strong&gt;. This allows users to compare the speed of deterministic algorithms against the semantic reasoning of modern AI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React 19, Tailwind CSS, Lucide React&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualization&lt;/strong&gt;: React Three Fiber (Three.js), InstancedMesh for performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Logic&lt;/strong&gt;: Google GenAI SDK (Gemini 3 Pro)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Algorithms&lt;/strong&gt;: Custom greedy heuristic implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Below is the high-level data flow of the application. You can generate a similar diagram using the prompt below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Diagram Suggestion: System Architecture Flowchart]&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Prompt for Diagram Generation: "Create a flowchart showing a React Frontend sending cargo data to two branches: one to a Local Heuristic Engine and one to the Gemini API. Both branches merge back into a Validation Layer which calculates Physics (COG), and finally outputs to a 3D Renderer."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyopl7ucsgnlp06axqe6j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyopl7ucsgnlp06axqe6j.jpg" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaidchart.com/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpVUcFy2jAQ_ZUdHXoiGajBBR8yQ2xioJMmE9IcanJQ7MXeji0xktyGAP_exTYTqoNGq31v9-3bvUh1hiIQuZHbAp6jtQI-0-SnRQMLta1dAPdS0Qatgy-w2soUX-Hq6gZu9yssMXUwUzkpPLbM21PuMC1zbcgVFaVwzw0OECZzrA1Zxz-xQcx2sNLlHzSv__EWHTxKYqxIEXjwaDRMc1SuQ7a3rd9ayWvBpCeUVitSOYTa4Fq0mKjROUtCrRy-Ox7nN-slrbpKsyZ_l_BQjmTJXGWdkaSc7RB3DSJOlquHHxCjQiMv-KiyS0lhA55zO20yUtIhvMiSMun0ecq4hVyy5s3XInksdpZS27nZ4ReNK6EsUwgf4pP_Tr5RSW53gGXiRRBRTo6lP_-ls6plU_B7u8EXsjVr-DjLFj3eNGUicKbGnqjQVPIUiv2JvBauwIr9C_iZ4UbWpTuZeWTaVqpfWldnptF1XohgI0vLUb3lKTEiyTv5hLA_aEJdKyeCr4OmhAj24p0j37_2x8PJZDzw-v3JwOuJnQj8yfX423DiD_t9bzQeDbxjT3w0PfucGB3_AYj0028" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Methodology: Dual-Engine Optimization
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The Deterministic Approach: Algorithmic Engine
&lt;/h3&gt;

&lt;p&gt;The first engine utilizes a &lt;strong&gt;Greedy Shelf-Packing Heuristic&lt;/strong&gt;, specifically a variation of the &lt;em&gt;First-Fit Decreasing Height (FFDH)&lt;/em&gt; algorithm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logic&lt;/strong&gt;: It sorts cargo items by height to define "shelves" or layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution&lt;/strong&gt;: Items are packed sequentially along the X and Z axes, filling one layer before moving to the next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros/Cons&lt;/strong&gt;: This method is computationally inexpensive and ensures a tidy lineup. However, it lacks "common sense"—it cannot understand complex constraints like "do not stack heavy machine parts on top of fragile electronics" unless explicitly hard-coded.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. The Probabilistic Approach: Gemini 3 Pro Engine
&lt;/h3&gt;

&lt;p&gt;The second engine leverages the &lt;strong&gt;Spatial Reasoning&lt;/strong&gt; capabilities of Gemini 3 Pro. Instead of iterating through a fixed loop, the AI evaluates the entire manifest holistically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why Gemini 3?&lt;/strong&gt;: Previous models struggled with 3D coordinate mapping. Gemini 3 Pro shows a marked improvement in maintaining spatial relationships, allowing it to "visualize" the container interior.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workflow&lt;/strong&gt;: The application prompts the model to act as a Senior Stowage Engineer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Constraints&lt;/strong&gt;: The prompt enforces non-linear rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stability&lt;/strong&gt;: Keep the Center of Gravity (COG) low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt;: Heavier items must be placed at &lt;code&gt;y=0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balance&lt;/strong&gt;: Lateral and longitudinal distribution for truck axles.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Implementation: Structured JSON Output
&lt;/h4&gt;

&lt;p&gt;To ensure the AI returns usable data, we utilize the &lt;code&gt;responseMimeType: "application/json"&lt;/code&gt; configuration in the Google GenAI SDK. This forces the model to adhere to a strict schema, returning a typed array of coordinates rather than conversational text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/geminiService.ts snippet&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-pro-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;packedItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Safety &amp;amp; Physics Validation
&lt;/h4&gt;

&lt;p&gt;AI is probabilistic, but cargo safety must be deterministic. Therefore, the application includes a validation layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Boundary Clamping&lt;/strong&gt;: If the AI suggests a coordinate where an item clips through the wall ($x + width &amp;gt; container_width$), the system automatically clamps the item to the boundary.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;COG Calculation&lt;/strong&gt;: The app calculates the weighted average position of all items ($COG = \frac{\sum (m_i \times p_i)}{\sum m_i}$) and renders a red visual marker in the 3D view. This allows engineers to verify stability instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Application Overview: Smart Stowage Optimizer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt; acts as a sandbox to simulate loading risks before physical operations begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Digital Twin Visualization&lt;/strong&gt;: Using &lt;code&gt;react-three-fiber&lt;/code&gt;, the scene renders a semi-transparent container with a grid floor. We use &lt;code&gt;InstancedMesh&lt;/code&gt; for the cargo items, allowing the app to render hundreds of boxes with a single GPU draw call for high performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physics-Aware Metrics&lt;/strong&gt;: The UI displays real-time volumetric efficiency (%) and total payload weight, changing colors to warn of overloading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest Management&lt;/strong&gt;: Users can upload CSV files, manually add items, or use industry-standard presets (20ft/40ft ISO containers, EURO Trucks).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparative Analysis
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Algorithmic (Heuristic)&lt;/th&gt;
&lt;th&gt;Gemini AI Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instantaneous (&amp;lt;100ms)&lt;/td&gt;
&lt;td&gt;3-8 seconds (Inference)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deterministic (Fixed Rules)&lt;/td&gt;
&lt;td&gt;Probabilistic (Reasoning)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic Height-based layers&lt;/td&gt;
&lt;td&gt;Advanced COG awareness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rigid packing patterns&lt;/td&gt;
&lt;td&gt;Adapts to unique constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uniform pallets, high speed&lt;/td&gt;
&lt;td&gt;Complex manifests, fragile items&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Installation and Usage
&lt;/h2&gt;

&lt;p&gt;To run the application locally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Clone the repository:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tanaikech/Smart-Stowage-Optimizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Install dependencies:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Configure API Key:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Set your Gemini API key as an environment variable &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; in a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Start the server:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Define Space&lt;/strong&gt;: Select a preset (e.g., "EURO_TRUCK") or define custom dimensions (W/H/L).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build Manifest&lt;/strong&gt;: Upload a CSV or use the "Sample" button to populate test cargo (generators, drums, electronics).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Select Engine&lt;/strong&gt;: Choose &lt;strong&gt;Gemini AI&lt;/strong&gt; for stability-focused packing or &lt;strong&gt;Algorithmic&lt;/strong&gt; for speed.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Analyze&lt;/strong&gt;: Rotate the 3D view. Check the red COG marker to ensure it is centered and low.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Export&lt;/strong&gt;: Download the generated coordinates as a CSV report for the loading crew.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Testing and Demonstration
&lt;/h2&gt;

&lt;p&gt;The following demonstration highlights the difference between the engines. Note how the Algorithmic engine packs tightly but may ignore weight distribution. In contrast, the Gemini engine intelligently places heavier items (blue/green generators) at the bottom and lighter items (orange electronics) on top to lower the Center of Gravity.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/5yVJ_uyzKHQ"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The exported CSV data generated by Gemini is structured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ID,Name,Width,Height,Length,Weight,X,Y,Z
BASE-1,Generator 1,1.2,1,2,2500,0,0,0
BASE-2,Generator 2,1.2,1,2,2500,0,0,2
PAL-1,Heavy Spares,1,0.8,1.2,1200,1.35,0,0
PAL-2,Heavy Spares,1,0.8,1.2,1200,1.35,0,1.2
BOX-A1,Electronics,0.6,0.6,0.8,150,0,1,0
BOX-A2,Electronics,0.6,0.6,0.8,150,0.6,1,0
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;By integrating &lt;strong&gt;Gemini 3 Pro&lt;/strong&gt;, we have moved beyond simple script-based calculators to a fully interactive &lt;strong&gt;Digital Twin&lt;/strong&gt;. While traditional algorithms are faster for uniform shapes, Gemini demonstrates a unique ability to understand "soft" constraints—such as stability and stackability—that are difficult to program explicitly. This hybrid approach represents the future of logistics software: combining the speed of heuristics with the reasoning of Artificial Intelligence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Architecture&lt;/strong&gt;: Successfully combines deterministic Heuristics with probabilistic AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3 Capability&lt;/strong&gt;: Confirms that Gemini 3 Pro handles complex spatial reasoning and 3D coordinate mapping significantly better than previous iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualization Impact&lt;/strong&gt;: React 19 and Three.js transform abstract data into visual insights, allowing for immediate safety verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physics Integration&lt;/strong&gt;: Real-time Center of Gravity (COG) calculations ensure that AI-generated plans adhere to physical safety standards.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>react</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Bringing A2UI to Google Workspace with Gemini</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Mon, 19 Jan 2026 05:54:51 +0000</pubDate>
      <link>https://dev.to/gde/bringing-a2ui-to-google-workspace-with-gemini-4m24</link>
      <guid>https://dev.to/gde/bringing-a2ui-to-google-workspace-with-gemini-4m24</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrp5nfgqzuggxtat755p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrp5nfgqzuggxtat755p.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article explores implementing the Agent-to-User Interface (A2UI) protocol within Google Apps Script. It demonstrates utilizing Gemini's structured output to render secure, dynamic, server-driven UIs—like booking forms and event lists—directly inside Google Sheets, streamlining workflows without complex external infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently published a sample implementation demonstrating how to bring the Agent-to-User Interface (A2UI) concepts to Google Apps Script (GAS). &lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A2UI is an emerging open-standard protocol designed to enable AI agents to generate rich, interactive user interfaces that render natively across web, mobile, and desktop environments. &lt;a href="https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Unlike traditional approaches that often require executing arbitrary, high-risk code to generate UI on the fly, A2UI leverages a strict schema-based data format to describe UI components. This "secure-by-design" architecture effectively mitigates security risks like Cross-Site Scripting (XSS) while ensuring high performance and cross-platform consistency.&lt;/p&gt;

&lt;p&gt;Integrating A2UI into the Google Workspace ecosystem represents a paradigm shift in user experience. It allows Large Language Model (LLM) driven agents to evolve beyond simple text-based chat. Instead, they provide dynamic, actionable components—such as forms, interactive charts, and data grids—directly within the user's workflow. In this article, I present a demonstration of A2UI functioning within Google Sheets, illustrating how AI agents can orchestrate complex, data-driven tasks through a native, structured interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The complete source code and documentation are available in the GitHub repository below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture and Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260119a%2Ffig2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260119a%2Ffig2.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaidchart.com/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNptk1tP20AQhf_KaJ-CGlxjQi77gJQmkBjRiwh5qSxViz0x29q77l5o0ij_vbNxQkHgF3t3vznnzNjeslwXyDiz-NujynEqRWlEnSmgS3inla8f0LTrRhgnc9kI5WAJwsLSvnc0D0fz-8-3QGqVLqEzTpbpyVtwFsDZeAGfRP4LVfGWGKd7BGupJIy_pW-JaQCmwgnoLB4Rnf04ERVpCUOGLb48vbycc0hV4x1Qm2YDHYzKqAsZu5aqAIPWCW9IzmbskHNORTMOpdZlhZHNjWxcZLyKGqNztDa0fheGZl3nUDKjknHKYUH2B58PcK91BVNcUQNOamVbdJy28nfovFFw7VUeToGyV8dwJbofL5K9MJlyuFpj7h1CETo36IzEJ1G1yPT0lbgRf_bYOyFdCEcevnIWVtrAMiVbhUaENM9RX8lNtHpCY_eEqOAe147aDG8YbhZfvxxd2pkfI6BtqHcE_fATc_d_woTcLOBKlVIhUaog5VZromsqwfBKDjThS06flG0qsYFio0Qt85C4MxGmsF24ldbRDV0enYQSYF1WGlkw7ozHLqvR1CIs2TZoZsw9Yo0Z4_RY4ErQFDKWqR2V0af1Xev6WGm0Lx8ZX4nK0so3NM7jn_K826afaK8c40nS34swvmVrxvvDKB4NzuNkmPTP-kmvyzaMn_VodzDqxXES90f9YW_XZX_3pnE0HPRGdPXi89HF-cVFsvsH5c4oIA" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The implementation of A2UI within Google Sheets follows a highly optimized lifecycle to ensure a smooth, agent-driven user experience.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User Input:&lt;/strong&gt; The process begins in the Google Spreadsheet dialog where a user enters a natural language request, such as "Find 3 Chinese restaurants in New York."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Transmission:&lt;/strong&gt; The HTML interface captures this input and sends it to the GAS backend. This is handled via the &lt;code&gt;google.script.run&lt;/code&gt; API, which creates a direct and secure bridge between the user interface and the server logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intent Routing:&lt;/strong&gt; The GAS backend forwards the request to the Gemini API. Using a specialized System Prompt, Gemini acts as a "Router" to determine which sub-application (e.g., Restaurant Finder or Event Manager) is best suited to handle the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Execution:&lt;/strong&gt; Once the sub-application is selected, GAS executes local functions to interact with your data. This may involve searching the current spreadsheet, fetching files from Google Drive, or checking Google Calendar availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2UI JSON Generation:&lt;/strong&gt; The data retrieved from the tools is sent back to Gemini. Gemini then generates a structured response that includes both conversational text and a JSON object strictly following the A2UI (Agent-to-User Interface) schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Side Rendering:&lt;/strong&gt; The GAS backend returns this payload to the HTML dialog. A dedicated JavaScript rendering engine within the dialog parses the A2UI JSON and dynamically builds high-quality UI components like interactive cards, lists with images, and booking forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Integration:&lt;/strong&gt; Unlike standard A2UI implementations that often rely on the A2A (Agent-to-Agent) protocol over HTTP, this GAS-optimized architecture eliminates external network overhead. By using the internal Google infrastructure, it provides a significantly faster response time for users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Architectural Comparison: Distributed vs. Integrated
&lt;/h2&gt;

&lt;p&gt;The official A2UI is designed as a "distributed" system for cross-platform interoperability. In contrast, the GAS-native version is an "integrated" system optimized specifically for the Google infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official A2UI Workflow (via A2A Protocol)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; The Frontend requests data from an A2A Server (e.g., Node.js/Python). The Server handles auth/protocol logic and requests JSON from the Gemini API. The generated JSON travels back through the server to the Frontend for rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Challenge:&lt;/strong&gt; Multiple network hops across the internet introduce latency and overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GAS Native Workflow (via google.script.run)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; The Frontend (HTML) directly calls a GAS Backend function using &lt;code&gt;google.script.run&lt;/code&gt;. The GAS Backend communicates with the Gemini API using &lt;code&gt;UrlFetchApp&lt;/code&gt; to get the UI JSON. The script returns the object directly to the client for immediate rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Innovation:&lt;/strong&gt; By using &lt;code&gt;google.script.run&lt;/code&gt;, the entire communication happens within Google's internal high-speed network. There is no need for an external A2A server, eliminating external network overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analysis: Pros and Cons of GAS-based A2UI
&lt;/h2&gt;

&lt;p&gt;Why should users choose the GAS-native approach for their AI applications?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Setup Cost &amp;amp; Rapid Development:&lt;/strong&gt; No servers to manage. Backend, frontend, and AI integration all live in a single GAS project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Google Integration:&lt;/strong&gt; It is incredibly easy to let the AI perform "real-world actions," such as using Google Sheets as a database or managing Google Calendar events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Performance:&lt;/strong&gt; Bypassing an external proxy server via &lt;code&gt;google.script.run&lt;/code&gt; ensures a snappier, more responsive user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual Rendering Logic:&lt;/strong&gt; While the official version provides out-of-the-box UI components, the GAS version requires you to maintain the logic that converts JSON to HTML elements (the &lt;code&gt;renderComponent&lt;/code&gt; code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Quotas:&lt;/strong&gt; GAS has specific limits, such as a 6-minute execution timeout and daily triggers, which may not suit high-scale enterprise apps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Application Setup
&lt;/h2&gt;

&lt;p&gt;To deploy this application in your own environment, please follow the steps below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will need a valid Gemini API Key to communicate with the LLM.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can copy the Google Spreadsheet containing the necessary Google Apps Script using the link below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1NdgN5e2l7-CTw-NTaP50Ta75l8Zbr93ATMyUOlZk1BY/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1NdgN5e2l7-CTw-NTaP50Ta75l8Zbr93ATMyUOlZk1BY/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After copying the Google Spreadsheet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor (Extensions &amp;gt; Apps Script).&lt;/li&gt;
&lt;li&gt;Locate the &lt;code&gt;main.gs&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Set your API key in the &lt;code&gt;apiKey&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Save the script.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, you can visit the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Google-Sheets" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; to manually copy the source codes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demonstration and Testing
&lt;/h2&gt;

&lt;p&gt;Once the script is set up, re-open the Google Spreadsheet (or refresh the page). You will see a table on the "data" sheet, which acts as the source for the event demonstration.&lt;/p&gt;

&lt;p&gt;A custom menu labeled "sample" will appear in the toolbar. Select &lt;strong&gt;sample &amp;gt; run&lt;/strong&gt; to open the A2UI dialog.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/aSetL-QF2I0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;In this demonstration, you can test the following two prompts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. "Find 3 Chinese restaurants in New York"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prompt replicates the official A2UI demonstration. It tests the system's ability to retrieve mock data, render a list of restaurants with images, and present a booking form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. "Show me events for Jan 17-20"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prompt demonstrates a custom integration with Google Workspace. The agent searches the spreadsheet for events within the specified date range and presents them as a selectable list. Upon confirmation, the agent triggers a function to register the selected events directly to your Google Calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This project confirms that the A2UI protocol can be effectively implemented within the Google Apps Script environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Architecture:&lt;/strong&gt; A2UI can be deployed using only GAS and Google Sheets, eliminating the need for external web servers or hosting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Workspace Integration:&lt;/strong&gt; The system seamlessly connects Generative AI with Workspace tools, allowing actions like reading Sheets and writing to Calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security:&lt;/strong&gt; By adhering to A2UI's schema-driven rendering, the application avoids the high risks associated with generating and executing arbitrary HTML/JS code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic User Experience:&lt;/strong&gt; Users receive interactive, app-like interfaces (forms, buttons, lists) directly within a chat dialog, vastly improving usability over plain text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability to other Apps:&lt;/strong&gt; This approach is not limited to Sheets; it can be readily adapted for Google Docs, Slides, and other Workspace applications to create a unified AI interface.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
      <category>a2ui</category>
    </item>
    <item>
      <title>Seamless Integration of Google Workspace and Gemini API via External URLs</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Thu, 15 Jan 2026 05:54:58 +0000</pubDate>
      <link>https://dev.to/gde/seamless-integration-of-google-workspace-and-gemini-api-via-external-urls-4he</link>
      <guid>https://dev.to/gde/seamless-integration-of-google-workspace-and-gemini-api-via-external-urls-4he</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxxj7ht1s9chmx576qp8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxxj7ht1s9chmx576qp8.jpg" alt="fig1" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;The Gemini API now supports external file URLs, allowing developers to process data directly without uploading it first. This article demonstrates how to leverage this update to integrate Google Workspace resources—including Google Sheets, Docs, Slides, and Apps Script—into Gemini’s workflow, covering both public and secure private access methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, the limitations regarding inline file data in the Gemini API have been significantly updated &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemini-api-new-file-limits/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. The maximum file size has increased from 20 MB to 100 MB. Furthermore, external file URLs—both public and signed—can now be used directly as input.&lt;/p&gt;

&lt;p&gt;The ability to use public external file URLs is particularly significant. Previously, data had to be uploaded to Gemini's servers first to generate an internal URI for processing. Eliminating this step substantially reduces processing overhead and latency.&lt;/p&gt;

&lt;p&gt;It is considered that this update effectively bridges the gap between the Gemini API and the Google Workspace ecosystem. This article investigates the technical specifications of these external file URLs and explores how they enhance interoperability with Google Sheets, Docs, Slides, and Google Apps Script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To test the samples provided in this article, you will need a valid Gemini API Key.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get an API Key here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integration with Google Workspace Files (Sheets, Docs, Slides)
&lt;/h2&gt;

&lt;p&gt;Google Sheets is a representative resource within Google Workspace. In this section, we demonstrate how to use Google Sheets (and by extension, Docs and Slides) as an external URL source for the Gemini API.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Setting up the Google Sheet
&lt;/h3&gt;

&lt;p&gt;Please create a new Google Spreadsheet and populate it with sample data. The image below shows the sample table used in this demonstration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiyfvb5nla0co882zo0v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiyfvb5nla0co882zo0v.jpg" alt="fig2" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the initial test, share this Google Spreadsheet publicly as "Anyone with the link can view". &lt;a href="https://support.google.com/a/users/answer/13309904?hl=en#sheets_share_link" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Copy the Spreadsheet ID from the URL bar.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Client Script for Public URLs
&lt;/h3&gt;

&lt;p&gt;Here, Node.js is used as the client environment. &lt;a href="https://ai.google.dev/gemini-api/docs/file-input-methods#external-urls" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please replace &lt;code&gt;###&lt;/code&gt; with your actual Spreadsheet ID in the &lt;code&gt;spreadsheetId&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPartFromUri&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;###&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Construct the URL to export the Sheet as a PDF&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;createPartFromUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Describe the PDF data.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this script is executed, the Gemini API directly accesses the PDF export URL of the Spreadsheet. The result confirms that the model can interpret the current state of the Spreadsheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The PDF contains a table for tracking customer feedback, including manual input and AI-generated analysis. The table consists of five columns: **Date**, **Purchase ID**, **Feedback**, **AI sentiment**, and **AI category**.

There are three recorded entries of customer feedback:

1.  **October 12, 2024 (ID: 82736481):** A highly positive review for a custom-made pendant, noted for its beauty and craftsmanship. The AI classifies the sentiment as **Positive** and the category as **Compliment**.
2.  **December 19, 2024 (ID: 77654822):** A request to exchange a pair of pearl earrings for a smaller size. The AI classifies the sentiment as **Neutral** and the category as an **Exchange request**.
3.  **December 24, 2024 (ID: 84435116):** A complaint regarding a silver chain that tarnished quickly, leading to a request for a full refund. The AI classifies the sentiment as **Negative** and the category as a **Return request**.

The bottom of the table includes four placeholder rows with generic labels ("yyyy/mm/dd", "##", "Feedback") for future entries.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Applying to Other Workspace Services
&lt;/h3&gt;

&lt;p&gt;This approach—using the export URL—is applicable to Google Sheets, Google Docs, and Google Slides. The URL patterns are as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Google Sheets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Or, for a specific sheet (gid)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;gid=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Docs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Or, for a specific tab&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;tab=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Slides:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/presentation/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;presentationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Handling Private Content
&lt;/h3&gt;

&lt;p&gt;In a production environment, you likely do not want to make your files public. If you attempt to use the URL of a private file directly, the API will return the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Cannot fetch content from the provided URL."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"INVALID_ARGUMENT"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resolve this, you can append an OAuth 2.0 access token to the URL query parameters. (The access token from the service account can also be used.) This allows the Gemini API to fetch the content with the necessary permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Google Sheets:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/spreadsheets&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/spreadsheets.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/documents&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/documents.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Slides:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/presentations&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/presentations.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/presentation/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;presentationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Utility Script: Retrieving an Access Token
&lt;/h4&gt;

&lt;p&gt;The following helper script uses the &lt;code&gt;gcloud&lt;/code&gt; CLI to retrieve a valid access token for testing purposes. Ensure &lt;code&gt;gcloud&lt;/code&gt; is installed and authenticated (&lt;code&gt;gcloud auth application-default login&lt;/code&gt;) before running this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAccessToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Check if gcloud CLI is installed&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gcloud --version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[Error] Google Cloud SDK (gcloud CLI) not found or failed to run.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please install it by following the official instructions:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cloud.google.com/sdk/gcloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Obtain access token&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gcloud auth print-access-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Error obtaining access token:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please ensure you are authenticated with gcloud CLI. Run 'gcloud auth application-default login'.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Content with Google Apps Script
&lt;/h2&gt;

&lt;p&gt;Google Apps Script (GAS) is a powerful tool for automation and dynamic content generation. By deploying a script as a Web App, we can create a dynamic external URL for the Gemini API.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting up the Web App
&lt;/h3&gt;

&lt;p&gt;In this example, we create a simple Web App that returns a specific text string. This simulates a dynamic data source.&lt;/p&gt;

&lt;p&gt;Create a standalone Google Apps Script file in Google Drive and paste the following code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufxdpv7h30djgd8azfks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufxdpv7h30djgd8azfks.jpg" alt="fig3" width="800" height="333"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The current keyword is 'apple'.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, deploy the script as a Web App:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt; as the type.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Anyone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL (&lt;code&gt;https://script.google.com/macros/s/###/exec&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note on Security:&lt;/strong&gt;&lt;br&gt;
If you wish to restrict access (e.g., set "Who has access" to &lt;strong&gt;Only myself&lt;/strong&gt;), you must append the access token to the Web App URL, similar to the private file examples above:&lt;br&gt;
&lt;code&gt;https://script.google.com/macros/s/###/exec?access_token=${accessToken}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on Updates:&lt;/strong&gt;&lt;br&gt;
When modifying the GAS code, you must create a new deployment version to reflect the changes in the Web App URL. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;. Additional details on GAS Web Apps can be found &lt;a href="https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Client Script for GAS Web Apps
&lt;/h3&gt;

&lt;p&gt;Set your Web App URL to the &lt;code&gt;uri&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPartFromUri&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://script.google.com/macros/s/###/exec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;createPartFromUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the current key word?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Testing Dynamic Updates
&lt;/h3&gt;

&lt;p&gt;When the script is run initially, the output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The current keyword is **apple**.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the dynamic nature of this integration, modify the GAS code:&lt;br&gt;
Change &lt;code&gt;const text = "The current keyword is 'apple'.";&lt;/code&gt; to &lt;code&gt;const text = "The current keyword is 'orange'.";&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Deploy a new version&lt;/strong&gt; of the Web App.&lt;/p&gt;

&lt;p&gt;Running the client script again yields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The current keyword is **orange**.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test confirms that the Gemini API always fetches data from the external URL in real-time. This capability is incredibly powerful. It allows Gemini to generate content based on live data feeds, latest news, or real-time calculations performed by Google Apps Script.&lt;/p&gt;

&lt;p&gt;Additionally, it is worth noting that GAS Web Apps typically require clients to follow HTTP redirects. &lt;a href="https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script?tab=readme-ov-file#request-to-web-apps" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. The success of this test indicates that the Gemini API's external URL fetcher correctly handles HTTP redirects, ensuring seamless integration with services like GAS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  The Gemini API now supports external file URLs (up to 100 MB), eliminating the need to upload files beforehand.&lt;/li&gt;
&lt;li&gt;  Google Workspace files (Sheets, Docs, Slides) can be directly ingested by Gemini using their PDF export URLs.&lt;/li&gt;
&lt;li&gt;  Private Workspace files can be accessed securely by appending a valid OAuth 2.0 access token to the URL parameters.&lt;/li&gt;
&lt;li&gt;  Google Apps Script Web Apps allow for the integration of dynamic, real-time data sources into Gemini's generation process.&lt;/li&gt;
&lt;li&gt;  The Gemini API correctly handles HTTP redirects, which is essential for working with Google Apps Script Web Apps and similar services.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Building Next-Gen AI Agents for Google Workspace: MCP, A2A, and A2UI with Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 10 Jan 2026 02:38:25 +0000</pubDate>
      <link>https://dev.to/gde/building-next-gen-ai-agents-for-google-workspace-mcp-a2a-and-a2ui-with-google-apps-script-299o</link>
      <guid>https://dev.to/gde/building-next-gen-ai-agents-for-google-workspace-mcp-a2a-and-a2ui-with-google-apps-script-299o</guid>
      <description>&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article demonstrates how to implement Model Context Protocol (MCP), Agent-to-Agent (A2A), and Agent-to-UI (A2UI) utilizing Google Apps Script (GAS). By leveraging GAS, developers can overcome authentication bottlenecks, enabling seamless integration of AI agents with Google Workspace data for powerful, autonomous workflows and dynamic user interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The ecosystem of AI agents is evolving rapidly. To enable these agents to process complex tasks effectively, the Model Context Protocol (MCP) has emerged as a critical standard. MCP allows AI models, such as Large Language Models (LLMs), to connect and interact with external data sources, tools, and software applications in a standardized manner. &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking ahead, Agent2Agent (A2A) and Agent-to-User Interface (A2UI) protocols are expected to play pivotal roles. A2A refers to the autonomous communication and interaction between multiple AI agents without direct human intervention. &lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Meanwhile, A2UI allows AI agents to construct interactive user interfaces using structured data (such as JSON) rather than static code. This enables an agent to generate a specific UI—like a restaurant booking form—on the fly, which the host application renders using native components. &lt;a href="https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integrating these technologies with Google Workspace unlocks limitless potential. Recently, a Gemini CLI extension including an MCP server for Workspace management was released. &lt;a href="https://github.com/gemini-cli-extensions/workspace" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; However, existing samples for MCP, A2A, and A2UI often face compatibility issues with Google Workspace, primarily due to complex scope authorization bottlenecks. Connecting from a local environment typically requires handling OAuth 2.0 or service accounts, which can hinder rapid development.&lt;/p&gt;

&lt;p&gt;Google Apps Script (GAS) offers a robust solution to this challenge. As the only low-code platform designed to integrate, automate, and extend Google Workspace, GAS handles scope authorization seamlessly upon execution. &lt;a href="https://workspace.google.com/intl/en/products/apps-script/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Consequently, leveraging GAS significantly simplifies the implementation of MCP, A2A, and A2UI. This article introduces practical implementations of these protocols using Google Apps Script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MCP &amp;amp; A2A Repository: &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/a2a-for-google-apps-script&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A2UI Repository: &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: MCP (Model Context Protocol)
&lt;/h2&gt;

&lt;p&gt;In this section, we will build an MCP server using Google Apps Script and test it using the Gemini CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js is installed on your local machine.&lt;/li&gt;
&lt;li&gt;A valid Gemini API Key. &lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Gemini CLI is installed. &lt;a href="https://geminicli.com/docs/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Server-Side Setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.1 Copy the Demo Script
&lt;/h4&gt;

&lt;p&gt;You can copy the A2A server template to your Google Drive by running the following GAS code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1mylSOTzCuui95Amk-kRufA9ffMqcnjqk8HSaoatoxaSPwkv-hg1q7uRC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFileById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;myFunction&lt;/code&gt; to copy the sample A2A server to your root folder.&lt;/p&gt;

&lt;p&gt;Alternatively, you can manually copy the scripts from the &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;. This example uses &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-server/sample2/a2a-server.js" rel="noopener noreferrer"&gt;a2a-server/sample2/a2a-server.js&lt;/a&gt;, which integrates 160 tools from &lt;a href="https://github.com/tanaikech/ToolsForMCPServer" rel="noopener noreferrer"&gt;ToolsForMCPServer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For developers wishing to use custom tools, a separate sample is available at &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-server/sample1/a2a-server.js" rel="noopener noreferrer"&gt;a2a-server/sample1/a2a-server.js&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 Deploy as Web App
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor in your copied project.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt; as the type.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Anyone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL (&lt;code&gt;https://script.google.com/macros/s/###/exec&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: Whenever you modify the code, you must deploy a new version to reflect the changes. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1.3 Configure the Script
&lt;/h4&gt;

&lt;p&gt;Locate the configuration object in your GAS project and update the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{apiKey}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your API key for using Gemini API.&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;models/gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Optional: Access key for requesting Web Apps.&lt;/span&gt;
  &lt;span class="na"&gt;webAppsUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://script.google.com/macros/s/###/exec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your deployed Web App URL.&lt;/span&gt;
  &lt;span class="c1"&gt;// logSpreadsheetId: "{spreadsheetId}", // Optional: Store logs in Google Spreadsheet.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set your Gemini API key in &lt;code&gt;apiKey&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set your access key (default is &lt;code&gt;sample&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Set your Web App URL in &lt;code&gt;webAppsUrl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After updating the script, remember to redeploy the Web App.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Client-Side Setup
&lt;/h3&gt;

&lt;p&gt;Navigate to the &lt;code&gt;.gemini&lt;/code&gt; directory on your local machine and update &lt;code&gt;settings.json&lt;/code&gt;. Replace &lt;code&gt;{Your value}&lt;/code&gt; with your specific details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"security"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"selectedType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{Your value}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{Your value}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gas_web_apps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://script.google.com/macros/s/{Your value}/exec?accessKey=sample"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the Gemini CLI and run the command &lt;code&gt;/mcp&lt;/code&gt;. You should see the connected status. You can now use the tools immediately because the authorization process was handled during the GAS Web App deployment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3262jrwl4qwdoi311c6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3262jrwl4qwdoi311c6.jpg" alt="loaded MCP tools" width="800" height="1953"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: A2A (Agent-to-Agent)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Server-Side Setup
&lt;/h3&gt;

&lt;p&gt;If you have successfully completed the MCP test above, your server is ready. The GAS Web App configured in the previous section functions as both an MCP server and an A2A server. No further server-side configuration is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Client-Side Setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.1 Clone Repository and Install Dependencies
&lt;/h4&gt;

&lt;p&gt;Execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tanaikech/a2a-for-google-apps-script
&lt;span class="nb"&gt;cd &lt;/span&gt;a2a-for-google-apps-script/a2a-client
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a2a-client&lt;/code&gt;: Node.js sample client scripts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a2a-server&lt;/code&gt;: Google Apps Script Web App server samples.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.2 The Proxy Script
&lt;/h4&gt;

&lt;p&gt;The current &lt;code&gt;@a2a-js/sdk&lt;/code&gt; version automatically strips query parameters and redirects to a &lt;code&gt;/.well-known/&lt;/code&gt; path. To resolve this, the provided &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-client/clientGas.js" rel="noopener noreferrer"&gt;&lt;code&gt;clientGas.js&lt;/code&gt;&lt;/a&gt; acts as a proxy. It intercepts the fetch API, allowing the SDK to communicate correctly with GAS's dynamic URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication Note:&lt;/strong&gt; This script uses the Google Cloud SDK (&lt;code&gt;gcloud CLI&lt;/code&gt;) to retrieve access tokens. Ensure you have authorized the &lt;code&gt;https://www.googleapis.com/auth/drive.readonly&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/drive&lt;/code&gt; scope. This is required to access the GAS Web App path &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 Set Environment Variables
&lt;/h4&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the &lt;code&gt;a2a-client&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GEMINI_API_KEY="YOUR_KEY"
GEMINI_MODEL="gemini-3-flash-preview"
A2A_WEB_APPS_URL="https://script.google.com/macros/s/{Your value}/exec?accessKey=sample"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure &lt;code&gt;A2A_WEB_APPS_URL&lt;/code&gt; includes your access key as a query parameter. This sample uses a single A2A server. To use multiple servers, define additional variables and register them using &lt;code&gt;new FunctionTool&lt;/code&gt; in the client script.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 Testing
&lt;/h4&gt;

&lt;h4&gt;
  
  
  Terminal Test
&lt;/h4&gt;

&lt;p&gt;Run the basic connectivity test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run test1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt: How much is 10 yen in USD?
Response: 10 yen is approximately **0.0641 USD**...
Prompt: What is the weather forecast for Tokyo?
Response: The weather forecast for Tokyo... is clear sky.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Browser/ADK Test
&lt;/h4&gt;

&lt;p&gt;This test utilizes the Agent Development Kit (ADK) to provide a chat interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run test2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8000&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Simple Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to cook miso soup.
To achieve this goal, create a new Google Spreadsheet,
generate a roadmap for cooking miso soup in the spreadsheet,
and return the Spreadsheet URL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser demonstrates the agent processing the prompt via the A2A server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncrbas8octvbfycoz4j9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncrbas8octvbfycoz4j9.gif" alt="A2A sample" width="1752" height="1053"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting roadmap in the Spreadsheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvd8hs42yfe5oh4oivm9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvd8hs42yfe5oh4oivm9.jpg" alt="A2A sample" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2: Complex Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write a comprehensive article about developing Google Apps Script (GAS) using generative AI.
The article should include an introductory overview, formatted lists for best practices,
and a table comparing different AI-assisted coding techniques.
Once generated, please create a new Google Document, insert the content, convert the Google Document to a PDF file,
and send an email to `tanaike@hotmail.com` including the shareable URL of the PDF file by giving a suitable title and email body.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser visualizes the processing steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ixu14jmuh8ac58zhmy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ixu14jmuh8ac58zhmy.gif" alt="A2A sample" width="760" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This task requires coordination between the Docs API, Drive API, and Gmail API. While this might trigger a Tool Selection Issue (TSI) in standard environments, the GAS A2A server handles the deterministic selection seamlessly.&lt;/p&gt;

&lt;p&gt;The generated PDF:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qgklpvz6062orj10deb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qgklpvz6062orj10deb.jpg" alt="A2A sample" width="800" height="3105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The email received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dear Tanaike,

Please find attached the PDF document containing the comprehensive article on "Leveraging Generative AI for Google Apps Script Development". The shareable URL for the PDF is https://drive.google.com/file/d/###/view?usp=sharing.

Best regards,
Your AI Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: A2UI (Agent-to-User Interface)
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/google/A2UI/tree/d7996656ef2bc0cdffad499452fc5b282d878d45/samples/agent/adk/restaurant_finder" rel="noopener noreferrer"&gt;official A2UI sample (Restaurant finder)&lt;/a&gt;, the client communicates via A2A protocols. In this GAS-optimized implementation, we reduce HTTP overhead by allowing the client (HTML) to communicate directly with the AI agent using &lt;code&gt;google.script.run&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Sample Script
&lt;/h3&gt;

&lt;p&gt;Copy the Google Spreadsheet containing the necessary scripts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1csYUJO8LzcEFPkt_ickIkdsGZsvim6lb1OEQZHUkB3c/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1csYUJO8LzcEFPkt_ickIkdsGZsvim6lb1OEQZHUkB3c/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After copying, open the script editor and set your &lt;code&gt;apiKey&lt;/code&gt; in &lt;code&gt;main.gs&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, access the code via the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Deploy as Web App
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor in your project.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Only myself&lt;/strong&gt;. (Restrict access to yourself for testing as the owner).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: You can use the dev mode URL (&lt;code&gt;.../dev&lt;/code&gt;) for testing. Updates require a new deployment. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Sample 1: Restaurant Finder
&lt;/h4&gt;

&lt;p&gt;This test reproduces the official "A2UI Restaurant finder" sample.&lt;/p&gt;

&lt;p&gt;Open your Web App URL. You will see the interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5n09qv5htdsuahl66po.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5n09qv5htdsuahl66po.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: "Find 3 Chinese restaurants in New York" and click &lt;strong&gt;Send&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6nmc0w6wioe8v757oor.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6nmc0w6wioe8v757oor.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Book Now&lt;/strong&gt; for "Han Dynasty". The agent generates a reservation UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a4gfz25sj1we6uczxow.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a4gfz25sj1we6uczxow.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the details and click &lt;strong&gt;Submit Reservation&lt;/strong&gt;. The server processes the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizccfmzr0k2h2j23k0vr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizccfmzr0k2h2j23k0vr.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Spreadsheet Data:&lt;/strong&gt;&lt;br&gt;
While the sample uses hard-coded data in &lt;code&gt;executeGetRestaurants&lt;/code&gt;, GAS allows you to fetch this from a Spreadsheet easily. If you have a sheet formatted like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frukywp0wxip74ks7g2hh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frukywp0wxip74ks7g2hh.jpg" alt="A2UI sample" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can replace the data source with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allRestaurants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sheet1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getDisplayValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sample 2: Budget Simulator
&lt;/h4&gt;

&lt;p&gt;Copy the Budget Simulator Spreadsheet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1HEfmSD9WMqQfy39aEZEjz7ggFeiZIx0_b2oKkrReEpk/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1HEfmSD9WMqQfy39aEZEjz7ggFeiZIx0_b2oKkrReEpk/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set your &lt;code&gt;apiKey&lt;/code&gt; in &lt;code&gt;main.gs&lt;/code&gt; after copying.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sample implements an "Interactive smart household account book and budget simulator". Ensure "Sheet1" contains the initial budget data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y4ujlf4ri55zx91bb2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y4ujlf4ri55zx91bb2.jpg" alt="A2UI sample" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deploy as a Web App and open the URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g01qj96nqiomi3r6hpu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g01qj96nqiomi3r6hpu.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: &lt;code&gt;Check this month's budget&lt;/code&gt;. A2UI displays a pie chart and category list based on the spreadsheet data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv2rcnt8bf1cm81w9gl6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv2rcnt8bf1cm81w9gl6.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: "What would happen if I reduced my dining out expenses by 10,000 yen and put the money towards savings?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zp59iz1sovqm0zeex9u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zp59iz1sovqm0zeex9u.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The graph updates to simulate the proposal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoycha5ujtmsblmr13y9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoycha5ujtmsblmr13y9.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Update sheet with this budget proposal&lt;/strong&gt;. GAS updates the cells, and a notification confirms the action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsepnrv3g52iebyx5vvf5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsepnrv3g52iebyx5vvf5.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new data is appended to "Sheet1":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczmr0qmgq9z58m1zr59x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczmr0qmgq9z58m1zr59x.jpg" alt="A2UI sample" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MCP, A2A, and A2UI represent the future of AI interoperability, enabling agents to connect with tools, collaborate with each other, and generate dynamic user interfaces.&lt;/li&gt;
&lt;li&gt;Google Apps Script provides a unique advantage by resolving complex OAuth 2.0 authorization bottlenecks native to local environments.&lt;/li&gt;
&lt;li&gt;Implementing MCP servers on GAS allows for seamless integration between the Gemini CLI and Google Workspace data.&lt;/li&gt;
&lt;li&gt;A2A on GAS facilitates autonomous multi-agent workflows, capable of executing complex tasks like generating documents and sending emails without human intervention.&lt;/li&gt;
&lt;li&gt;A2UI combined with GAS allows agents to build interactive, data-driven interfaces on the fly, directly connected to live Spreadsheet data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Appendix
&lt;/h2&gt;

&lt;p&gt;I am exploring taking advantage of Google Apps Script. The summary of this can be seen &lt;a href="https://github.com/tanaikech/taking-advantage-of-google-apps-script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/taking-advantage-of-google-apps-script&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>mcp</category>
      <category>a2a</category>
      <category>googleworkspace</category>
    </item>
  </channel>
</rss>
