DEV Community

xbill for Google Developer Experts

Posted on • Originally published at Medium on

Firestore MCP Development with Kotlin, Cloud Run, and Gemini CLI

Leveraging Gemini CLI and the underlying Gemini LLM to build Model Context Protocol (MCP) AI applications with Kotlin deployed to Google Cloud Run.

Why not just use Python?

Python has traditionally been the main coding language for ML and AI tools. One of the strengths of the MCP protocol is that the actual implementation details are independent of the development language. The reality is that not every project is coded in Python- and MCP allows you to use the latest AI approaches with other coding languages. The goal of this article is to provide a minimal viable basic working MCP stdio server in Kotlin that can be run locally without any unneeded extra code or extensions.

What is Kotlin?

Kotlin is a modern, statically typed, general-purpose programming language developed by JetBrains that runs on the Java Virtual Machine (JVM). It is primarily known as the preferred language for Android app development , as announced by Google in 2019.

Kotlin MCP Documentation

Kotlin has an official MCP SDK implementation:

GitHub - modelcontextprotocol/kotlin-sdk: The official Kotlin SDK for Model Context Protocol servers and clients. Maintained in collaboration with JetBrains

Kotlin Version Management

One of the downsides of the wide deployment of Java and Java based tools has been managing the language versions across platforms and maintaining supported versions.

There are several Version Managers available- some may be more suited to your specific environment than others. A good all-around Java Version Manager is SDKMAN:

Home | SDKMAN! the Software Development Kit Manager

The SDKMAN tool is used to download the Java system version- typically this will be a Temurin build. As of writingโ€Šโ€”โ€Šthe mainstream Java version is Java 25.

To validate your current version:

xbill@penguin:~$ java --version
openjdk 25.0.1 2025-10-21 LTS
OpenJDK Runtime Environment Temurin-25.0.1+8 (build 25.0.1+8-LTS)
OpenJDK 64-Bit Server VM Temurin-25.0.1+8 (build 25.0.1+8-LTS, mixed mode, sharing)
xbill@penguin:~$
Enter fullscreen mode Exit fullscreen mode

Install System Packages

Use SDKMAN or another package manager to install the dependent packagesโ€Šโ€”โ€Šincluding Kotlin, Java, Gradle, and Maven:

xbill@penguin:~$ sdk install kotlin
kotlin 2.3.0 is already installed.
xbill@penguin:~$ sdk install gradle
gradle 9.2.1 is already installed.
xbill@penguin:~$ sdk install java
java 25.0.1-tem is already installed.
xbill@penguin:~$
xbill@penguin:~$ sdk install maven
maven 3.9.12 is already installed.
Enter fullscreen mode Exit fullscreen mode

What is Firestore?

Google Firestore, also known as Cloud Firestore is a part of the Google Firebase application development platform. It is fundamentally a cloud-hosted NoSQL database for storing and syncing data. Firestore can be directly accessed by mobile and web applications through native SDKs.

Firestore | Firebase

Gemini CLI

If not pre-installed you can download the Gemini CLI to interact with the source files and provide real-time assistance:

npm install -g @google/gemini-cli
Enter fullscreen mode Exit fullscreen mode

Testing the Gemini CLI Environment

Once you have all the tools and the correct Node.js version in place- you can test the startup of Gemini CLI. You will need to authenticate with a Key or your Google Account:

gemini
Enter fullscreen mode Exit fullscreen mode

Node Version Management

Gemini CLI needs a consistent, up to date version of Node. The nvm command can be used to get a standard Node environment:

GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions

Gemini CLI needs a minimum Node version of 20โ€Šโ€”โ€Šbut as of writing the mainstream Node version is 25.

Where do I start?

The strategy for starting MCP development is a incremental step by step approach.

First, the basic development environment is setup with the required system variables, and a working Gemini CLI configuration.

Then, a minimal Hello World Style Kotlin MCP Server is built with HTTP transport. This server is validated with Gemini CLI in the local environment.

This setup validates the connection from Gemini CLI to the local process via MCP. The MCP client (Gemini CLI) and the Kotlin MCP server both run in the same local environment.

