Skip to content
Open
2 changes: 1 addition & 1 deletion demo-applications/cymbal-transit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<dependency>
<groupId>com.google.cloud.mcp</groupId>
<artifactId>mcp-toolbox-sdk-java</artifactId>
<version>0.2.0</version>
<version>0.2.1-SNAPSHOT</version>
</dependency>

<!-- Java Annotations -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public static void main(final String[] args) throws Exception {
// Start the Spring Boot application.
app.run(args);
logger.info(
"Hello from Cloud Run! The container started successfully and is listening for HTTP requests on " + port);
"Hello from Cloud Run! The container started successfully and is listening for HTTP"
+ " requests on "
+ port);
}
}

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<dependency>
<groupId>com.google.cloud.mcp</groupId>
<artifactId>mcp-toolbox-sdk-java</artifactId>
<version>0.2.0</version><!-- {x-version-update:mcp-toolbox-sdk-java:released} -->
<version>0.2.1-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
285 changes: 153 additions & 132 deletions example/src/main/java/cloudcode/helloworld/ExampleUsage.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,145 +16,166 @@

package cloudcode.helloworld;

import java.util.Map;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.io.FileInputStream;
import com.google.cloud.mcp.McpToolboxClient;
import com.google.cloud.mcp.AuthTokenGetter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import com.google.cloud.mcp.McpToolboxClient;
import com.google.cloud.mcp.auth.AuthTokenGetter;
import java.io.FileInputStream;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
* Sample Application to demostrate the usage of the MCP Toolbox Java SDK.
* Covers: Global Auth, Parameterized Auth, Discovery, Simple Tool, Authenticated Tool, Parameter Binding.
* Sample Application to demostrate the usage of the MCP Toolbox Java SDK. Covers: Global Auth,
* Parameterized Auth, Discovery, Simple Tool, Authenticated Tool, Parameter Binding.
*/
public class ExampleUsage {
public static void main(String[] args) {
// CONFIGURATION
String targetUrl = "YOUR_TOOLBOX_SERVICE_ENDPOINT";

// Match the Service URL if using Cloud Run OIDC
String tokenAudience = targetUrl;

// --------------------------------------------------------------------------------
// AUTHENTICATION SETUP
// --------------------------------------------------------------------------------
// FOR LOCAL DEVELOPMENT: Use a Service Account Key JSON file.
// FOR PRODUCTION (Cloud Run): Comment out the 'keyPath' logic and use ADC directly.
// --------------------------------------------------------------------------------

String keyPath = "YOUR_CREDENTIALS_JSON_FILE_PATH.json";

System.out.println("--- Starting MCP Toolbox Integration Test ---");
System.out.println("Target Server: " + targetUrl);

try {
System.out.println(" [Init] Fetching ID Token...");

GoogleCredentials credentials;

// --- OPTION A: LOCAL DEV (Explicit Key File) ---
if (keyPath != null && !keyPath.isEmpty()) {
System.out.println(" [Auth] Using Service Account Key File: " + keyPath);
credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
}
// --- OPTION B: PRODUCTION (ADC) ---
else {
System.out.println(" [Auth] Using Application Default Credentials (ADC)");
credentials = GoogleCredentials.getApplicationDefault();
}

if (!(credentials instanceof IdTokenProvider)) {
throw new RuntimeException("Loaded credentials do not support ID Tokens.");
}

// Generate Token for the specified Audience
String idToken = ((IdTokenProvider) credentials).idTokenWithAudience(tokenAudience, Collections.emptyList()).getTokenValue();
System.out.println(" [Debug] Token Generated.");

// Initialize Client with Global Auth (Applies to ALL calls - Gate 1)
McpToolboxClient client = McpToolboxClient.builder()
.baseUrl(targetUrl)
.apiKey(idToken)
.build();

// STEP 1: TEST DISCOVERY METHODS
client.listTools()
.thenCompose(tools -> {
System.out.println("\n[1] listTools(): Success. Found " + tools.size() + " tools.");
return client.loadToolset();
})
.thenCompose(tools -> {
System.out.println("[2] loadToolset() (Alias): Success.");
return client.loadToolset("retail")
.handle((res, ex) -> {
if (ex == null) System.out.println("[3] loadToolset('retail'): Found " + res.size() + " tools.");
else System.out.println("[3] loadToolset('retail'): Skipped (Not configured on server).");
return null;
public static void main(String[] args) {
// CONFIGURATION
String targetUrl = "YOUR_TOOLBOX_SERVICE_ENDPOINT";

// Match the Service URL if using Cloud Run OIDC
String tokenAudience = targetUrl;

// --------------------------------------------------------------------------------
// AUTHENTICATION SETUP
// --------------------------------------------------------------------------------
// FOR LOCAL DEVELOPMENT: Use a Service Account Key JSON file.
// FOR PRODUCTION (Cloud Run): Comment out the 'keyPath' logic and use ADC directly.
// --------------------------------------------------------------------------------

String keyPath = "YOUR_CREDENTIALS_JSON_FILE_PATH.json";

System.out.println("--- Starting MCP Toolbox Integration Test ---");
System.out.println("Target Server: " + targetUrl);

try {
System.out.println(" [Init] Fetching ID Token...");

GoogleCredentials credentials;

// --- OPTION A: LOCAL DEV (Explicit Key File) ---
if (keyPath != null && !keyPath.isEmpty()) {
System.out.println(" [Auth] Using Service Account Key File: " + keyPath);
credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
}
// --- OPTION B: PRODUCTION (ADC) ---
else {
System.out.println(" [Auth] Using Application Default Credentials (ADC)");
credentials = GoogleCredentials.getApplicationDefault();
}

if (!(credentials instanceof IdTokenProvider)) {
throw new RuntimeException("Loaded credentials do not support ID Tokens.");
}

// Generate Token for the specified Audience
String idToken =
((IdTokenProvider) credentials)
.idTokenWithAudience(tokenAudience, Collections.emptyList())
.getTokenValue();
System.out.println(" [Debug] Token Generated.");

// Initialize Client with Global Auth (Applies to ALL calls - Gate 1)
McpToolboxClient client =
McpToolboxClient.builder().baseUrl(targetUrl).apiKey(idToken).build();

// STEP 1: TEST DISCOVERY METHODS
client
.listTools()
.thenCompose(
tools -> {
System.out.println("\n[1] listTools(): Success. Found " + tools.size() + " tools.");
return client.loadToolset();
})
.thenCompose(
tools -> {
System.out.println("[2] loadToolset() (Alias): Success.");
return client
.loadToolset("retail")
.handle(
(res, ex) -> {
if (ex == null)
System.out.println(
"[3] loadToolset('retail'): Found " + res.size() + " tools.");
else
System.out.println(
"[3] loadToolset('retail'): Skipped (Not configured on server).");
return null;
});
})
.thenCompose(ignore -> {

// STEP 2: INVOKE TOOL WITHOUT EXTRA AUTH
System.out.println("\n[4] Testing Simple Tool: 'get-retail-facet-filters'...");
return client.invokeTool("get-retail-facet-filters", Map.of());
})
.thenCompose(result -> {
System.out.println(" -> Result: " + (result.content() != null ? "Received Data" : "Empty"));

// STEP 3: INVOKE TOOL WITH AUTHENTICATED PARAMETERS
System.out.println("\n[5] Testing Authenticated Tool: 'get-toy-price'...");

// Define the getter for the 'google_auth' service
AuthTokenGetter toolAuthGetter = () -> CompletableFuture.completedFuture(idToken);

// Load using the sophisticated overload
return client.loadTool("get-toy-price", Map.of("google_auth", toolAuthGetter));
})
.thenCompose(tool -> {
System.out.println(" -> Loaded Tool: " + tool.definition().description());

// STEP 4: TEST BINDING PARAMETERS SEQUENTIALLY
System.out.println("\n[A] Executing UNBOUND (Runtime arg: 'barbie')...");

return tool.execute(Map.of("description", "barbie"))
.thenCompose(result1 -> {
if (result1.content() != null && !result1.content().isEmpty()) {
System.out
.println(" -> Result (Unbound): " + result1.content().get(0).text());
}

// NOW bind the parameter
System.out.println("\n[B] Binding 'description' to 'soft toy'...");
tool.bindParam("description", "soft toy");

System.out.println(" -> Executing BOUND (Runtime arg: 'barbie' - should be IGNORED)...");
// We pass 'barbie', but expecting 'soft toy' price because of binding override
return tool.execute(Map.of("description", "barbie"));
})
.thenCompose(
ignore -> {

// STEP 2: INVOKE TOOL WITHOUT EXTRA AUTH
System.out.println("\n[4] Testing Simple Tool: 'get-retail-facet-filters'...");
return client.invokeTool("get-retail-facet-filters", Map.of());
})
.thenCompose(
result -> {
System.out.println(
" -> Result: " + (result.content() != null ? "Received Data" : "Empty"));

// STEP 3: INVOKE TOOL WITH AUTHENTICATED PARAMETERS
System.out.println("\n[5] Testing Authenticated Tool: 'get-toy-price'...");

// Define the getter for the 'google_auth' service
AuthTokenGetter toolAuthGetter = () -> CompletableFuture.completedFuture(idToken);

// Load using the sophisticated overload
return client.loadTool("get-toy-price", Map.of("google_auth", toolAuthGetter));
})
.thenCompose(
tool -> {
System.out.println(" -> Loaded Tool: " + tool.definition().description());

// STEP 4: TEST BINDING PARAMETERS SEQUENTIALLY
System.out.println("\n[A] Executing UNBOUND (Runtime arg: 'barbie')...");

return tool.execute(Map.of("description", "barbie"))
.thenCompose(
result1 -> {
if (result1.content() != null && !result1.content().isEmpty()) {
System.out.println(
" -> Result (Unbound): " + result1.content().get(0).text());
}

// NOW bind the parameter
System.out.println("\n[B] Binding 'description' to 'soft toy'...");
tool.bindParam("description", "soft toy");

System.out.println(
" -> Executing BOUND (Runtime arg: 'barbie' - should be"
+ " IGNORED)...");
// We pass 'barbie', but expecting 'soft toy' price because of binding
// override
return tool.execute(Map.of("description", "barbie"));
});
})
.thenAccept(result -> {
System.out.println("\n[6] Final Result (Bound):");
if (result.isError()) {
System.err.println("Tool execution failed: " + result.content().get(0).text());
} else if (result.content() != null && !result.content().isEmpty()) {
String output = result.content().get(0).text();
System.out.println(" " + output.substring(0, Math.min(output.length(), 200)) + "...");
} else {
System.out.println(" Empty Response");
}
})
.exceptionally(ex -> {
System.err.println("\n!!! TEST FAILED !!!");
ex.printStackTrace();
return null;
})
.join();

} catch (Exception e) {
e.printStackTrace();
}
System.out.println("\n--- Test Suite Complete ---");
})
.thenAccept(
result -> {
System.out.println("\n[6] Final Result (Bound):");
if (result.isError()) {
System.err.println("Tool execution failed: " + result.content().get(0).text());
} else if (result.content() != null && !result.content().isEmpty()) {
String output = result.content().get(0).text();
System.out.println(
" " + output.substring(0, Math.min(output.length(), 200)) + "...");
} else {
System.out.println(" Empty Response");
}
})
.exceptionally(
ex -> {
System.err.println("\n!!! TEST FAILED !!!");
ex.printStackTrace();
return null;
})
.join();

} catch (Exception e) {
e.printStackTrace();
}
System.out.println("\n--- Test Suite Complete ---");
}
}
Loading
Loading