searchPolicies(String searchQuery) {
+ return mcpClient
+ .invokeTool("search-policies", Map.of("search_query", searchQuery))
+ .thenApply(
+ result -> {
+ if (result.isError() || result.content() == null || result.content().isEmpty())
+ return "No policy information found.";
+ return result.content().stream()
+ .map(content -> content.text())
+ .collect(Collectors.joining(", ", "[", "]"));
});
- }
+ }
}
/**
* 5. THE REST CONTROLLER
- * Now radically simplified! No more manual if/else logic or JSON parsing.
+ *
+ * Now radically simplified! No more manual if/else logic or JSON parsing.
*/
@RestController
@RequestMapping("/api/agent")
class TransitAgentController {
- private final TransitAgent transitAgent;
+ private final TransitAgent transitAgent;
- public TransitAgentController(TransitAgent transitAgent) {
- this.transitAgent = transitAgent;
- }
+ public TransitAgentController(TransitAgent transitAgent) {
+ this.transitAgent = transitAgent;
+ }
- @PostMapping("/chat")
- public ResponseEntity handleUserChat(@RequestBody String userMessage, HttpSession session) {
- // We use the HTTP Session ID to tell LangChain4j which memory context to load
- String sessionId = session.getId();
-
- // Let Gemini 3 Flash handle the thinking, tool execution, and response generation!
- String agentResponse = transitAgent.chat(sessionId, userMessage);
-
- return ResponseEntity.ok(agentResponse);
- }
+ @PostMapping("/chat")
+ public ResponseEntity handleUserChat(
+ @RequestBody String userMessage, HttpSession session) {
+ // We use the HTTP Session ID to tell LangChain4j which memory context to load
+ String sessionId = session.getId();
+
+ // Let Gemini 3.5 Flash handle the thinking, tool execution, and response generation!
+ String agentResponse = transitAgent.chat(sessionId, userMessage);
+
+ return ResponseEntity.ok(agentResponse);
+ }
}
diff --git a/example/pom.xml b/example/pom.xml
index 53ca764..9727be7 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -33,7 +33,7 @@
com.google.cloud.mcp
mcp-toolbox-sdk-java
- 0.2.0
+ 0.3.0-SNAPSHOT
diff --git a/example/src/main/java/cloudcode/helloworld/ExampleUsage.java b/example/src/main/java/cloudcode/helloworld/ExampleUsage.java
index 504886c..5570ad2 100644
--- a/example/src/main/java/cloudcode/helloworld/ExampleUsage.java
+++ b/example/src/main/java/cloudcode/helloworld/ExampleUsage.java
@@ -16,145 +16,168 @@
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.
+ *
+ * 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 ---");
+ }
}
diff --git a/example/src/main/java/cloudcode/helloworld/InputValidationTest.java b/example/src/main/java/cloudcode/helloworld/InputValidationTest.java
index b171ae7..4971e25 100644
--- a/example/src/main/java/cloudcode/helloworld/InputValidationTest.java
+++ b/example/src/main/java/cloudcode/helloworld/InputValidationTest.java
@@ -16,100 +16,101 @@
package cloudcode.helloworld;
-import com.google.cloud.mcp.McpToolboxClient;
-import com.google.cloud.mcp.Tool;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenProvider;
+import com.google.cloud.mcp.McpToolboxClient;
+import com.google.cloud.mcp.tool.Tool;
import java.io.FileInputStream;
import java.util.Collections;
-import java.util.Map;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class InputValidationTest {
- public static void main(String[] args) {
- String targetUrl = "YOUR_TOOLBOX_SERVICE_ENDPOINT";
- 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.
- // --------------------------------------------------------------------------------
+ public static void main(String[] args) {
+ String targetUrl = "YOUR_TOOLBOX_SERVICE_ENDPOINT";
+ 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";
+ String keyPath = "/YOUR_CREDENTIALS_JSON_FILE_PATH.json";
- System.out.println("--- Starting MCP Toolbox Input Validation Test ---");
+ System.out.println("--- Starting MCP Toolbox Input Validation Test ---");
- try {
- // 1. Setup Auth (Same as before)
- System.out.println(" [Init] Fetching ID Token...");
- GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
- if (!(credentials instanceof IdTokenProvider)) {
- throw new RuntimeException("Loaded credentials do not support ID Tokens.");
- }
- String idToken = ((IdTokenProvider) credentials).idTokenWithAudience(tokenAudience, Collections.emptyList()).getTokenValue();
+ try {
+ // 1. Setup Auth (Same as before)
+ System.out.println(" [Init] Fetching ID Token...");
+ GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
+ if (!(credentials instanceof IdTokenProvider)) {
+ throw new RuntimeException("Loaded credentials do not support ID Tokens.");
+ }
+ String idToken =
+ ((IdTokenProvider) credentials)
+ .idTokenWithAudience(tokenAudience, Collections.emptyList())
+ .getTokenValue();
- // 2. Initialize Client
- McpToolboxClient client = McpToolboxClient.builder()
- .baseUrl(targetUrl)
- .build();
+ // 2. Initialize Client
+ McpToolboxClient client = McpToolboxClient.builder().baseUrl(targetUrl).build();
- // 3. Load the Tool
- // We MUST use loadTool() because validation relies on the ToolDefinition fetched from the server.
- System.out.println(" [Init] Loading tool 'get-toy-price'...");
- Tool tool = client.loadTool("get-toy-price").join();
+ // 3. Load the Tool
+ // We MUST use loadTool() because validation relies on the ToolDefinition fetched from the
+ // server.
+ System.out.println(" [Init] Loading tool 'get-toy-price'...");
+ Tool tool = client.loadTool("get-toy-price").join();
- // 4. Register Auth
- // We manually register the token getter so the Tool object can inject the header.
- tool.addAuthTokenGetter("google_auth", () -> CompletableFuture.completedFuture(idToken));
+ // 4. Register Auth
+ // We manually register the token getter so the Tool object can inject the header.
+ tool.addAuthTokenGetter("google_auth", () -> CompletableFuture.completedFuture(idToken));
+ // --- Test Case A: Valid Input ---
+ System.out.println("\n[Test A] Sending VALID input (String)...");
+ try {
+ Map validArgs = Map.of("description", "barbie");
+ var result = tool.execute(validArgs).join();
+ System.out.println(
+ " ✅ Success! Output: "
+ + (result.content().isEmpty() ? "Empty" : result.content().get(0).text()));
+ } catch (Exception e) {
+ System.err.println(" ❌ Unexpected failure: " + e.getMessage());
+ e.printStackTrace();
+ }
- // --- Test Case A: Valid Input ---
- System.out.println("\n[Test A] Sending VALID input (String)...");
- try {
- Map validArgs = Map.of("description", "barbie");
- var result = tool.execute(validArgs).join();
- System.out.println(" ✅ Success! Output: " + (result.content().isEmpty() ? "Empty" : result.content().get(0).text()));
- } catch (Exception e) {
- System.err.println(" ❌ Unexpected failure: " + e.getMessage());
- e.printStackTrace();
- }
+ // --- Test Case B: Invalid Type (Int instead of String) ---
+ System.out.println("\n[Test B] Sending INVALID input (Integer instead of String)...");
+ try {
+ // The 'description' parameter is defined as type: string. We pass an Integer.
+ Map invalidArgs = Map.of("description", 12345);
+ tool.execute(invalidArgs).join();
+ System.err.println(" ❌ FAILED: Validation did not catch the error!");
+ } catch (Exception e) {
+ // We expect a RuntimeException wrapping IllegalArgumentException
+ Throwable cause = e.getCause();
+ System.out.println(" ✅ Caught Expected Error: " + cause.getMessage());
+ }
- // --- Test Case B: Invalid Type (Int instead of String) ---
- System.out.println("\n[Test B] Sending INVALID input (Integer instead of String)...");
- try {
- // The 'description' parameter is defined as type: string. We pass an Integer.
- Map invalidArgs = Map.of("description", 12345);
-
- tool.execute(invalidArgs).join();
- System.err.println(" ❌ FAILED: Validation did not catch the error!");
- } catch (Exception e) {
- // We expect a RuntimeException wrapping IllegalArgumentException
- Throwable cause = e.getCause();
- System.out.println(" ✅ Caught Expected Error: " + cause.getMessage());
- }
-
+ // --- Test Case C: Null Value (Filtering) ---
+ System.out.println("\n[Test C] Sending NULL value (should be filtered)...");
+ try {
+ // We use a HashMap because Map.of doesn't allow nulls
+ Map nullArgs = new HashMap<>();
+ nullArgs.put("description", "barbie"); // Valid param
+ nullArgs.put("some_optional_param", null); // Null param
- // --- Test Case C: Null Value (Filtering) ---
- System.out.println("\n[Test C] Sending NULL value (should be filtered)...");
- try {
- // We use a HashMap because Map.of doesn't allow nulls
- Map nullArgs = new HashMap<>();
- nullArgs.put("description", "barbie"); // Valid param
- nullArgs.put("some_optional_param", null); // Null param
-
- // If validation works, 'some_optional_param' will be removed before sending
- var result = tool.execute(nullArgs).join();
- System.out.println(" ✅ Success! Null value was filtered and request succeeded.");
- } catch (Exception e) {
- System.out.println(" ❌ Result: " + e.getCause().getMessage());
- }
+ // If validation works, 'some_optional_param' will be removed before sending
+ var result = tool.execute(nullArgs).join();
+ System.out.println(" ✅ Success! Null value was filtered and request succeeded.");
+ } catch (Exception e) {
+ System.out.println(" ❌ Result: " + e.getCause().getMessage());
+ }
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("\n--- Done ---");
+ } catch (Exception e) {
+ e.printStackTrace();
}
+ System.out.println("\n--- Done ---");
+ }
}
diff --git a/example/src/main/java/cloudcode/helloworld/StrictFlagTest.java b/example/src/main/java/cloudcode/helloworld/StrictFlagTest.java
index ba32ff9..df01a8c 100644
--- a/example/src/main/java/cloudcode/helloworld/StrictFlagTest.java
+++ b/example/src/main/java/cloudcode/helloworld/StrictFlagTest.java
@@ -17,18 +17,16 @@
package cloudcode.helloworld;
import com.google.cloud.mcp.McpToolboxClient;
-import com.google.cloud.mcp.Tool;
-import java.util.Map;
+import com.google.cloud.mcp.tool.Tool;
import java.util.HashMap;
+import java.util.Map;
public class StrictFlagTest {
public static void main(String[] args) {
String targetUrl = "YOUR_TOOLBOX_SERVICE_ENDPOINT";
System.out.println("--- Starting MCP Toolbox Strict Flag Test ---");
- McpToolboxClient client = McpToolboxClient.builder()
- .baseUrl(targetUrl)
- .build();
+ McpToolboxClient client = McpToolboxClient.builder().baseUrl(targetUrl).build();
// Prepare bindings for a NON-EXISTENT tool
Map> paramBinds = new HashMap<>();
@@ -38,7 +36,8 @@ public static void main(String[] args) {
System.out.println("\n[Test 1] Loading with Strict = FALSE...");
try {
Map tools = client.loadToolset(null, paramBinds, null, false).join();
- System.out.println(" ✅ Success! Loaded " + tools.size() + " tools. Unknown binding was ignored.");
+ System.out.println(
+ " ✅ Success! Loaded " + tools.size() + " tools. Unknown binding was ignored.");
} catch (Exception e) {
System.err.println(" ❌ Failed unexpectedly: " + e.getMessage());
}
@@ -60,4 +59,4 @@ public static void main(String[] args) {
System.out.println("\n--- Done ---");
}
-}
\ No newline at end of file
+}
diff --git a/pom.xml b/pom.xml
index 2a8076c..ef1b548 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
com.google.cloud.mcp
mcp-toolbox-sdk-java
- 0.2.1-SNAPSHOT
+ 0.3.0-SNAPSHOT
MCP Toolbox SDK for Java
Java client library for the Model Context Protocol (MCP) Toolbox.
https://github.com/googleapis/mcp-toolbox-sdk-java
diff --git a/src/main/java/com/google/cloud/mcp/McpToolboxClient.java b/src/main/java/com/google/cloud/mcp/McpToolboxClient.java
index 472a6f3..ff79e04 100644
--- a/src/main/java/com/google/cloud/mcp/McpToolboxClient.java
+++ b/src/main/java/com/google/cloud/mcp/McpToolboxClient.java
@@ -16,6 +16,14 @@
package com.google.cloud.mcp;
+import com.google.cloud.mcp.auth.AuthTokenGetter;
+import com.google.cloud.mcp.auth.CredentialsProvider;
+import com.google.cloud.mcp.client.McpToolboxClientBuilder;
+import com.google.cloud.mcp.tool.Tool;
+import com.google.cloud.mcp.tool.ToolDefinition;
+import com.google.cloud.mcp.tool.ToolPostProcessor;
+import com.google.cloud.mcp.tool.ToolPreProcessor;
+import com.google.cloud.mcp.tool.ToolResult;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
diff --git a/src/main/java/com/google/cloud/mcp/AuthMethods.java b/src/main/java/com/google/cloud/mcp/auth/AuthMethods.java
similarity index 98%
rename from src/main/java/com/google/cloud/mcp/AuthMethods.java
rename to src/main/java/com/google/cloud/mcp/auth/AuthMethods.java
index cb3815f..a287009 100644
--- a/src/main/java/com/google/cloud/mcp/AuthMethods.java
+++ b/src/main/java/com/google/cloud/mcp/auth/AuthMethods.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenProvider;
diff --git a/src/main/java/com/google/cloud/mcp/AuthResolver.java b/src/main/java/com/google/cloud/mcp/auth/AuthResolver.java
similarity index 98%
rename from src/main/java/com/google/cloud/mcp/AuthResolver.java
rename to src/main/java/com/google/cloud/mcp/auth/AuthResolver.java
index 109c6ba..0fccadd 100644
--- a/src/main/java/com/google/cloud/mcp/AuthResolver.java
+++ b/src/main/java/com/google/cloud/mcp/auth/AuthResolver.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
import java.util.HashMap;
import java.util.List;
diff --git a/src/main/java/com/google/cloud/mcp/AuthTokenGetter.java b/src/main/java/com/google/cloud/mcp/auth/AuthTokenGetter.java
similarity index 96%
rename from src/main/java/com/google/cloud/mcp/AuthTokenGetter.java
rename to src/main/java/com/google/cloud/mcp/auth/AuthTokenGetter.java
index 5d3f736..6067352 100644
--- a/src/main/java/com/google/cloud/mcp/AuthTokenGetter.java
+++ b/src/main/java/com/google/cloud/mcp/auth/AuthTokenGetter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
import java.util.concurrent.CompletableFuture;
diff --git a/src/main/java/com/google/cloud/mcp/CredentialsProvider.java b/src/main/java/com/google/cloud/mcp/auth/CredentialsProvider.java
similarity index 96%
rename from src/main/java/com/google/cloud/mcp/CredentialsProvider.java
rename to src/main/java/com/google/cloud/mcp/auth/CredentialsProvider.java
index eb428a0..a9c7ca4 100644
--- a/src/main/java/com/google/cloud/mcp/CredentialsProvider.java
+++ b/src/main/java/com/google/cloud/mcp/auth/CredentialsProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
import java.util.concurrent.CompletableFuture;
diff --git a/src/main/java/com/google/cloud/mcp/GoogleCredentialsProvider.java b/src/main/java/com/google/cloud/mcp/auth/GoogleCredentialsProvider.java
similarity index 98%
rename from src/main/java/com/google/cloud/mcp/GoogleCredentialsProvider.java
rename to src/main/java/com/google/cloud/mcp/auth/GoogleCredentialsProvider.java
index 11a7232..bb1490e 100644
--- a/src/main/java/com/google/cloud/mcp/GoogleCredentialsProvider.java
+++ b/src/main/java/com/google/cloud/mcp/auth/GoogleCredentialsProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
diff --git a/src/main/java/com/google/cloud/mcp/ResolvedAuth.java b/src/main/java/com/google/cloud/mcp/auth/ResolvedAuth.java
similarity index 97%
rename from src/main/java/com/google/cloud/mcp/ResolvedAuth.java
rename to src/main/java/com/google/cloud/mcp/auth/ResolvedAuth.java
index 017d7e7..e79ecfb 100644
--- a/src/main/java/com/google/cloud/mcp/ResolvedAuth.java
+++ b/src/main/java/com/google/cloud/mcp/auth/ResolvedAuth.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.auth;
+import com.google.cloud.mcp.tool.ToolDefinition;
import java.util.Map;
/** Represents a resolved set of authentication credentials for a tool execution. */
diff --git a/src/main/java/com/google/cloud/mcp/McpToolboxClientBuilder.java b/src/main/java/com/google/cloud/mcp/client/McpToolboxClientBuilder.java
similarity index 91%
rename from src/main/java/com/google/cloud/mcp/McpToolboxClientBuilder.java
rename to src/main/java/com/google/cloud/mcp/client/McpToolboxClientBuilder.java
index 7c2f765..0b66c13 100644
--- a/src/main/java/com/google/cloud/mcp/McpToolboxClientBuilder.java
+++ b/src/main/java/com/google/cloud/mcp/client/McpToolboxClientBuilder.java
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
-
+package com.google.cloud.mcp.client;
+
+import com.google.cloud.mcp.McpToolboxClient;
+import com.google.cloud.mcp.ProtocolVersion;
+import com.google.cloud.mcp.auth.CredentialsProvider;
+import com.google.cloud.mcp.tool.ToolPostProcessor;
+import com.google.cloud.mcp.tool.ToolPreProcessor;
+import com.google.cloud.mcp.transport.HttpMcpTransport;
+import com.google.cloud.mcp.transport.Transport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
diff --git a/src/main/java/com/google/cloud/mcp/McpToolboxClientImpl.java b/src/main/java/com/google/cloud/mcp/client/McpToolboxClientImpl.java
similarity index 95%
rename from src/main/java/com/google/cloud/mcp/McpToolboxClientImpl.java
rename to src/main/java/com/google/cloud/mcp/client/McpToolboxClientImpl.java
index c5d254b..d8ebdd2 100644
--- a/src/main/java/com/google/cloud/mcp/McpToolboxClientImpl.java
+++ b/src/main/java/com/google/cloud/mcp/client/McpToolboxClientImpl.java
@@ -14,10 +14,22 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.client;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.cloud.mcp.McpToolboxClient;
+import com.google.cloud.mcp.auth.AuthTokenGetter;
+import com.google.cloud.mcp.auth.CredentialsProvider;
+import com.google.cloud.mcp.tool.Tool;
+import com.google.cloud.mcp.tool.ToolDefinition;
+import com.google.cloud.mcp.tool.ToolPostProcessor;
+import com.google.cloud.mcp.tool.ToolPreProcessor;
+import com.google.cloud.mcp.tool.ToolResult;
+import com.google.cloud.mcp.transport.HttpMcpTransport;
+import com.google.cloud.mcp.transport.Transport;
+import com.google.cloud.mcp.transport.TransportManifest;
+import com.google.cloud.mcp.transport.TransportResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
diff --git a/src/main/java/com/google/cloud/mcp/McpException.java b/src/main/java/com/google/cloud/mcp/exception/McpException.java
similarity index 96%
rename from src/main/java/com/google/cloud/mcp/McpException.java
rename to src/main/java/com/google/cloud/mcp/exception/McpException.java
index c057016..9016674 100644
--- a/src/main/java/com/google/cloud/mcp/McpException.java
+++ b/src/main/java/com/google/cloud/mcp/exception/McpException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.exception;
/** Unchecked exception thrown for MCP Toolbox Client operations and protocol failures. */
public class McpException extends RuntimeException {
diff --git a/src/main/java/com/google/cloud/mcp/Tool.java b/src/main/java/com/google/cloud/mcp/tool/Tool.java
similarity index 97%
rename from src/main/java/com/google/cloud/mcp/Tool.java
rename to src/main/java/com/google/cloud/mcp/tool/Tool.java
index 49cfe59..4fb0229 100644
--- a/src/main/java/com/google/cloud/mcp/Tool.java
+++ b/src/main/java/com/google/cloud/mcp/tool/Tool.java
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.tool;
+import com.google.cloud.mcp.McpToolboxClient;
+import com.google.cloud.mcp.auth.AuthResolver;
+import com.google.cloud.mcp.auth.AuthTokenGetter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
diff --git a/src/main/java/com/google/cloud/mcp/ToolDefinition.java b/src/main/java/com/google/cloud/mcp/tool/ToolDefinition.java
similarity index 98%
rename from src/main/java/com/google/cloud/mcp/ToolDefinition.java
rename to src/main/java/com/google/cloud/mcp/tool/ToolDefinition.java
index ac8e60b..bef492f 100644
--- a/src/main/java/com/google/cloud/mcp/ToolDefinition.java
+++ b/src/main/java/com/google/cloud/mcp/tool/ToolDefinition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/main/java/com/google/cloud/mcp/ToolPostProcessor.java b/src/main/java/com/google/cloud/mcp/tool/ToolPostProcessor.java
similarity index 96%
rename from src/main/java/com/google/cloud/mcp/ToolPostProcessor.java
rename to src/main/java/com/google/cloud/mcp/tool/ToolPostProcessor.java
index 09000bb..61280ea 100644
--- a/src/main/java/com/google/cloud/mcp/ToolPostProcessor.java
+++ b/src/main/java/com/google/cloud/mcp/tool/ToolPostProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.tool;
import java.util.concurrent.CompletableFuture;
diff --git a/src/main/java/com/google/cloud/mcp/ToolPreProcessor.java b/src/main/java/com/google/cloud/mcp/tool/ToolPreProcessor.java
similarity index 97%
rename from src/main/java/com/google/cloud/mcp/ToolPreProcessor.java
rename to src/main/java/com/google/cloud/mcp/tool/ToolPreProcessor.java
index a280a85..190e38d 100644
--- a/src/main/java/com/google/cloud/mcp/ToolPreProcessor.java
+++ b/src/main/java/com/google/cloud/mcp/tool/ToolPreProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.tool;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
diff --git a/src/main/java/com/google/cloud/mcp/ToolResult.java b/src/main/java/com/google/cloud/mcp/tool/ToolResult.java
similarity index 97%
rename from src/main/java/com/google/cloud/mcp/ToolResult.java
rename to src/main/java/com/google/cloud/mcp/tool/ToolResult.java
index d447adc..29b6a61 100644
--- a/src/main/java/com/google/cloud/mcp/ToolResult.java
+++ b/src/main/java/com/google/cloud/mcp/tool/ToolResult.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/main/java/com/google/cloud/mcp/HttpMcpTransport.java b/src/main/java/com/google/cloud/mcp/transport/BaseMcpTransport.java
similarity index 56%
rename from src/main/java/com/google/cloud/mcp/HttpMcpTransport.java
rename to src/main/java/com/google/cloud/mcp/transport/BaseMcpTransport.java
index 6cfcc86..a09b383 100644
--- a/src/main/java/com/google/cloud/mcp/HttpMcpTransport.java
+++ b/src/main/java/com/google/cloud/mcp/transport/BaseMcpTransport.java
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.google.cloud.mcp;
+package com.google.cloud.mcp.transport;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.cloud.mcp.ProtocolVersion;
+import com.google.cloud.mcp.auth.CredentialsProvider;
+import com.google.cloud.mcp.tool.ToolDefinition;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -33,91 +36,29 @@
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
-/** Default HTTP transport implementation using Java 11 HttpClient. */
-public final class HttpMcpTransport implements Transport {
+public abstract class BaseMcpTransport implements Transport {
- private static final Logger logger = Logger.getLogger(HttpMcpTransport.class.getName());
- private static final String HTTP_WARNING =
+ protected static final Logger logger = Logger.getLogger(BaseMcpTransport.class.getName());
+ protected static final String HTTP_WARNING =
"This connection is using HTTP. To prevent credential exposure, please ensure all"
+ " communication is sent over HTTPS.";
- private final String baseUrl;
- private final Map clientHeaders;
- private final CredentialsProvider credentialsProvider;
- private final HttpClient httpClient;
- private final ObjectMapper objectMapper;
- private final ProtocolVersion preferredProtocolVersion;
- private final Object initLock = new Object();
- private CompletableFuture initFuture;
- private volatile ProtocolVersion negotiatedProtocolVersion;
- private volatile String sessionId;
-
- /**
- * Constructs a new HttpMcpTransport with a base URL.
- *
- * @param baseUrl The base URL of the remote service.
- */
- public HttpMcpTransport(String baseUrl) {
- this(baseUrl, Map.of(), (CredentialsProvider) null);
- }
-
- /**
- * Constructs a new HttpMcpTransport with base URL and default headers.
- *
- * @param baseUrl The base URL of the remote service.
- * @param clientHeaders Default HTTP headers to include in every request.
- */
- public HttpMcpTransport(String baseUrl, Map clientHeaders) {
- this(baseUrl, clientHeaders, (CredentialsProvider) null);
- }
-
- /**
- * Constructs a new HttpMcpTransport with base URL, default headers and credentials provider.
- *
- * @param baseUrl The base URL of the remote service.
- * @param clientHeaders Default HTTP headers to include in every request.
- * @param credentialsProvider Provider for retrieving authorization credentials.
- */
- public HttpMcpTransport(
- String baseUrl, Map clientHeaders, CredentialsProvider credentialsProvider) {
- this(baseUrl, clientHeaders, credentialsProvider, null, null, null);
- }
-
- /**
- * Constructs a HttpMcpTransport.
- *
- * @param baseUrl The base URL of the remote service.
- * @param clientHeaders Default HTTP headers to include in every request.
- * @param preferredProtocolVersion Preferred MCP protocol version.
- * @param httpClient Custom HTTP Client.
- * @param executor Optional Executor for handling async requests.
- */
- public HttpMcpTransport(
- String baseUrl,
- Map clientHeaders,
- ProtocolVersion preferredProtocolVersion,
- HttpClient httpClient,
- java.util.concurrent.Executor executor) {
- this(baseUrl, clientHeaders, null, preferredProtocolVersion, httpClient, executor);
- }
-
- /**
- * Primary constructor for HttpMcpTransport.
- *
- * @param baseUrl The base URL of the remote service.
- * @param clientHeaders Default HTTP headers to include in every request.
- * @param credentialsProvider Provider for retrieving authorization credentials.
- * @param preferredProtocolVersion Preferred MCP protocol version.
- * @param httpClient Custom HTTP Client.
- * @param executor Optional Executor for handling async requests.
- */
- public HttpMcpTransport(
- String baseUrl,
- Map clientHeaders,
- CredentialsProvider credentialsProvider,
- ProtocolVersion preferredProtocolVersion,
- HttpClient httpClient,
- java.util.concurrent.Executor executor) {
+ protected final String baseUrl;
+ protected final Map clientHeaders;
+ protected final CredentialsProvider credentialsProvider;
+ protected final HttpClient httpClient;
+ protected final ObjectMapper objectMapper;
+ protected final ProtocolVersion preferredProtocolVersion;
+ protected final Object initLock = new Object();
+ protected CompletableFuture initFuture;
+
+ protected BaseMcpTransport(
+ final String baseUrl,
+ final Map clientHeaders,
+ final CredentialsProvider credentialsProvider,
+ final ProtocolVersion preferredProtocolVersion,
+ final HttpClient httpClient,
+ final java.util.concurrent.Executor executor) {
if (baseUrl == null || baseUrl.isEmpty()) {
throw new IllegalArgumentException("Base URL must be provided");
}
@@ -146,28 +87,13 @@ public HttpMcpTransport(
this.objectMapper = new ObjectMapper();
}
- HttpMcpTransport(String baseUrl, HttpClient httpClient) {
- this(baseUrl, Map.of(), null, null, httpClient, null);
- }
-
- HttpMcpTransport(String baseUrl, Map clientHeaders, HttpClient httpClient) {
- this(baseUrl, clientHeaders, null, null, httpClient, null);
- }
-
- HttpMcpTransport(
- String baseUrl,
- Map clientHeaders,
- CredentialsProvider credentialsProvider,
- HttpClient httpClient) {
- this(baseUrl, clientHeaders, credentialsProvider, null, httpClient, null);
- }
-
@Override
- public String getBaseUrl() {
+ public final String getBaseUrl() {
return this.baseUrl;
}
- private CompletableFuture