Next- the basic MCP server is wrapped in a container and deployed to Google Cloud Run.

This web deployed endpoint is then validated with the local Gemini CLI installation.

Setup the Basic Environment

At this point you should have a working Kotlin environment and a working Gemini CLI installation. The next step is to clone the GitHub samples repository with support scripts:

cd ~
git clone https://github.com/xbill9/gemini-cli-codeassist
Enter fullscreen mode Exit fullscreen mode

Then run init.sh from the cloned directory.

The script will attempt to determine your shell environment and set the correct variables:

cd gemini-cli-codeassist
source init.sh
Enter fullscreen mode Exit fullscreen mode

If your session times out or you need to re-authenticate- you can run the set_env.sh script to reset your environment variables:

cd gemini-cli-codeassist
source set_env.sh
Enter fullscreen mode Exit fullscreen mode

Variables like PROJECT_ID need to be setup for use in the various build scripts- so the set_env script can be used to reset the environment if you time-out.

Hello World with HTTP SSE Transport

One of the key features that the standard MCP libraries provide is abstracting various transport methods.

The high level MCP tool implementation is the same no matter what low level transport channel/method that the MCP Client uses to connect to a MCP Server.

The simplest transport that the SDK supports is the stdio (stdio/stdout) transportโ€Šโ€”โ€Šwhich connects a locally running process. Both the MCP client and MCP Server must be running in the same environment.

The HTTP SSE streaming transport allows the MCP client and server to be in the same environment or distributed over the Internet.

Key Kotlin Configuration Files

The key files for the Kotlin build are reviewed with Gemini CLI:

cd ~/gemini-cli-codeassist/mcp-https-kotlin
gemini

โœฆ I have completed the review and refactoring of the project to adhere to Kotlin best practices.

  Summary of Findings and Improvements

   1. Architecture & Separation of Concerns:
       * Repository Pattern: The data access logic was decoupled from the server implementation (Main.kt) by introducing a
         dedicated repository layer.
       * Interface Segregation: An InventoryRepository interface was created to define the contract, with
         FirestoreInventoryRepository providing the specific implementation. This makes the code more testable and modular.
       * Data Models: The Product data class was moved to its own file (model/Product.kt) to keep the codebase organized.

   2. Code Cleanliness & Safety:
       * Global State Removed: Mutable global variables for the database connection were replaced with dependency injection,
         passing the repository instance to the application module.
       * Error Handling: A safeToolCall helper function was introduced in Main.kt to standardize exception handling and logging
         across all MCP tools, significantly reducing boilerplate and improving reliability.
       * Constants: Magic numbers (like batch size) were moved to companion object constants.

   3. Testing:
       * Unit Testing: The ApplicationTest.kt was updated to use mockk for mocking the InventoryRepository. This allows testing
         the server logic without requiring a live Firestore connection.

   4. Linting & Style:
       * KtLint Integration: The ktlint plugin was added and configured.
       * Style Enforcement: The codebase was formatted to adhere to standard Kotlin coding conventions (e.g., removing wildcard
         imports, fixing indentation, naming conventions, and line lengths). All files now pass ktlintCheck.

  The project is now well-structured, testable, and follows modern Kotlin development standards.

Enter fullscreen mode Exit fullscreen mode

So How Does this all Fit Together?

Kotlin is a bit different than many other languages- a combination of settings and conventions allow the application to be auto configured as a MCP stdio server.

Gemini CLI was used to generate a high level summary of the project:

> what does this project do?

