diff --git a/demo-applications/spring-boot-postgres/README.md b/demo-applications/spring-boot-postgres/README.md
new file mode 100644
index 0000000..4882024
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/README.md
@@ -0,0 +1,156 @@
+# Spring Boot MCP Toolbox PostgreSQL Example 🚀
+
+This sample application demonstrates how to build a modern **Spring Boot 3** microservice integrated with the **MCP Toolbox Java SDK** (`com.google.cloud.mcp:mcp-toolbox-sdk-java`).
+
+The application connects to an official [MCP Toolbox](https://github.com/googleapis/mcp-toolbox) server running in Docker, configured with a custom declarative **`tools.yaml`** that defines domain-specific database tools (`get-all-products`, `get-product-by-id`, `get-products-by-category`, `add-product`, `delete-product-by-id`, `list_tables`, `get-table-schema`) backed by a live **PostgreSQL** instance.
+
+---
+
+## Architecture Overview
+
+```
+┌─────────────────────────────────┐
+│ Spring Boot 3 Application │
+│ (Port 8080) │
+│ - ProductController (REST API) │
+│ - ProductCatalogService │
+│ - McpToolboxClient (Java SDK) │
+└────────────────┬────────────────┘
+ │ HTTP (JSON-RPC 2.0 /mcp)
+ ▼
+┌─────────────────────────────────┐
+│ MCP Toolbox Server (Docker) │
+│ (Port 5005:5000) │
+│ --config /tools.yaml │
+│ (Custom declarative tools) │
+└────────────────┬────────────────┘
+ │ TCP (5432)
+ ▼
+┌─────────────────────────────────┐
+│ PostgreSQL 15 (Docker) │
+│ (Port 5433:5432, DB: mcpdb) │
+│ - Table: products │
+└─────────────────────────────────┘
+```
+
+---
+
+## Custom Declarative `tools.yaml`
+
+Rather than exposing arbitrary raw SQL execution, the application defines domain-specific tools declaratively in [`tools.yaml`](./tools.yaml):
+
+| Tool Name | Parameters | Description |
+| :--- | :--- | :--- |
+| `get-all-products` | None | Retrieves all products ordered by ID. |
+| `get-product-by-id` | `id` (integer) | Retrieves a single product by its ID. |
+| `get-products-by-category` | `category` (string) | Retrieves products filtered by category. |
+| `add-product` | `name` (string), `category` (string, optional), `price` (float), `stock` (integer) | Inserts a product using parameterized queries (`$1, $2, $3, $4`) and returns the created record via `RETURNING`. |
+| `delete-product-by-id` | `id` (integer) | Deletes a product by ID. |
+| `list_tables` | None | Lists public tables from `information_schema.tables`. |
+| `get-table-schema` | `table_name` (string) | Introspects column types and nullability for a table. |
+
+### Why Declarative Tools?
+1. **Parameterized Security**: Toolbox binds arguments via PostgreSQL prepared statement parameters (`$1`, `$2`), eliminating SQL injection risks.
+2. **Schema & Validation**: Input types, required parameters, and descriptions are enforced at the MCP layer before hitting the database.
+3. **Domain Abstraction**: LLMs and microservices interact with clean business tools rather than raw SQL commands.
+
+## Maven Dependency
+
+Add the MCP Toolbox Java SDK dependency to your `pom.xml`:
+
+```xml
+
+ com.google.cloud.mcp
+ mcp-toolbox-sdk-java
+ 1.0.0
+
+```
+
+---
+
+## Prerequisites
+
+- **Java 17+** (JDK 17, 21, or 26)
+- **Maven 3.9+**
+- **Docker** container engine
+
+---
+
+## Quickstart
+
+### 1. Start the Docker Containers
+
+Run the automated startup script to launch PostgreSQL and MCP Toolbox:
+
+```bash
+./scripts/start-containers.sh
+```
+
+This script:
+1. Spawns a PostgreSQL container (`mcp-postgres`) on port `5433`.
+2. Seeds a sample `products` table with initial records.
+3. Spawns the MCP Toolbox server (`mcp-toolbox`) linked to PostgreSQL on port `5005`.
+4. Waits until the server responds to tool discovery.
+
+### 2. Run the Test Suites
+
+Execute standalone unit tests (offline, hermetic, no Docker containers required):
+
+```bash
+mvn clean test -Dnet.bytebuddy.experimental=true
+```
+
+Execute end-to-end integration tests (requires Docker containers running from Step 1):
+
+```bash
+mvn clean verify -Dnet.bytebuddy.experimental=true
+```
+
+### 3. Run the Spring Boot Application
+
+```bash
+mvn spring-boot:run
+```
+
+The application will start on `http://localhost:8080`.
+
+---
+
+## REST Endpoints
+
+### 1. List Available MCP Tools
+```bash
+curl -s http://localhost:8080/api/tools | jq .
+```
+
+### 2. Get All Products
+```bash
+curl -s http://localhost:8080/api/products | jq .
+```
+
+### 3. Create a New Product
+```bash
+curl -s -X POST http://localhost:8080/api/products \
+ -H "Content-Type: application/json" \
+ -d '{
+ "name": "Ultra HD Monitor 34-inch",
+ "category": "Electronics",
+ "price": 649.99,
+ "stock": 30
+ }'
+```
+
+### 4. Inspect Table Schema
+```bash
+curl -s http://localhost:8080/api/schema/products | jq .
+```
+
+---
+
+## Teardown
+
+To shut down and remove the test containers:
+
+```bash
+./scripts/stop-containers.sh
+```
diff --git a/demo-applications/spring-boot-postgres/pom.xml b/demo-applications/spring-boot-postgres/pom.xml
new file mode 100644
index 0000000..438df9c
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/pom.xml
@@ -0,0 +1,109 @@
+
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.1.3
+
+
+
+ com.google.cloud.mcp.example
+ spring-boot-postgres-example
+ 1.0.0
+ Spring Boot MCP Toolbox PostgreSQL Example
+ Demonstration of using the Java MCP Toolbox SDK within a Spring Boot 3 application connected to a containerized PostgreSQL instance.
+
+
+ 17
+ 17
+ 17
+ UTF-8
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.google.cloud.mcp
+ mcp-toolbox-sdk-java
+ 1.0.0
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ -Dnet.bytebuddy.experimental=true
+
+ **/*Test.java
+
+
+ **/*IT.java
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+ -Dnet.bytebuddy.experimental=true
+
+ **/*IT.java
+
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
diff --git a/demo-applications/spring-boot-postgres/scripts/start-containers.sh b/demo-applications/spring-boot-postgres/scripts/start-containers.sh
new file mode 100755
index 0000000..cce193d
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/scripts/start-containers.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+NETWORK_NAME="${NETWORK_NAME:-mcp-network}"
+
+echo "==> Cleaning up previous test containers and network if running..."
+docker rm -f mcp-toolbox mcp-postgres 2>/dev/null || true
+docker network rm "${NETWORK_NAME}" 2>/dev/null || true
+docker network create "${NETWORK_NAME}"
+
+POSTGRES_USER="${POSTGRES_USER:-mcpuser}"
+POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-mcppass}"
+POSTGRES_DATABASE="${POSTGRES_DATABASE:-mcpdb}"
+
+echo "==> Starting PostgreSQL container (port 5433:5432)..."
+docker run -d --name mcp-postgres --network "${NETWORK_NAME}" --network-alias postgres -p 5433:5432 \
+ -e POSTGRES_USER="${POSTGRES_USER}" \
+ -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
+ -e POSTGRES_DB="${POSTGRES_DATABASE}" \
+ postgres:15-alpine
+
+echo "==> Waiting for PostgreSQL to be ready..."
+until docker exec mcp-postgres pg_isready -U "${POSTGRES_USER}" -d "${POSTGRES_DATABASE}"; do
+ sleep 1
+done
+
+echo "==> Initializing schema and seed data in PostgreSQL from schema.sql..."
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+docker exec -i mcp-postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DATABASE}" < "${SCRIPT_DIR}/../src/main/resources/schema.sql"
+
+echo "==> Starting MCP Toolbox container with custom tools.yaml (port 5005:5000)..."
+docker run -d --name mcp-toolbox --network "${NETWORK_NAME}" -p 5005:5000 \
+ -e POSTGRES_HOST=postgres \
+ -e POSTGRES_PORT=5432 \
+ -e POSTGRES_DATABASE="${POSTGRES_DATABASE}" \
+ -e POSTGRES_USER="${POSTGRES_USER}" \
+ -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
+ -v "${SCRIPT_DIR}/../tools.yaml:/tools.yaml:ro" \
+ us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest \
+ --config /tools.yaml --address 0.0.0.0 --port 5000
+
+echo "==> Waiting for MCP Toolbox to be ready on http://localhost:5005/mcp..."
+until curl -s -X POST http://localhost:5005/mcp -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | grep -q "get-all-products"; do
+ sleep 1
+done
+
+echo "==> MCP Toolbox Server is healthy and accepting requests on http://localhost:5005/mcp."
diff --git a/demo-applications/spring-boot-postgres/scripts/stop-containers.sh b/demo-applications/spring-boot-postgres/scripts/stop-containers.sh
new file mode 100755
index 0000000..51563ac
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/scripts/stop-containers.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+NETWORK_NAME="${NETWORK_NAME:-mcp-network}"
+docker rm -f mcp-toolbox mcp-postgres 2>/dev/null || true
+docker network rm "${NETWORK_NAME}" 2>/dev/null || true
+echo "==> Test containers and network stopped and removed."
diff --git a/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/SpringBootPostgresApplication.java b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/SpringBootPostgresApplication.java
new file mode 100644
index 0000000..c2e7a12
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/SpringBootPostgresApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.mcp.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/** Entry point for the Spring Boot MCP Toolbox PostgreSQL Example Application. */
+@SpringBootApplication
+public class SpringBootPostgresApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootPostgresApplication.class, args);
+ }
+}
diff --git a/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/config/McpToolboxConfig.java b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/config/McpToolboxConfig.java
new file mode 100644
index 0000000..0fcd9d2
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/config/McpToolboxConfig.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.mcp.example.config;
+
+import com.google.cloud.mcp.McpToolboxClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/** Configuration class that registers the {@link McpToolboxClient} as a Spring bean. */
+@Configuration
+public class McpToolboxConfig {
+
+ private static final Logger logger = LoggerFactory.getLogger(McpToolboxConfig.class);
+
+ @Value("${mcp.toolbox.url:http://localhost:5005/mcp}")
+ private String toolboxUrl;
+
+ @Bean
+ public McpToolboxClient mcpToolboxClient() {
+ logger.info("Initializing McpToolboxClient configured with baseUrl: {}", toolboxUrl);
+ return McpToolboxClient.builder().baseUrl(toolboxUrl).build();
+ }
+}
diff --git a/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/controller/ProductController.java b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/controller/ProductController.java
new file mode 100644
index 0000000..324e838
--- /dev/null
+++ b/demo-applications/spring-boot-postgres/src/main/java/com/google/cloud/mcp/example/controller/ProductController.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.mcp.example.controller;
+
+import com.google.cloud.mcp.example.model.Product;
+import com.google.cloud.mcp.example.service.ProductCatalogService;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/** REST controller exposing product catalog and MCP tool inspection APIs. */
+@RestController
+@RequestMapping("/api")
+public class ProductController {
+
+ private final ProductCatalogService catalogService;
+
+ public ProductController(ProductCatalogService catalogService) {
+ this.catalogService = catalogService;
+ }
+
+ /**
+ * Returns list of tools available on the MCP Toolbox server.
+ *
+ * @return List of tool names.
+ */
+ @GetMapping("/tools")
+ public CompletableFuture>> getAvailableTools() {
+ return catalogService.listAvailableTools().thenApply(tools -> ResponseEntity.ok(tools));
+ }
+
+ /**
+ * Returns all products in the database.
+ *
+ * @return List of products.
+ */
+ @GetMapping("/products")
+ public CompletableFuture>> getAllProducts() {
+ return catalogService.getAllProducts().thenApply(products -> ResponseEntity.ok(products));
+ }
+
+ /**
+ * Returns a product by its ID.
+ *
+ * @param id The product ID.
+ * @return The product or HTTP 404 Not Found.
+ */
+ @GetMapping("/products/{id}")
+ public CompletableFuture> getProductById(@PathVariable Long id) {
+ if (id == null || id <= 0) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product ID must be positive"));
+ }
+ return catalogService
+ .getProductById(id)
+ .thenApply(
+ product ->
+ product != null ? ResponseEntity.ok(product) : ResponseEntity.notFound().build());
+ }
+
+ /**
+ * Returns products belonging to a specified category.
+ *
+ * @param category Category name.
+ * @return List of matching products.
+ */
+ @GetMapping("/products/category/{category}")
+ public CompletableFuture>> getProductsByCategory(
+ @PathVariable String category) {
+ if (category == null || category.trim().isEmpty()) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product category cannot be null or empty"));
+ }
+ return catalogService.getProductsByCategory(category.trim()).thenApply(ResponseEntity::ok);
+ }
+
+ /**
+ * Creates a new product.
+ *
+ * @param product Product details.
+ * @return HTTP 201 Created with Location header and persisted product.
+ */
+ @PostMapping("/products")
+ public CompletableFuture> createProduct(@RequestBody Product product) {
+ if (product == null) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product payload cannot be null"));
+ }
+ if (product.name() == null || product.name().trim().isEmpty()) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product name cannot be null or empty"));
+ }
+ if (product.price() == null) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product price cannot be null"));
+ }
+ if (product.stock() == null) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product stock cannot be null"));
+ }
+ return catalogService
+ .addProduct(product.name(), product.category(), product.price(), product.stock())
+ .thenApply(
+ created ->
+ ResponseEntity.created(URI.create("/api/products/" + created.id())).body(created));
+ }
+
+ /**
+ * Deletes a product by its ID.
+ *
+ * @param id The product ID.
+ * @return HTTP 204 No Content if deleted, or HTTP 404 Not Found.
+ */
+ @DeleteMapping("/products/{id}")
+ public CompletableFuture> deleteProduct(@PathVariable Long id) {
+ if (id == null || id <= 0) {
+ return CompletableFuture.failedFuture(
+ new IllegalArgumentException("Product ID must be positive"));
+ }
+ return catalogService
+ .deleteProductById(id)
+ .thenApply(
+ deleted ->
+ deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build());
+ }
+
+ /**
+ * Returns database table schema.
+ *
+ * @param tableName Name of the table.
+ * @return Schema JSON string.
+ */
+ @GetMapping("/schema/{tableName}")
+ public CompletableFuture> getTableSchema(@PathVariable String tableName) {
+ return catalogService.getTableSchema(tableName).thenApply(schema -> ResponseEntity.ok(schema));
+ }
+
+ @ExceptionHandler(IllegalArgumentException.class)
+ public ResponseEntity