Skip to content

Commit 7b2e090

Browse files
committed
feat: Simplify ExampleUsage to basic connection, listing, and invocation; extract parameter bindings to ParameterBindingUsage
1 parent 0870c1f commit 7b2e090

3 files changed

Lines changed: 224 additions & 153 deletions

File tree

example/src/main/java/cloudcode/simple/ExampleUsage.java

Lines changed: 35 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -16,166 +16,48 @@
1616

1717
package cloudcode.simple;
1818

19-
import com.google.auth.oauth2.GoogleCredentials;
20-
import com.google.auth.oauth2.IdTokenProvider;
21-
import com.google.cloud.mcp.AuthTokenGetter;
2219
import com.google.cloud.mcp.McpToolboxClient;
23-
import java.io.FileInputStream;
24-
import java.util.Collections;
2520
import java.util.Map;
26-
import java.util.concurrent.CompletableFuture;
2721

2822
/**
29-
* Sample Application to demonstrate the usage of the MCP Toolbox Java SDK. Covers: Global Auth,
30-
* Parameterized Auth, Discovery, Simple Tool, Authenticated Tool, Parameter Binding.
23+
* A minimal example demonstrating client initialization, tool discovery, and invocation.
3124
*/
3225
public class ExampleUsage {
3326
public static void main(String[] args) {
34-
// CONFIGURATION
3527
String targetUrl = System.getProperty("toolbox.url", "YOUR_TOOLBOX_SERVICE_ENDPOINT");
36-
37-
// Match the Service URL if using Cloud Run OIDC
38-
String tokenAudience = System.getProperty("toolbox.tokenAudience", targetUrl);
39-
40-
// --------------------------------------------------------------------------------
41-
// AUTHENTICATION SETUP
42-
// --------------------------------------------------------------------------------
43-
// FOR LOCAL DEVELOPMENT: Use a Service Account Key JSON file.
44-
// FOR PRODUCTION (Cloud Run): Comment out the 'keyPath' logic and use ADC directly.
45-
// --------------------------------------------------------------------------------
46-
47-
String keyPath = System.getProperty("toolbox.keyPath", "YOUR_CREDENTIALS_JSON_FILE_PATH.json");
48-
49-
System.out.println("--- Starting MCP Toolbox Integration Test ---");
50-
System.out.println("Target Server: " + targetUrl);
51-
52-
try {
53-
System.out.println(" [Init] Fetching ID Token...");
54-
55-
GoogleCredentials credentials;
56-
57-
// --- OPTION A: LOCAL DEV (Explicit Key File) ---
58-
if (keyPath != null && !keyPath.isEmpty()) {
59-
System.out.println(" [Auth] Using Service Account Key File: " + keyPath);
60-
credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
61-
}
62-
// --- OPTION B: PRODUCTION (ADC) ---
63-
else {
64-
System.out.println(" [Auth] Using Application Default Credentials (ADC)");
65-
credentials = GoogleCredentials.getApplicationDefault();
66-
}
67-
68-
if (!(credentials instanceof IdTokenProvider)) {
69-
throw new RuntimeException("Loaded credentials do not support ID Tokens.");
70-
}
71-
72-
// Generate Token for the specified Audience
73-
String idToken =
74-
((IdTokenProvider) credentials)
75-
.idTokenWithAudience(tokenAudience, Collections.emptyList())
76-
.getTokenValue();
77-
System.out.println(" [Debug] Token Generated.");
78-
79-
// Initialize Client with Global Auth (Applies to ALL calls - Gate 1)
80-
McpToolboxClient client =
81-
McpToolboxClient.builder().baseUrl(targetUrl).apiKey(idToken).build();
82-
83-
// STEP 1: TEST DISCOVERY METHODS
84-
client
85-
.listTools()
86-
.thenCompose(
87-
tools -> {
88-
System.out.println("\n[1] listTools(): Success. Found " + tools.size() + " tools.");
89-
return client.loadToolset();
90-
})
91-
.thenCompose(
92-
tools -> {
93-
System.out.println("[2] loadToolset() (Alias): Success.");
94-
return client
95-
.loadToolset("retail")
96-
.handle(
97-
(res, ex) -> {
98-
if (ex == null)
99-
System.out.println(
100-
"[3] loadToolset('retail'): Found " + res.size() + " tools.");
101-
else
102-
System.out.println(
103-
"[3] loadToolset('retail'): Skipped (Not configured on server).");
104-
return null;
105-
});
106-
})
107-
.thenCompose(
108-
ignore -> {
109-
110-
// STEP 2: INVOKE TOOL WITHOUT EXTRA AUTH
111-
System.out.println("\n[4] Testing Simple Tool: 'get-retail-facet-filters'...");
112-
return client.invokeTool("get-retail-facet-filters", Map.of());
113-
})
114-
.thenCompose(
115-
result -> {
116-
System.out.println(
117-
" -> Result: " + (result.content() != null ? "Received Data" : "Empty"));
118-
119-
// STEP 3: INVOKE TOOL WITH AUTHENTICATED PARAMETERS
120-
System.out.println("\n[5] Testing Authenticated Tool: 'get-toy-price'...");
121-
122-
// Define the getter for the 'google_auth' service
123-
AuthTokenGetter toolAuthGetter = () -> CompletableFuture.completedFuture(idToken);
124-
125-
// Load using the sophisticated overload
126-
return client.loadTool("get-toy-price", Map.of("google_auth", toolAuthGetter));
127-
})
128-
.thenCompose(
129-
tool -> {
130-
System.out.println(" -> Loaded Tool: " + tool.definition().description());
131-
132-
// STEP 4: TEST BINDING PARAMETERS SEQUENTIALLY
133-
System.out.println("\n[A] Executing UNBOUND (Runtime arg: 'barbie')...");
134-
135-
return tool.execute(Map.of("description", "barbie"))
136-
.thenCompose(
137-
result1 -> {
138-
if (result1.content() != null && !result1.content().isEmpty()) {
139-
System.out.println(
140-
" -> Result (Unbound): " + result1.content().get(0).text());
141-
}
142-
143-
// NOW bind the parameter
144-
System.out.println("\n[B] Binding 'description' to 'soft toy'...");
145-
tool.bindParam("description", "soft toy");
146-
147-
System.out.println(
148-
" -> Executing BOUND (Runtime arg: 'barbie' - should be"
149-
+ " IGNORED)...");
150-
// We pass 'barbie', but expecting 'soft toy' price because of binding
151-
// override
152-
return tool.execute(Map.of("description", "barbie"));
153-
});
154-
})
155-
.thenAccept(
156-
result -> {
157-
System.out.println("\n[6] Final Result (Bound):");
158-
if (result.isError()) {
159-
System.err.println("Tool execution failed: " + result.content().get(0).text());
160-
} else if (result.content() != null && !result.content().isEmpty()) {
161-
String output = result.content().get(0).text();
162-
System.out.println(
163-
" " + output.substring(0, Math.min(output.length(), 200)) + "...");
164-
} else {
165-
System.out.println(" Empty Response");
166-
}
167-
})
168-
.exceptionally(
169-
ex -> {
170-
System.err.println("\n!!! TEST FAILED !!!");
171-
ex.printStackTrace();
172-
return null;
173-
})
174-
.join();
175-
176-
} catch (Exception e) {
177-
e.printStackTrace();
178-
}
179-
System.out.println("\n--- Test Suite Complete ---");
28+
String apiKey = System.getProperty("toolbox.apiKey", "YOUR_API_KEY");
29+
30+
System.out.println("--- Starting MCP Toolbox Simple Example ---");
31+
System.out.println("Connecting to: " + targetUrl);
32+
33+
// Initialize the client
34+
McpToolboxClient client =
35+
McpToolboxClient.builder().baseUrl(targetUrl).apiKey(apiKey).build();
36+
37+
// 1. List available tools
38+
client
39+
.listTools()
40+
.thenAccept(
41+
tools -> {
42+
System.out.println("Available tools: " + tools.size());
43+
for (String toolName : tools.keySet()) {
44+
System.out.println(" - " + toolName);
45+
}
46+
47+
// 2. Invoke a simple tool
48+
if (tools.containsKey("get-retail-facet-filters")) {
49+
System.out.println("\nInvoking 'get-retail-facet-filters'...");
50+
client
51+
.invokeTool("get-retail-facet-filters", Map.of())
52+
.thenAccept(
53+
result -> {
54+
System.out.println("Result: " + result.content().get(0).text());
55+
})
56+
.join();
57+
}
58+
})
59+
.join();
60+
61+
System.out.println("\n--- Simple Example Complete ---");
18062
}
18163
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cloudcode.simple;
18+
19+
import com.google.auth.oauth2.GoogleCredentials;
20+
import com.google.auth.oauth2.IdTokenProvider;
21+
import com.google.cloud.mcp.AuthTokenGetter;
22+
import com.google.cloud.mcp.McpToolboxClient;
23+
import java.io.FileInputStream;
24+
import java.util.Collections;
25+
import java.util.Map;
26+
import java.util.concurrent.CompletableFuture;
27+
28+
/** Example demonstrating how to use parameter bindings and authenticated tool methods. */
29+
public class ParameterBindingUsage {
30+
public static void main(String[] args) {
31+
// CONFIGURATION
32+
String targetUrl = System.getProperty("toolbox.url", "YOUR_TOOLBOX_SERVICE_ENDPOINT");
33+
34+
// Match the Service URL if using Cloud Run OIDC
35+
String tokenAudience = System.getProperty("toolbox.tokenAudience", targetUrl);
36+
37+
// --------------------------------------------------------------------------------
38+
// AUTHENTICATION SETUP
39+
// --------------------------------------------------------------------------------
40+
// FOR LOCAL DEVELOPMENT: Use a Service Account Key JSON file.
41+
// FOR PRODUCTION (Cloud Run): Comment out the 'keyPath' logic and use ADC directly.
42+
// --------------------------------------------------------------------------------
43+
44+
String keyPath = System.getProperty("toolbox.keyPath", "YOUR_CREDENTIALS_JSON_FILE_PATH.json");
45+
46+
System.out.println("--- Starting MCP Toolbox Parameter Binding Example ---");
47+
System.out.println("Target Server: " + targetUrl);
48+
49+
try {
50+
System.out.println(" [Init] Fetching ID Token...");
51+
52+
GoogleCredentials credentials;
53+
54+
// --- OPTION A: LOCAL DEV (Explicit Key File) ---
55+
if (keyPath != null && !keyPath.isEmpty()) {
56+
System.out.println(" [Auth] Using Service Account Key File: " + keyPath);
57+
credentials = GoogleCredentials.fromStream(new FileInputStream(keyPath));
58+
}
59+
// --- OPTION B: PRODUCTION (ADC) ---
60+
else {
61+
System.out.println(" [Auth] Using Application Default Credentials (ADC)");
62+
credentials = GoogleCredentials.getApplicationDefault();
63+
}
64+
65+
if (!(credentials instanceof IdTokenProvider)) {
66+
throw new RuntimeException("Loaded credentials do not support ID Tokens.");
67+
}
68+
69+
// Generate Token for the specified Audience
70+
String idToken =
71+
((IdTokenProvider) credentials)
72+
.idTokenWithAudience(tokenAudience, Collections.emptyList())
73+
.getTokenValue();
74+
System.out.println(" [Debug] Token Generated.");
75+
76+
// Initialize Client
77+
McpToolboxClient client =
78+
McpToolboxClient.builder().baseUrl(targetUrl).apiKey(idToken).build();
79+
80+
// STEP 1: LOAD TOOL WITH AUTH PROVIDERS
81+
System.out.println("\n[1] Testing Authenticated Tool: 'get-toy-price'...");
82+
83+
// Define the getter for the 'google_auth' service
84+
AuthTokenGetter toolAuthGetter = () -> CompletableFuture.completedFuture(idToken);
85+
86+
client
87+
.loadTool("get-toy-price", Map.of("google_auth", toolAuthGetter))
88+
.thenCompose(
89+
tool -> {
90+
System.out.println(" -> Loaded Tool: " + tool.definition().description());
91+
92+
// STEP 2: TEST BINDING PARAMETERS SEQUENTIALLY
93+
System.out.println("\n[A] Executing UNBOUND (Runtime arg: 'barbie')...");
94+
95+
return tool.execute(Map.of("description", "barbie"))
96+
.thenCompose(
97+
result1 -> {
98+
if (result1.content() != null && !result1.content().isEmpty()) {
99+
System.out.println(
100+
" -> Result (Unbound): " + result1.content().get(0).text());
101+
}
102+
103+
// NOW bind the parameter
104+
System.out.println("\n[B] Binding 'description' to 'soft toy'...");
105+
tool.bindParam("description", "soft toy");
106+
107+
System.out.println(
108+
" -> Executing BOUND (Runtime arg: 'barbie' - should be"
109+
+ " IGNORED)...");
110+
// We pass 'barbie', but expecting 'soft toy' price because of binding
111+
// override
112+
return tool.execute(Map.of("description", "barbie"));
113+
});
114+
})
115+
.thenAccept(
116+
result -> {
117+
System.out.println("\n[2] Final Result (Bound):");
118+
if (result.isError()) {
119+
System.err.println("Tool execution failed: " + result.content().get(0).text());
120+
} else if (result.content() != null && !result.content().isEmpty()) {
121+
String output = result.content().get(0).text();
122+
System.out.println(" Result content: " + output);
123+
}
124+
})
125+
.join();
126+
127+
} catch (Exception e) {
128+
e.printStackTrace();
129+
}
130+
System.out.println("\n--- Parameter Binding Example Complete ---");
131+
}
132+
}

src/test/java/com/google/cloud/mcp/e2e/ToolboxActualServerVerifyTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,61 @@ public void testBulkToolsetExampleFlow() throws Exception {
214214
System.clearProperty("toolbox.keyPath");
215215
}
216216
}
217+
218+
@Test
219+
public void testParameterBindingExampleFlow() throws Exception {
220+
// Set System properties to configure ParameterBindingUsage to hit our local port 8099
221+
System.setProperty("toolbox.url", "http://localhost:" + PORT + "/mcp");
222+
System.setProperty("toolbox.keyPath", "");
223+
224+
// 1. Programmatically compile ParameterBindingUsage.java on the fly
225+
File sourceFile = new File("example/src/main/java/cloudcode/simple/ParameterBindingUsage.java");
226+
assertTrue(sourceFile.exists(), "ParameterBindingUsage.java does not exist");
227+
228+
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
229+
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
230+
String classpath = System.getProperty("java.class.path");
231+
232+
List<String> optionList = new ArrayList<>();
233+
optionList.add("-classpath");
234+
optionList.add(classpath);
235+
optionList.add("-d");
236+
optionList.add("target/test-classes");
237+
238+
Iterable<? extends JavaFileObject> compilationUnits =
239+
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
240+
boolean compileSuccess =
241+
compiler.getTask(null, fileManager, null, optionList, null, compilationUnits).call();
242+
fileManager.close();
243+
244+
assertTrue(compileSuccess, "Compilation of ParameterBindingUsage.java failed.");
245+
246+
// 2. Load the compiled ParameterBindingUsage class
247+
Class<?> clazz = Class.forName("cloudcode.simple.ParameterBindingUsage");
248+
249+
// Mock GoogleCredentials statically to return a dummy IdTokenProvider
250+
try (MockedStatic<GoogleCredentials> mockedCredentials =
251+
Mockito.mockStatic(GoogleCredentials.class)) {
252+
GoogleCredentials mockCreds =
253+
Mockito.mock(
254+
GoogleCredentials.class,
255+
Mockito.withSettings().extraInterfaces(IdTokenProvider.class));
256+
IdToken mockToken = Mockito.mock(IdToken.class);
257+
Mockito.when(mockToken.getTokenValue()).thenReturn("dummy-token");
258+
Mockito.when(
259+
((IdTokenProvider) mockCreds)
260+
.idTokenWithAudience(Mockito.anyString(), Mockito.anyList()))
261+
.thenReturn(mockToken);
262+
263+
mockedCredentials.when(GoogleCredentials::getApplicationDefault).thenReturn(mockCreds);
264+
265+
// Execute the actual example class's main method directly!
266+
System.out.println("Executing ParameterBindingUsage.main...");
267+
clazz.getMethod("main", String[].class).invoke(null, (Object) new String[0]);
268+
System.out.println("ParameterBindingUsage.main completed successfully.");
269+
} finally {
270+
System.clearProperty("toolbox.url");
271+
System.clearProperty("toolbox.keyPath");
272+
}
273+
}
217274
}

0 commit comments

Comments
 (0)