import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallback;
import tools.jackson.databind.json.JsonMapper;
public class MagicHourSpring {
public static void main(String[] args) {
String key = System.getenv("MAGIC_HOUR_API_KEY");
String projectId = System.getenv("MAGIC_HOUR_IMAGE_PROJECT_ID");
if (key == null || key.isBlank() || projectId == null || projectId.isBlank()) {
throw new IllegalArgumentException("Set MAGIC_HOUR_API_KEY and a saved MAGIC_HOUR_IMAGE_PROJECT_ID.");
}
var transport = HttpClientStreamableHttpTransport.builder("https://mcp.magichour.ai")
.endpoint("/")
.clientBuilder(HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NEVER))
.requestBuilder(HttpRequest.newBuilder().header("Authorization", "Bearer " + key))
.build();
var client = McpClient.sync(transport).requestTimeout(Duration.ofSeconds(30)).build();
try {
client.initialize();
ToolCallback[] tools = SyncMcpToolCallbackProvider.builder().mcpClients(client).build().getToolCallbacks();
System.out.println("Discovered " + tools.length + " Spring AI tool callbacks.");
var account = payload(find(tools, "account_retrieve").call("{}"));
if (!(account.get("id") instanceof String) || !(account.get("credits") instanceof Number)) {
throw new IllegalStateException("Unexpected account response.");
}
System.out.println("Magic Hour authorization verified; account id:String, credits:Number.");
String arguments = JsonMapper.builder().build().writeValueAsString(Map.of("id", projectId));
var project = payload(find(tools, "image_projects_retrieve_details").call(arguments));
if (!projectId.equals(project.get("id")) || !(project.get("status") instanceof String)) {
throw new IllegalStateException("Unexpected saved-project response.");
}
System.out.println("Saved project status: " + project.get("status"));
} finally {
boolean closed = client.closeGracefully();
if (!closed) client.close();
System.out.println("MCP graceful close: " + closed);
}
}
private static ToolCallback find(ToolCallback[] tools, String name) {
return Arrays.stream(tools).filter(t -> name.equals(t.getToolDefinition().name())).findFirst()
.orElseThrow(() -> new IllegalStateException("Required MCP tool unavailable: " + name));
}
private static Map<?, ?> payload(String result) {
var mapper = JsonMapper.builder().build();
List<?> content = mapper.readValue(result, List.class);
for (Object item : content) {
if (item instanceof Map<?, ?> block && block.get("text") instanceof String text) {
return mapper.readValue(text, Map.class);
}
}
throw new IllegalStateException("MCP result has no JSON text content.");
}
}