โœฆ This project is a Kotlin-based Model Context Protocol (MCP) server that manages a product inventory for a fictional store
  called "Cymbal Superstore". It is designed to be consumed by MCP clients (like Claude Desktop or Gemini-powered IDE
  extensions).

  Core Functionality

   1. Inventory Management:
       * It uses Google Cloud Firestore as its database to store product information.
       * It exposes tools to list all products, get details for a specific product by ID, and search for products by name.
       * It includes tools to seed the database with initial sample data and reset (clear) the database.

   2. Communication Protocol:
       * It implements the Model Context Protocol (MCP) using the official Kotlin SDK.
       * It communicates over HTTP using Server-Sent Events (SSE) for real-time updates and message handling.
       * It runs a Ktor server (Netty engine) on port 8080.

   3. Deployment:
       * It is containerized using Dockerfile and includes configuration (cloudbuild.yaml) for deployment to Google Cloud Run.

  Available Tools (for MCP Clients)

   * greet: Returns a simple greeting.
   * get_products: Returns a JSON list of all products in the inventory.
   * get_product_by_id: Returns details for a specific product ID.
   * search: specific product search by name.
   * seed: Populates the Firestore database with sample products.
   * reset: Deletes all products from the Firestore database.
   * get_root: Returns a welcome message.
Enter fullscreen mode Exit fullscreen mode

Running the Kotlin Code

First- switch the directory with the Kotlin MCP sample code:

cd ~/gemini-cli-codeassist/firestore-https-kotlin
Enter fullscreen mode Exit fullscreen mode

Run the release version on the local system:

xbill@penguin:~/gemini-cli-codeassist/firestore-https-kotlin$ make release
./gradlew ktlintFormat
Calculating task graph as no cached configuration is available for tasks: ktlintFormat

BUILD SUCCESSFUL in 633ms
7 actionable tasks: 7 up-to-date
Configuration cache entry stored.
./gradlew build
Calculating task graph as no cached configuration is available for tasks: build

BUILD SUCCESSFUL in 1s
15 actionable tasks: 15 up-to-date
Configuration cache entry stored.
./gradlew ktlintCheck
Calculating task graph as no cached configuration is available for tasks: ktlintCheck

BUILD SUCCESSFUL in 531ms
7 actionable tasks: 7 up-to-date
Configuration cache entry stored.
xbill@penguin:~/gemini-cli-codeassist/firestore-https-kotlin$
Enter fullscreen mode Exit fullscreen mode

Gemini CLI settings.json

The default Gemini CLI settings.json has an entry for the Kotlin source:

