I'm at it again, playing with code in my spare time. I figured it was high time I started looking into all the fuss about AI agents. So naturally, I decided to build my own—in Java, because apparently, I like torturing myself.
I have no good reason for doing this, nor do I want to build anything "useful." I'm just having fun. But first (embarrassed), I had to learn what an AI agent even is.
So, What is an AI Agent, Anyway? Turns out, it's not just a simple API call. It's a system with a goal that can Sense (get a request), Think (plan steps, pick tools), and Act (run code, write files). The LLM is just the "brain" part of it. So something smarter than an API call. It's a loop through the Sense --> Think --> Act steps. Nice.
My (Pointless) Project: Meet CodeBro
Now that I had the theory, I needed a project. I decided to build an agent that takes a programming problem as its input, then returns the logic, implementation, and tests for how to solve it.
I shall call him CodeBro.
Does anyone need this? No. Like I said, I'm just messing around.
So, what do I need?
- An LLM: I chose Claude, for no real reason. First problem solved.
- The Flow: The initial concept is dead simple: Input → HTTP Request to Claude → Response.
To get something working, I'll need a few building blocks in Java:
- An APIClient (preferably with an interface, because the person who has to maintain this might be me).
- A PromptBuilder class to, well, build the prompt.
- The main CodeBro "agent" or orchestrator class.
- A Main class to actually run the thing (a CLI seems fancy enough).
Hitting the API
Okay, I have the basic idea. I need to figure out the API. As you'd expect, Anthropic has documentation, and I'll want the Messages API.
The curl command looks something like this:
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data \
'{
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello, world"}
]
}'
I could definitely hit this using Java's HttpClient, but in the docs, I see there's a Java SDK. Sick. Don't you just love popular languages?
To start, let's define an interface for the client. This will help with testing (if I get to that) and just in case I want to swap things out later.
public interface APIClient {
String sendMessage(String prompt) throws Exception;
}
The API Client
Now let's implement this bad boy. Thanks to the Java SDK, I don't have to deal with manual JSON parsing (because let's be honest, string manipulation in Java is about as fun as a root canal).
public class AnthropicSDKClient implements APIClient {
private final AnthropicClient client;
private static final String MODEL = "claude-sonnet-4-20250514";
public AnthropicSDKClient(String apiKey) {
this.client = AnthropicOkHttpClient.builder()
.apiKey(apiKey)
.build();
}
@Override
public String sendMessage(String userMessage) throws IOException, InterruptedException {
try {
TextBlock textBlock = TextBlock.builder()
.text(userMessage)
.build();
MessageParam messageParam = MessageParam.builder()
.role(MessageParam.Role.USER)
.content(ContentBlock.ofText(textBlock))
.build();
MessageCreateParams params = MessageCreateParams.builder()
.model(MODEL)
.maxTokens(4096L)
.messages(Collections.singletonList(messageParam))
.build();
Message message = client.messages().create(params);
return message.content().stream()
.filter(ContentBlock::isText)
.map(block -> block.asText().text())
.findFirst()
.orElseThrow(() -> new IOException("No text content in response"));
} catch (Exception e) {
throw new IOException("SDK Error: " + e.getMessage(), e);
}
}
}
The Prompt Builder
Cool. So that's the API client sorted. But I want my prompts to be good, so let's make a dedicated class for that.
public class PromptBuilder {
public String buildTutorialPrompt(String problemStatement) {
return """
You are an expert coding tutor. Analyze the following problem and provide a comprehensive tutorial.
Structure your response with these sections:
1. PROBLEM ANALYSIS
- Restate the problem
- Identify constraints
- Determine inputs/outputs
2. SOLUTION STRATEGY
- Multiple approaches
- Trade-offs
- Best approach and why
3. ALGORITHM DESIGN
- Step-by-step algorithm
- Time and space complexity
4. IMPLEMENTATION
- Complete working Java code
- Comments and edge cases
5. TEST CASES
- JUnit test code
- Example cases and edge cases
Here's the problem:
""" + problemStatement;
}
}
The Orchestrator: CodeBro Himself
Now for the main orchestrator—CodeBro himself:
public class CodeBro {
private final APIClient apiClient;
private final PromptBuilder promptBuilder;
public CodeBro(APIClient apiClient, PromptBuilder promptBuilder) {
this.apiClient = apiClient;
this.promptBuilder = promptBuilder;
}
public String solveProblem(String problemStatement) throws Exception {
String prompt = promptBuilder.buildTutorialPrompt(problemStatement);
String response = apiClient.sendMessage(prompt);
return response;
}
}
Look at that. Clean. Simple. Probably has bugs. But it compiles, which is basically the same as working, right?
Bringing CodeBro to Life (The CLI)
Now I need a way to actually use this thing. Let's throw together a CLI because I'm fancy like that:
public class Main {
public static void main(String[] args) {
try {
// Load API key from config.properties (don't commit your keys, people)
Properties props = new Properties();
try (InputStream input = Main.class.getClassLoader()
.getResourceAsStream("config.properties")) {
props.load(input);
}
String apiKey = props.getProperty("anthropic.api.key");
APIClient apiClient = new AnthropicSDKClient(apiKey);
CodeBro codeBro = new CodeBro(apiClient, new PromptBuilder());
Scanner scanner = new Scanner(System.in);
System.out.println("\n=================================");
System.out.println(" Welcome to CodeBro! 🤝");
System.out.println(" Your AI Coding Buddy");
System.out.println("=================================\n");
while (true) {
System.out.println("Paste your coding problem below.");
System.out.println("Type 'END' on a new line when done, or 'quit' to exit.\n");
StringBuilder problemBuilder = new StringBuilder();
while (true) {
String line = scanner.nextLine();
if (line.trim().equalsIgnoreCase("quit")) {
System.out.println("\nThanks for using CodeBro! Happy coding! 👋");
scanner.close();
return;
}
if (line.trim().equalsIgnoreCase("END")) {
break;
}
problemBuilder.append(line).append("\n");
}
String problem = problemBuilder.toString().trim();
if (problem.isEmpty()) {
System.out.println("No problem entered. Try again.\n");
continue;
}
System.out.println("\n🤔 CodeBro is thinking...\n");
String tutorial = codeBro.solveProblem(problem);
System.out.println(tutorial);
System.out.println("\n" + "=".repeat(50) + "\n");
System.out.print("Solve another problem? (y/n): ");
String answer = scanner.nextLine().trim().toLowerCase();
if (!answer.equals("y") && !answer.equals("yes")) {
System.out.println("\nThanks for using CodeBro! Happy coding! 👋");
break;
}
System.out.println();
}
scanner.close();
} catch (Exception e) {
System.err.println("Something went horribly wrong: " + e.getMessage());
e.printStackTrace();
}
}
}
And... it works! I threw the classic Two Sum problem at it and got back a full tutorial with multiple approaches, complexity analysis, working code, and tests. Not too shabby.
But Wait, There's More
Now here's where it gets interesting. I started thinking: "What if CodeBro could actually do things?" Like, what if instead of just telling me the solution, it could create a full Maven project with the code and tests already set up?
This is where I need to clarify something because I got confused at first too.
Tools vs MCP (They're Not the Same Thing)
Tools are a feature baked into Claude's API. You tell Claude "hey, you can call these functions" and Claude decides when to use them. It's like giving Claude a toolbox:
// You define a tool
Tool writeFileTool = Tool.builder()
.name("write_file")
.description("Writes content to a file")
.inputSchema(/* parameters */)
.build();
// Claude can now use it
"I'll write that to a file for you" → calls write_file tool
MCP (Model Context Protocol) is Anthropic's open standard for organizing and sharing these tools. Think of it like this:
- Tools = The concept of "Claude can call functions"
- MCP = A standardized way to package and distribute those functions
Instead of everyone writing their own write_file tool from scratch, MCP gives you pre-built "servers" that provide common tools:
- Filesystem server (read, write, list files)
- Database server (query, insert, update)
- Web server (fetch URLs, search)
- Git server (commit, push, diff)
So MCP uses the tools feature, but adds structure and reusability. It's like the difference between writing your own HTTP library vs. just using an existing one.
Why This Matters for CodeBro
With tools alone, I'd have to manually define every file operation, every command execution, etc. With MCP, I can just plug into existing servers and boom—CodeBro can suddenly interact with the filesystem, run commands, whatever.
The flow would look like:
You: "Solve two-sum and create a project for it"
CodeBro: [generates solution]
CodeBro: [uses MCP filesystem server → write_file("pom.xml", ...)]
CodeBro: [uses MCP filesystem server → write_file("Solution.java", ...)]
CodeBro: [uses MCP filesystem server → write_file("SolutionTest.java", ...)]
CodeBro: [uses MCP command server → execute("mvn test")]
CodeBro: "✅ Project created at ./two-sum/ and all tests pass!"
Claude decides when to use these tools based on what you asked for. You don't explicitly tell it "now write this file"—it figures that out.
This is where I'm currently experimenting. Because an AI agent that goes from "here's a LeetCode problem" to "here's a fully tested, runnable Maven project" in seconds? That's the kind of useless thing I go for.
The Point
Look, CodeBro isn't revolutionary. It's not going to change the world. But, I had fun. I built something. I learned something. And now I'm writing about it, which means maybe you'll go build something too.
So go mess around with code. Build something useless. Torture yourself with Java if that's your thing. The best way to learn this stuff is to just... do it.
Full code on GitHub
Now if you'll excuse me, I'm going to go teach CodeBro how to commit its own solutions to Git. What could possibly go wrong?
Top comments (0)