Understanding MCP Through Raw STDIO Communication with David Parry - JVM Weekly vol. 161
This month: MCP demystified, garbage collection wisdom, processor architecture deep dives, and IDE productivity tricks.
Today I’m handing over the main feature to a guest author. Recently on the foojay.io, David Parry from Qodo published what I consider one of the best technical deep-dives on MCP I’ve ever read. If you’ve been scratching your head about how AI assistants actually communicate with external tools - not at the SDK level, but at the raw protocol level - this is the piece you’ve been waiting for.
I asked David (in person, as we are both on Jfokus Today!) whether he’d let me feature it in JVM Weekly. The answer was a “yes” ❤️.
Understanding MCP Through Raw STDIO Communication
Ever wondered how AI assistants like Claude actually communicate with external tools and services? While most tutorials focus on using pre-built SDKs and frameworks, this article takes a different approach—we’ll dissect a production MCP server built from scratch using only Java’s standard libraries and raw STDIO communication.
By stripping away all the abstractions and implementing the Model Context Protocol directly, we’ll uncover the surprisingly elegant mechanics that enable AI systems to discover, understand, and execute tools.
Whether you’re building AI integrations, debugging mysterious protocol errors, or simply curious about what really happens when an AI “uses a tool,” this deep dive will transform JSON-RPC messages flowing through stdin/stdout from abstract concepts into concrete, debuggable reality.
No frameworks, no magic—just the protocol in its purest form.
Want to build this yourself? This article is based on code from the Agent MCP Workshop, an instructor-led workshop that guides you through building a complete MCP server from scratch. The workshop includes hands-on exercises, detailed explanations, and practical examples that complement the concepts discussed in this article.
Understanding MCP Through Raw STDIO Communication
The Model Context Protocol (MCP) represents a paradigm shift in how AI systems interact with external tools and data sources. This article dives deep into the protocol’s STDIO-based communication layer, examining a real-world Java implementation built without frameworks to better understand how the protocol and communication actually work at the fundamental level.
By implementing MCP from scratch using only standard Java libraries, we gain invaluable insights into the protocol’s inner workings—insights often hidden by higher-level abstractions and frameworks. This bare-metal approach reveals the elegant simplicity underlying MCP’s powerful capabilities.
Why STDIO? The Power of Universal Communication
Before diving into the implementation, it’s crucial to understand why MCP chose STDIO (Standard Input/Output) as its primary transport mechanism. STDIO provides:
Universal compatibility: Every programming language can read from stdin and write to stdout
Process isolation: Natural security boundaries between the AI client and tool server
Simplicity: No network configuration, firewall rules, or authentication complexity
Debugging ease: Messages can be logged, inspected, and replayed
This implementation demonstrates these principles by building everything from scratch—no MCP SDK, no framework dependencies, just pure Java interacting with STDIO streams.
At the heart of any MCP implementation lies a robust transport layer. The Java implementation demonstrates a clean separation of concerns through its I/O handler architecture:
public class IOHandlerImpl implements IOHandler {
private final static LogFile logger = LogFileWriter.getInstance();
private final PrintWriter writer;
private final List<Consumer<String>> lineListeners;
private final AtomicBoolean running;
private final Gson gson = new Gson();
@Override
public void emit(Object message) {
String text = gson.toJson(message);
logger.log("[API][SENT]: " + text);
writer.println(text);
writer.flush();
}
@Override
public void startInputReader() {
if (running.get()) {
return; // Already running
}
try (Scanner scanner = new Scanner(System.in)) {
running.set(true);
while (running.get()) {
if (!scanner.hasNextLine()) {
running.set(false);
break;
}
String line = scanner.nextLine();
logger.log("[API][RECEIVED]" + line);
publishLine(line);
}
}
}
}This implementation showcases several critical design decisions:
Direct STDIO access: Reading from
System.inand writing toSystem.outwithout buffering frameworksThread-safe operations using
AtomicBooleanandCopyOnWriteArrayListEvent-driven architecture with listener patterns for incoming messages
Line-based protocol: Each JSON-RPC message is a complete line, enabling simple parsing
Comprehensive logging that writes to files (never stdout!) for debugging
The beauty of this approach is its transparency. When the server emits a message:
public void emit(Object message) {
String text = gson.toJson(message);
logger.log("[API][SENT]: " + text); // Log to file, not stdout!
writer.println(text); // Send to stdout
writer.flush(); // Ensure immediate delivery
}The message goes directly to stdout as a single line of JSON. No framing, no length prefixes, no binary protocols—just newline-delimited JSON that any tool can read and debug.
Understanding the JSON-RPC Message Flow
MCP uses JSON-RPC 2.0 over STDIO, which means every message is a self-contained JSON object on a single line. Let’s trace through an actual message exchange to see how this works:
Client → Server: Initialization Request
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"0.1.0","capabilities":{"roots":{},"sampling":{}},"clientInfo":{"name":"mcp-inspector","version":"0.1.0"}},"id":0}This single line contains everything needed for initialization. The server reads it from stdin using:
String line = scanner.nextLine();
logger.log("[API][RECEIVED]" + line);
publishLine(line); // Notify the routerServer → Client: Initialization Response
{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"0.1.0","capabilities":{"tools":{},"prompts":{},"resources":{}},"serverInfo":{"name":"agent-mcp-workshop","version":"0.0.1"}}}The server writes this response directly to stdout. No HTTP headers, no WebSocket frames - just a line of JSON followed by a newline character.
The Message Type Hierarchy
The implementation uses Java records to model the JSON-RPC message types:
public record JsonRpcRequest(
String jsonrpc,
Long id,
String method,
Object params
) {}
public record JsonRpcNotification(
String jsonrpc,
String method,
Object params
) {} // Note: No id field for notifications!
public record JsonRpcResponse(
String jsonrpc,
Long id,
Object result
) {}
public record JsonRpcErrorResponse(
String jsonrpc,
Long id,
JsonRpcError error
) {}This type system directly maps to the JSON-RPC 2.0 specification, making the protocol implementation clear and type-safe.
Every MCP session begins with a crucial initialization handshake. The implementation demonstrates how servers advertise their capabilities:
case INITIALIZE -> {
InitializeParams initializeParams = deserializer.deserializeParams(
message, InitializeParams.class
);
ClientCapabilities clientCapabilities = initializeParams.capabilities();
if (clientCapabilities.roots() != null) {
hasRoots = true;
}
if (clientCapabilities.sampling() != null) {
hasSampling = true;
}
InitializeResultBuilder builder = InitializeResultBuilder
.builder()
.withProtocolVersion(initializeParams.protocolVersion())
.withDefaultCapabilities()
.withDefaultServerInfo();
success(message.id(), builder.build());
}This approach allows servers to clearly declare what they support, enabling intelligent capability negotiation between clients and servers.
Bidirectional Communication: Beyond Request-Response
One of MCP’s powerful features is true bidirectional communication. The server can send requests to the client, not just respond to them. This implementation demonstrates this with the roots feature:
case NOTIFICATIONS_INITIALIZED -> {
// Server initiates a request to the client!
if (hasRoots) {
io.emit(rootsRequest);
}
}The rootsRequest is a server-initiated message:
private static final JsonRpcRequest rootsRequest = new JsonRpcRequest(
JSON_RPC_VERSION,
ROOTS_REQUEST_ID, // Negative ID to avoid conflicts
"roots/list",
null
);When the client responds, the server processes it just like any other response:
private void process(JsonRpcResponse message) {
if (ROOTS_REQUEST_ID.equals(message.id())) {
RootsResponse rootsResponse = deserializer.deserializeResult(
message, RootsResponse.class
);
roots.clear();
for (Root root : rootsResponse.roots()) {
roots.add(root.uri());
}
}
}This bidirectional flow over STDIO demonstrates that MCP isn’t limited to simple request-response patterns—it’s a full-duplex protocol where both parties can initiate communication.
The routing mechanism demonstrates how MCP servers handle different message types:
public void route(String message) {
if (message == null || message.isEmpty()) {
return;
}
Object object = deserializer.deserialize(message);
switch (object) {
case JsonRpcRequest request -> process(request);
case JsonRpcNotification notification -> process(notification);
case JsonRpcResponse successResponse -> process(successResponse);
case JsonRpcErrorResponse errorResponse -> process(errorResponse);
default -> logger.log("Unknown message type: " + object);
}
}This pattern matching approach (using Java’s modern switch expressions) creates a clean, extensible routing system. Each message type has its own processing logic:
private void process(JsonRpcRequest message) {
UniqueKeys uniqueKey = UniqueKeys.fromValue(message.method());
switch (uniqueKey) {
case INITIALIZE -> { /* ... */ }
case PROMPTS_LIST -> { /* ... */ }
case PROMPTS_GET -> { /* ... */ }
case TOOLS_LIST -> { /* ... */ }
case TOOLS_CALL -> { /* ... */ }
case RESOURCES_LIST -> { /* ... */ }
case RESOURCES_READ -> { /* ... */ }
case PING -> { /* ... */ }
default -> logger.log("Unhandled RpcRequest method: " + uniqueKey);
}
}The Complete STDIO Loop: Putting It All Together
Let’s trace through a complete interaction to see how STDIO communication enables MCP:
1. Server Startup
public void start() {
// Register the router to handle incoming lines
this.io.addLineListener(router::route);
// Start reading from stdin in a separate thread
this.io.startInputReader();
// Keep the main thread alive
keepRunning();
}2. Client Connects (via process spawn)
The client spawns the server process and connects to its stdin/stdout:
java -jar agent-mcp-workshop-0.0.1.jar3. Message Exchange Begins
→ [stdin] {"jsonrpc":"2.0","method":"initialize","params":{...},"id":0}
← [stdout] {"jsonrpc":"2.0","id":0,"result":{...}}
→ [stdin] {"jsonrpc":"2.0","method":"notifications/initialized"}
← [stdout] {"jsonrpc":"2.0","method":"roots/list","id":-1000}
→ [stdin] {"jsonrpc":"2.0","id":-1000,"result":{"roots":[...]}}Each arrow represents a complete line written to stdin or stdout. The server never writes partial messages or multiple messages on one line—maintaining the protocol’s simplicity.
4. Error Handling Without Exceptions
Since STDIO doesn’t have error channels like HTTP status codes, errors are part of the protocol:
success(message.id(), ToolCallResultBuilder
.builder()
.addTextContent("Tool not found: " + toolCallParams.name())
.asError()
.build());This creates a valid response with an error flag, keeping the STDIO stream clean and the protocol predictable.
The keyword search tool demonstrates how to create self-describing, executable functionality:
public class KeyWordSearch implements Tool {
@Override
public String name() {
return "key_word_search";
}
@Override
public String description() {
return "Searches for a specified keyword across all files in a project. " +
"Returns the total count of matches and the absolute file paths " +
"of the files containing the keyword.";
}
@Override
public InputSchema schema() {
InputSchemaBuilder builder = InputSchemaBuilder
.builder()
.withType("object")
.addProperty(PropertySchemaBuilder
.builder()
.withKey("keyword")
.withType("string")
.withDescription("the keyword to search for in a file")
.required());
if (this.roots == null || this.roots.isEmpty()) {
builder.addProperty(PropertySchemaBuilder
.builder()
.withKey("root_directory")
.withType("string")
.withDescription("The absolute path to the root directory")
.required());
}
return builder.build();
}
}The schema definition is particularly important—it enables AI clients to understand exactly how to invoke the tool. The actual implementation showcases robust file handling:
public ToolCallResult call(ToolCallParams toolCallParams) {
String keyword = toolCallParams.arguments().get("keyword");
ToolCallResultBuilder builder = ToolCallResultBuilder.builder();
if (roots.isEmpty()) {
builder.addTextContent("No root directories specified for search.");
builder.asError();
} else {
List<ContentItem> contentItems = searchKeywordInDirectories(roots, keyword);
builder.withContent(contentItems);
}
return builder.build();
}Debugging STDIO Communication
One of the advantages of building MCP without frameworks is the ability to debug at the protocol level. The implementation includes comprehensive logging:
logger.log("[API][RECEIVED]" + line); // Every incoming message
logger.log("[API][SENT]: " + text); // Every outgoing messageThis creates a complete trace of the STDIO communication:
[2024-01-15 10:23:45] [API][RECEIVED]{"jsonrpc":"2.0","method":"tools/list","id":5}
[2024-01-15 10:23:45] [API][SENT]: {"jsonrpc":"2.0","id":5,"result":{"tools":[{"name":"key_word_search","description":"Searches for a specified keyword...","inputSchema":{...}}]}}
[2024-01-15 10:23:46] [API][RECEIVED]{"jsonrpc":"2.0","method":"tools/call","params":{"name":"key_word_search","arguments":{"keyword":"TODO"}},"id":6}
[2024-01-15 10:23:47] [API][SENT]: {"jsonrpc":"2.0","id":6,"result":{"content":[{"text":"/src/main/java/Server.java, keyword_count=3","type":"text"}]}}This trace can be replayed for testing, analyzed for performance, or used to debug protocol issues—something much harder with framework-heavy implementations.
case RESOURCES_LIST -> {
ResourcesListResultBuilder builder = ResourcesListResultBuilder
.builder()
.withResources(JavadocResources.loadAllHtmlResourcesFromFolder(
"javadoc/com/workshop/mcp/spec"
))
.withNextCursor("pageNext");
success(message.id(), builder.build());
}The JavadocResources class shows sophisticated resource handling:
public static String readResourceContent(String resourcePath) throws IOException {
try (InputStream inputStream = JavadocResources.class.getClassLoader()
.getResourceAsStream(resourcePath)) {
if (inputStream == null) {
throw new IOException("Resource not found: " + resourcePath);
}
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream))) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
}This approach allows servers to bundle and serve documentation, configurations, or any other static content directly from the JAR file—all transmitted as JSON over STDIO. When a client requests a resource:
→ {"jsonrpc":"2.0","method":"resources/read","params":{"uri":"javadoc/com/workshop/mcp/spec/Tool.html"},"id":10}
← {"jsonrpc":"2.0","id":10,"result":{"contents":[{"uri":"javadoc/com/workshop/mcp/spec/Tool.html","mimeType":"text/html","text":"<!DOCTYPE HTML>..."}]}}The entire HTML document is embedded in the JSON response, properly escaped and transmitted as a single line. This demonstrates STDIO’s flexibility—it can handle everything from simple method calls to large content transfers.
Prompts: Bridging Human Intent and Tool Execution
The prompt system creates user-friendly interfaces for tools:
case PROMPTS_LIST -> {
KeyWordSearch keyWordSearch = new KeyWordSearch(this.roots);
PromptsListResultBuilder builder = PromptsListResultBuilder
.builder()
.withPrompt("search_keyword",
"Creates a prompt, to search for a word using the " +
keyWordSearch.name() + " tool.")
.withPromptArgument("keyword", "The word to search for", true)
.withNextCursor("nextPage");
success(message.id(), builder.build());
}When retrieving a prompt, the server can provide intelligent guidance:
case PROMPTS_GET -> {
PromptsGetParams params = deserializer.deserializeParams(
message, PromptsGetParams.class
);
PromptsGetResultBuilder builder = PromptsGetResultBuilder
.builder()
.withDescription("keyword")
.addTextMessage("user", KEY_WORD_MESSAGE, params.arguments());
success(message.id(), builder.build());
}Advanced Features: Notification Handling
The implementation includes sophisticated notification handling:
private void process(JsonRpcNotification message) {
UniqueKeys uniqueKey = UniqueKeys.fromValue(message.method());
switch (uniqueKey) {
case NOTIFICATIONS_INITIALIZED -> {
if (hasRoots) {
io.emit(rootsRequest);
}
}
case NOTIFICATIONS_ROOTS_LIST_CHANGED -> {
io.emit(rootsRequest);
}
case NOTIFICATION_CANCELLED -> {
NotificationCancelledParams params = deserializer.deserializeParams(
message, NotificationCancelledParams.class
);
logger.log("Notification cancelled reason " + params.reason());
}
default -> logger.log("Unhandled notification method: " + uniqueKey);
}
}This shows how servers can react to client-side events and maintain synchronized state.
Server Lifecycle Management
The Server class demonstrates robust lifecycle management:
public class Server {
private final AtomicBoolean isShuttingDown = new AtomicBoolean(false);
private final CountDownLatch shutdownLatch;
public void start() {
try {
this.io.addLineListener(router::route);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (isShuttingDown.compareAndSet(false, true)) {
stop();
logger.close();
shutdownLatch.countDown();
}
}));
this.io.startInputReader();
keepRunning();
} catch (Exception e) {
logger.log("Error in main method", e);
stop();
System.exit(0);
}
}
}The use of shutdown hooks and countdown latches ensures graceful termination even in complex scenarios.
Key Architectural Patterns
1. Record Types for Protocol Messages
public record JsonRpcRequest(
String jsonrpc,
Long id,
String method,
Object params
) {}Java records provide immutable, self-documenting protocol structures.
2. Builder Pattern for Complex Responses
InitializeResult result = InitializeResultBuilder.builder()
.withProtocolVersion("1.0")
.withServerInfo("my-server", "1.0.0")
.withDefaultCapabilities()
.build();Builders ensure valid, complete responses while maintaining readability.
3. Enum-Based Method Routing
public enum UniqueKeys {
INITIALIZE("initialize"),
NOTIFICATIONS_INITIALIZED("notifications/initialized"),
PROMPTS_LIST("prompts/list"),
// ... more methods
public static UniqueKeys fromValue(String value) {
for (UniqueKeys key : UniqueKeys.values()) {
if (key.value.equalsIgnoreCase(value)) {
return key;
}
}
return NOT_FOUND;
}
}This approach provides type-safe method handling with built-in validation.
Why Building Without Frameworks Matters
This implementation deliberately avoids MCP SDKs or frameworks to reveal important insights:
1. The Protocol is Simple
At its core, MCP is just JSON-RPC 2.0 over newline-delimited streams. No magic, no hidden complexity—just structured messages over STDIO.
2. Debugging is Straightforward
Without framework abstractions, every message is visible and every routing decision is explicit. Problems can be traced directly to protocol-level issues.
3. Portability is Maximized
This implementation could be ported to any language that can read stdin and write stdout—no framework dependencies to worry about.
4. Understanding is Complete
By implementing from scratch, developers gain deep understanding of:
How capability negotiation works
Why message IDs matter
How bidirectional communication flows
What makes MCP transport-agnostic
5. Performance is Transparent
Without framework overhead, the performance characteristics are clear:
Message parsing time = JSON deserialization
Routing overhead = switch statement
I/O latency = STDIO buffering
Conclusion: The Elegance of STDIO-Based Protocols
This deep dive into a framework-free MCP implementation reveals the elegant simplicity at the protocol’s heart. By using STDIO as the transport layer, MCP achieves:
Universal compatibility: Any language that can read and write text can implement MCP
Process isolation: Natural security boundaries without complex authentication
Debugging transparency: Every message is visible and reproducible
Deployment simplicity: No ports, no certificates, no network configuration
The implementation demonstrates that building AI-integrated tools doesn’t require complex frameworks or abstractions. At its core, MCP is about structured communication—JSON messages flowing over STDIO streams, enabling AI systems to discover and use tools in a standardized way.
By understanding these fundamentals through a bare-metal implementation, developers gain the knowledge to:
Build MCP servers in any language or environment
Debug protocol issues at the message level
Optimize performance by understanding the actual costs
Extend the protocol while maintaining compatibility
As AI continues to evolve, the ability to create tools that AI can understand and use becomes increasingly valuable. This implementation shows that such integration doesn’t require magic—just careful attention to protocol details and disciplined STDIO handling.
The Model Context Protocol’s choice of STDIO as its primary transport isn’t just a technical decision—it’s a philosophical one. It says that AI-tool integration should be simple, debuggable, and accessible to everyone. By building without frameworks, we see this philosophy in action: powerful capabilities emerging from simple, well-designed primitives.
Whether you’re building the next generation of AI tools or simply understanding how AI systems communicate, the lessons from this STDIO-based implementation provide a solid foundation for creating robust, interoperable systems that bridge the gap between human intentions and machine capabilities.
Learn By Building: The Agent MCP Workshop
If you found this deep dive valuable and want to build your own MCP server from the ground up, check out the Agent MCP Workshop on GitHub. This instructor-led workshop takes you through five progressive lessons:
Building the Transport Layer - Create a robust STDIO communication foundation
Understanding the Protocol - Implement JSON-RPC message handling
Protocol Handshake & Routing - Build initialization and message dispatch
Implementing Capabilities - Add Resources, Tools, and Prompts
Agent Integration - Connect your MCP server with AI agents
The workshop provides hands-on experience with the exact code examined in this article, along with exercises, debugging techniques, and best practices for production deployment. Whether you’re learning solo or in a group setting, the workshop materials offer a structured path to MCP mastery.
Start building your own AI-integrated tools today: github.com/David-Parry/agent-mcp-workshop
Originally published at Foojay.io on July 2025.
And now, let’s review some of the other cool things that appeared on Foojay.io last month…
Foojay Podcast #89: Quarkus and Agentic Commerce
The latest Foojay Podcast episode features Frank Delporte discussing Quarkus with Michal Maléř and Holly Cummins, and the conversation goes in some unexpected directions.
Sure, they cover the expected ground - how Quarkus compares to other frameworks, the build-time optimization vs AOT vs JIT debate, and the impact on cloud costs (both financial and ecological). But things get really interesting when they dive into “chain transactions” and how nano businesses could serve as a model for paying content creators through standards like x402 and ERC-8004.
The discussion on whether MCP could serve as a content distribution mechanism (with Quarkus, naturally) at the 46-minute mark is worth the listen alone, especially in early 2026 where world visibly got crazy. It’s one of those podcasts that starts technical and ends up philosophical - in the best possible way.
The Ultimate 10 Years Java Garbage Collection Guide (2016–2026)
Alexius Dionysius Diakogiannis in The Ultimate 10 Years Java Garbage Collection Guide (2016–2026) has put together what might be the definitive guide to choosing the right garbage collector for your workload.
And yes, the title is ambitious, but the content delivers.
For years, the standard advice was “G1 is always best.” With Generational ZGC arriving in JDK 21-25, that’s no longer true. Generational ZGC achieves roughly 10% better throughput than its single-generation predecessor and prevents the allocation stalls that plagued earlier versions. The guide provides specific recommendations for different workload types - microservices, legacy JEE, stateful UI applications, and data-intensive batch processing.
If you’re still running with default GC settings in production (don’t worry, no judgment here 😉), this is your weekend reading.
Java on Single Board Computers: x86 vs ARM vs RISC-V
Frank Delporte is not only a Java Champion and Foojay Podcast host, but also an author of Getting Started with Java on the Raspberry Pi is expanding beyond his Raspberry Pi comfort zone in 2026. Java on Single Board Computers: x86 vs ARM vs RISC-V, his comparison of x86, ARM, and RISC-V architectures for Java development is exactly what you’d expect from Frank - thorough, practical, and full of the kind of details only someone who actually uses these boards would know.
The RISC-V section is particularly interesting for those of us watching that ecosystem mature. The good news: Java officially supports RISC-V with the OpenJDK RISC-V Port. The less good news: early implementations like the Kylin X1 are still 2-7x slower than Raspberry Pi 5 in benchmarks. But hey, RISC-V is open source and licensing-fee-free, so expect rapid improvement in the tooling.
Especially as it’s important part of the recent trade conflict.
Command Completion: IntelliJ IDEA with Less Shortcuts
How many shortcuts can you remember? Three? Five? 👩🏻💻 Marit van Dijk , Java Champion and Developer Advocate at JetBrains , asks the question we’ve all been afraid to answer - even before ourselves.
The new command completion feature in IntelliJ IDEA (..) lets you discover and execute IDE actions right from your editor - no shortcut memorization required. Type two dots after any symbol, and you’ll see all relevant commands for your current context. Want to rename a class but forgot Shift+F6? Just type .. and filter for rename. Need to add JavaDoc? you know the drill - ..add javadoc. Some commands even have aliases, so ..change name works just as well.
It’s one of those features that sounds minor but actually changes how you interact with the IDE. The article walks through fixing errors, performing file-level actions, refactoring, and navigation - all without leaving the flow of coding.
Why is My Talk Selected? Reflections from a Program Committee Reviewer
And finally, something for those of you eyeing the CFP deadlines on your calendar. Soham Dasgupta, drawing on his experience as a reviewer for Voxxed Days Amsterdam and organizer of internal meetups, shares the criteria that guide talk selection. If you’ve ever wondered why some submissions get accepted while others don’t (and you’ve definitely wondered), this explains the decision-making process.
The article breaks down selection criteria into two categories: speaker/talk-related reasons (clarity, originality, relevance, speaker credibility) and organization/program-related reasons (diversity, schedule balance, avoiding duplication). The most valuable part: understanding that rejection isn’t personal - it’s often about schedule fit, topic balance, or simply too many good submissions.
The honest admission that “as reviewers, we actively welcome a percentage of new speakers” is refreshing. The advice to share your story - not just technology features - resonates with what I’ve seen work at conferences. And the practical tip about including a recording link with your submission? Worth the read for that alone.
BTW: Big thanks to David Parry for the guest feature this month. It was great to see you on Jfokus man!
BTW2: IntelliJ IDEA Conf 2026 is coming! March 26-27, free virtual event - speakers are already published, schedule drops the week of Feb 9th.
List is enormous, just to mention Josh Long Ana-Maria Mihalceanu Ivar Grimstad Thomas Wuerthinger, Viktor Gamov Mark Pollack... the list is just to long, and that’s not including the team from JetBrains as a hosts and speakers: 👩🏻💻 Marit van Dijk Arun Gupta 👓 Anton Arhipov Evgeny Borisov and Siva Katamreddy.
BTW3: KotlinConf 2026 is also on the horizon - more information soon! 👉 kotlinconf.com
BTW4: A bit of selfplug at the end:
⭐⭐⭐⭐ New chapters of Vibe Engineering: Best Practices, Mistakes and Tradeoffs just dropped on Manning Publications Co. MEAP ⭐⭐⭐⭐
What’s in the new content? The things nobody seems to talk about - methodological approach and performance engineering techniques. No “10 magical prompts”, but also no “I let 🦞 rewrite my entire codebase in the weekend” - just methodology you can actually use regardless of which model is trending this week.
The core idea: engineering discipline comes from process, not from the model. Every technique comes from a year of daily testing with our team at VirtusLab - we stress-test every new trend (and eat the failures) so you don’t have to.
👉 hubs.la/Q03XhKmG0 🎟️ Save 40% with code: WATCHLELEK240