{
  "mcpServers": {
    "firestore-https-kotlin": {
      "url": "http://127.0.0.1:8080/sse"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Start the Local Server

The HTTP/SSE server runs locally in the current environment:

xbill@penguin:~/gemini-cli-codeassist/firestore-https-kotlin$ make run
./gradlew run
Calculating task graph as no cached configuration is available for tasks: run

> Task :run

{"@timestamp":"2026-01-07T13:53:42.681409269-05:00","@version":"1","message":"Firestore connected successfully.","logger_name":"InventoryRepository","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.888189958-05:00","@version":"1","message":"Autoreload is disabled because the development mode is off.","logger_name":"io.ktor.server.Application","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.959378381-05:00","@version":"1","message":"Adding Tool: \"greet\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.960184729-05:00","@version":"1","message":"Added Tool: \"greet\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.960407991-05:00","@version":"1","message":"Notifying listeners on feature update","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.965291526-05:00","@version":"1","message":"Emitting notification 1767812022962: ToolListChangedNotification(params=null)","logger_name":"io.modelcontextprotocol.kotlin.sdk.server.FeatureNotificationService","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969081387-05:00","@version":"1","message":"Adding Tool: \"get_products\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.96924545-05:00","@version":"1","message":"Added Tool: \"get_products\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969308295-05:00","@version":"1","message":"Notifying listeners on feature update","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969387804-05:00","@version":"1","message":"Emitting notification 1767812022969: ToolListChangedNotification(params=null)","logger_name":"io.modelcontextprotocol.kotlin.sdk.server.FeatureNotificationService","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969810602-05:00","@version":"1","message":"Adding Tool: \"get_product_by_id\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969924103-05:00","@version":"1","message":"Added Tool: \"get_product_by_id\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.969969349-05:00","@version":"1","message":"Notifying listeners on feature update","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.970040747-05:00","@version":"1","message":"Emitting notification 1767812022970: ToolListChangedNotification(params=null)","logger_name":"io.modelcontextprotocol.kotlin.sdk.server.FeatureNotificationService","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.970545777-05:00","@version":"1","message":"Adding Tool: \"search\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
{"@timestamp":"2026-01-07T13:53:42.970651882-05:00","@version":"1","message":"Added Tool: \"search\"","logger_name":"FeatureRegistry[Tool]","thread_name":"main","level":"INFO","level_value":20000}
Enter fullscreen mode Exit fullscreen mode

Validation with Gemini CLI

Finally- open a new terminal and start Gemini CLI. The MCP connection over HTTP/SSE to the code is validated, The full Gemini CLI Session will start:

> /mcp list

Configured MCP servers:

๐ŸŸข firestore-https-kotlin - Ready (7 tools)
  Tools:
  - get_product_by_id
  - get_products
  - get_root
  - greet
  - reset
  - search
  - seed

> search coffee

โœฆ I will search for products matching "coffee" in the inventory.

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ? search (firestore-https-kotlin MCP Server) {"query":"coffee"} โ† โ”‚
โ”‚ โ”‚
โ”‚ MCP Server: firestore-https-kotlin โ”‚
โ”‚ Tool: search โ”‚
โ”‚ โ”‚
โ”‚ Allow execution of MCP tool "search" from server "firestore-https-kotlin"? โ”‚
โ”‚ โ”‚
โ”‚ 1. Allow once โ”‚
โ”‚ 2. Allow tool for this session โ”‚
โ”‚ โ— 3. Allow all server tools for this session โ”‚
โ”‚ 4. No, suggest changes (esc) โ”‚
โ”‚ โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ โœ“ search (firestore-https-kotlin MCP Server) {"query":"coffee"} โ”‚
โ”‚ โ”‚
โ”‚ [{"id":"37kWbZHTwg5nbrSD13PB","name":"Coffee โ”‚
โ”‚ Beans","price":6.0,"quantity":375,"imgfile":"product-images/coffeebeans.png","timestamp":"2024-11-30T08:40:02.515Z","actualda โ”‚
โ”‚ teadded":"2026-01-07T16:53:00.672Z"}] โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
โœฆ Found 1 product: Coffee Beans (ID: 37kWbZHTwg5nbrSD13PB).
Enter fullscreen mode Exit fullscreen mode

Deploying to Cloud Run

After the local HTTP version of the MCP server has been tested locallyโ€Šโ€”โ€Šit can be deployed remotely to Google Cloud Run.

First- switch to the directory with the HTTP MCP sample code:

cd ~/gemini-cli-codeassist/firestore-https-kotlin
Enter fullscreen mode Exit fullscreen mode

Deploy the project to Google Cloud Run with the pre-built cloudbuild.yaml and Dockerfile:

cd ~/gemini-cli-codeassist/firestore-https-kotlin
xbill@penguin:~/gemini-cli-codeassist/firestore-https-kotlin$ make deploy
Enter fullscreen mode Exit fullscreen mode

The Cloud Build will start:

xbill@penguin:~/gemini-cli-codeassist/firestore-https-kotlin$ make deploy
gcloud builds submit . --config cloudbuild.yaml
Creating temporary archive of 24 file(s) totalling 91.3 KiB before compression.
Some files were not included in the source upload.

Check the gcloud log [/home/xbill/.config/gcloud/logs/2026.01.07/13.56.20.201957.log] to see which files and the contents of the
default gcloudignore file used (see `$ gcloud topic gcloudignore` to learn
more).

Uploading tarball of [.] to [gs://comglitn_cloudbuild/source/1767812180.567142-732aba1ab900451bae818150d30c57f3.tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/comglitn/locations/global/builds/94f843ed-6482-4d68-84e8-1300c6c0375a].
Logs are available at [https://console.cloud.google.com/cloud-build/builds/94f843ed-6482-4d68-84e8-1300c6c0375a?project=1056842563084].
Waiting for build to complete. Polling interval: 1 second(s).
Enter fullscreen mode Exit fullscreen mode

It can take 5โ€“10 minutes to complete the build.

When the build is complete- an endpoint will be returned:

โ”‚ Starting Step #2 โ”‚
โ”‚ Step #2: Already have image (with digest): gcr.io/cloud-builders/gcloud โ”‚
โ”‚ Step #2: Deploying container to Cloud Run service [mcp-https-kotlin] in project [comglitn] region [us-central1] โ”‚
โ”‚ Step #2: Deploying... โ”‚
โ”‚ Step #2: Setting IAM Policy...........done โ”‚
โ”‚ Step #2: Creating Revision...................................................................................done โ”‚
โ”‚ Step #2: Routing traffic.....done โ”‚
โ”‚ Step #2: Done. โ”‚
โ”‚ Step #2: Service [mcp-https-kotlin] revision [mcp-https-kotlin-00002-kvc] has been deployed and is serving 100 percent of โ”‚
โ”‚ traffic. โ”‚
โ”‚ Step #2: Service URL: https://mcp-https-kotlin-1056842563084.us-central1.run.app โ”‚
โ”‚ Finished Step #2 โ”‚
Enter fullscreen mode Exit fullscreen mode

The service endpoint in this example is :

https://firestore-https-kotlin-1056842563084.us-central1.run.app
Enter fullscreen mode Exit fullscreen mode

The actual endpoint will vary based on your project settings.

Review Service in Cloud Run

Navigate to the Google Cloud console and search for Cloud Run -

and then you can detailed information on the Cloud Run Service:

Cloud Logging

The remote server writes logs to stderr in standard JSON format. These logs are available from the deployed Cloud Run Service:

Validate HTTP connection

Once you have the Endpointโ€Šโ€”โ€Šyou can attempt a connection- navigate to in your browser:

https://firestore-https-kotlin-1056842563084.us-central1.run.app
Enter fullscreen mode Exit fullscreen mode

You will need to adjust the exact URL to match the URL returned from Cloud Build.

You will get an error- this connection is expecting a message in the MCP format:

Not Found
Enter fullscreen mode Exit fullscreen mode

Gemini CLI settings.json.cloudrun

Replace the default Gemini CLI configuration fileโ€Šโ€”โ€Š settings.json with a pre-configured sample- settings.json.cloudrun to use the Cloud Run version of the connection:

{
  "mcpServers": {
    "firestore-cloudrun-kotlin": {
      "url": "https://firestore-https-kotlin-1056842563084.us-central1.run.app/sse"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Copy the Cloud Run version of the Gemini CLI configuration file:

xbill@penguin:~/gemini-cli-codeassist/mcp-https-kotlin$ cd .gemini
cp settings.json.cloudrun settings.json
xbill@penguin:~/gemini-cli-codeassist/mcp-https-kotlin/.gemini$
Enter fullscreen mode Exit fullscreen mode

To cross check that you are using the Cloud Run end point- make sure that the local HTTP server is not active in your environment.

Validation with Gemini CLI

The final connection test uses Gemini CLI as a MCP client with the deployed Cloud Run Service providing the MCP server. Startup Gemini CLI with the updated settings :

gemini

> /mcp list

Configured MCP servers:

๐ŸŸข firestore-cloudrun-kotlin - Ready (7 tools)
  Tools:
  - get_product_by_id
  - get_products
  - get_root
  - greet
  - reset
  - search
  - seed

> get_products

โœฆ I will retrieve the list of products from the inventory database.
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ? get_products (firestore-cloudrun-kotlin MCP Server) {} โ† โ”‚
โ”‚ โ”‚
โ”‚ MCP Server: firestore-cloudrun-kotlin โ”‚
โ”‚ Tool: get_products โ”‚
โ”‚ โ”‚
โ”‚ Allow execution of MCP tool "get_products" from server "firestore-cloudrun-kotlin"? โ”‚
โ”‚ โ”‚
โ”‚ 1. Allow once โ”‚
โ”‚ 2. Allow tool for this session โ”‚
โ”‚ โ— 3. Allow all server tools for this session โ”‚
โ”‚ 4. No, suggest changes (esc) โ”‚
โ”‚ โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

โœฆ I have retrieved the product list from the inventory. How would you like to proceed?
Enter fullscreen mode Exit fullscreen mode

Extend MCP Tools

Once the code has been deployed to Cloud Runโ€Šโ€”โ€Šthe MCP tools are available in the local Gemini CLI context:

> build a menu that optimizes profit by using the MCP tools to get current 
inventory and price and quantity
Enter fullscreen mode Exit fullscreen mode

Redeploy the updates to Cloud Run:

make deploy
Enter fullscreen mode Exit fullscreen mode

And then Validate the new MCP tool:

> /mcp list

Configured MCP servers:

๐ŸŸข firestore-cloudrun-kotlin - Ready (8 tools)
  Tools:
 - get_revenue_menu

 I will retrieve the revenue-optimized menu from the inventory.
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ? get_revenue_menu (firestore-cloudrun-kotlin MCP Server) {} โ† โ”‚
โ”‚ โ”‚
โ”‚ MCP Server: firestore-cloudrun-kotlin โ”‚
โ”‚ Tool: get_revenue_menu โ”‚
โ”‚ โ”‚
โ”‚ Allow execution of MCP tool "get_revenue_menu" from server "firestore-cloudrun-kotlin"? โ”‚
โ”‚ โ”‚
โ”‚ 1. Allow once โ”‚
โ”‚ 2. Allow tool for this session โ”‚
โ”‚ โ— 3. Allow all server tools for this session โ”‚
โ”‚ 4. No, suggest changes (esc) โ”‚
โ”‚ โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
โœฆ Here is the revenue-optimized menu:

   * Beef: $10.00 (Quantity: 238)
   * Whole Wheat Bread: $10.00 (Quantity: 328)
   * Cola: $10.00 (Quantity: 93)
   * Whole Chicken: $10.00 (Quantity: 200)
   * Mint Chocolate Cookies: $9.00 (Quantity: 215)
   * Pineapple Kombucha: $8.00 (Quantity: 402)
   * Fresh Basil: $8.00 (Quantity: 81)
   * Walnuts: $7.00 (Quantity: 2)
   * Watermelon: $7.00 (Quantity: 279)
   * Green Tea: $6.00 (Quantity: 474)
Enter fullscreen mode Exit fullscreen mode

This new MCP tool can be used directly in the Gemini CLI context:

> what dishes will work with the revenue optimize menu

โœฆ Based on the ingredients found in the revenue-optimized menu, here are a few dish suggestions that combine these high-value
  items:

  Main Courses
   * Herbed Roast Chicken Sandwich: Use the Whole Chicken (roasted) and Fresh Basil served on Whole Wheat Bread.
   * Steak & Walnut Sandwich: Seared Beef paired with crushed Walnuts for texture, served on Whole Wheat Bread.
   * Basil Beef: Beef sautรฉed with Fresh Basil (best if you have basic pantry staples like oil and garlic), served with a side
     of Whole Wheat Bread.

  Sides & Snacks
   * Summer Refresh Platter: Slices of Watermelon sprinkled with Walnuts and torn Fresh Basil.

  Dessert & Drinks
   * Mint & Tea Pairing: Mint Chocolate Cookies served with hot or iced Green Tea.
   * Kombucha Float: Pineapple Kombucha (served over ice or with a scoop of vanilla ice cream if available).
   * Classic Combo: Cola served with the Beef or Chicken sandwiches.
Enter fullscreen mode Exit fullscreen mode

Summary

MCP development with Kotlin using Gemini CLI was validated with an incremental step by step approach.

A minimal streaming HTTP/SSE transport MCP Server was built from source code and validated with Gemini CLI running as a MCP client in the same local environment.

Thenโ€Šโ€”โ€Šthe MCP server was wrapped in a container and submitted to Google Cloud Build for deployment to Google Cloud Run. The remote MCP server was validated with a standard browser, and Gemini CLI.

Finally- remote MCP operations were performed from the local Gemini CLI installation to the MCP server hosted in Google Cloud Run.

This approach can be extended to more complex deployments and Cloud based options.

Top comments (0)