MCP protocol of choice: stdin/stdout? WTF, man?

Let’s talk about MCP. More specifically, let’s talk about using stdin/stdout as a protocol transport layer in the year of our Lord 2025.

Yes, yes—it’s universal. It’s composable. It works “everywhere.” It’s the spiritual successor to Unix pipes, which were cool at the time. The time when my Dad was hitting on my Mom. As an actual transport layer, stdin/stdout is a disaster.

Debugging Is Basically a Crime

Let’s say I want to create an MCP server in Python. Reasonable. Now let’s say I want to debug it. Set a breakpoint. Inspect variables. Use threads. Maybe spin up the LLM in the same process for context. You know, software engineering.

The moment you try to do this, you’re writing a debug driver. Congratulations. You are now:

  • Building a fake client to simulate a streaming LLM
  • Implementing bidirectional IO while praying the LLM doesn’t send surprise newline characters
  • Wrapping things in threads and/or asyncio or multiprocessing or whatnot other total fucking bullshit.

Been there. Twice:

  • Voitta’s Brokkoly: Thought I could run the LLM and the driver in one process. Spent 3 hours implementing queues, got it half-working, and realized I was debugging my own debug tool.
  • Samtyzukki: Round two. Same problem. Ended up with more abstraction layers than a Kafka conference.

Eventually, I just gave up and decided to use SSE (Server-Sent Events). Because you know what’s great about SSE? You can log things. You can see the messages. You can debug. It’s like rediscovering civilization after weeks of wilderness survival with only printf() and trauma.

 stdout Is Sacred, Until It Isn’t

Here’s the other problem. stdout is a shared space. You can’t count on it. Libraries will write to it. Dependencies will write to it. Your logger will write to it. Some genius upstream will write:

print(“INFO: falling back to CPU because the GPU is feeling shy today.”)

Congratulations. You just corrupted your transport. Your parser reads that as malformed JSON or a broken packet or an existential and spiritual crisis.

It’s not a bug. It’s a design decision—and not a good one.

This is the part where I invoke Rob Pike. Sorry. Not sorry.

In Go, to format a date, one doesn’t simply use YYYY-MM-DD. You do Mon Jan 2 15:04:05 MST 2006.

Because, I get it, we all need to get high once in a while. But srsly.

Credit where it’s due

Microsoft has a fix for an issue quite quickly (mentioned in a previous post).

Figuring out the reason for the magic number to backtrack from, though, I had posited another reason, and I was wrong… And overall it now reminded me of:

The appearance of our visitor was a surprise to me, since I had expected a typical country practitioner. He was a very tall, thin man, with a long nose like a beak, which jutted out between two keen, grey eyes, set closely together and sparkling brightly from behind a pair of gold-rimmed glasses. He was clad in a professional but rather slovenly fashion, for his frock-coat was dingy and his trousers frayed. Though young, his long back was already bowed, and he walked with a forward thrust of his head and a general air of peering benevolence. As he entered his eyes fell upon the stick in Holmes’s hand, and he ran towards it with an exclamation of joy. “I am so very glad,” said he. “I was not sure whether I had left it here or in the Shipping Office. I would not lose that stick for the world.”

“A presentation, I see,” said Holmes.

“Yes, sir.”

“From Charing Cross Hospital?”

“From one or two friends there on the occasion of my marriage.”

“Dear, dear, that’s bad!” said Holmes, shaking his head.

Dr. Mortimer blinked through his glasses in mild astonishment. “Why was it bad?”

“Only that you have disarranged our little deductions. Your marriage, you say?”

Which in turn reminded me of

И вот какого хрена “Shipping Office” переводится как “пароходство”?

Poor Man’s Tracepoints and a call sequence of a C program

My C is quite rusty, so to help me figure out the flow of a program, I thought I’d do with gdb what Tony Loton did with JPDA. Quickly giving myself a refresher on gdb, I thought tracepoints are the easiest way to go. Except that they are available only for
remote targets, and

  1. There’s no gdbserver on my host platform (Cygwin)
  2. The target platform does have it (good news!), but it doesn’t support tracepoints (and some say, that few if any stubs even support it

So I wrote a silly Perl script to read ctags information, create breakpoints on every function entry, print the arguments and resume. I haven’t bothered to figure out where to use pure MI vs. CLI commands, and in general I have no clue…

Now, what I think would be interesting is making this an add-on, using CDT (or, more generically, via Eclipse Debug Framework) and GEF… I envision something like a call graph (with exclusions of course, because it will become too big), which grows as you step through, displaying arguments. Could be a quick way to get a picture of how a program works before just reading the code and keeping stuff in your head…


In related news, upgrading to Eclipse 3.2.2 I lost the “Remote debugging” launch configuration. (Ironically, the reason for the attempted upgrade was to see whether a bug with hardcoded remote port has been fixed (it appeared that it’s always 4305, no matter what you put in; while the default one is 1234, which is the kind of thing an idiot would have on his luggage.). Which brings me to the “Zero information content” of various blogs/articles out there (which this blog is trying not to be). Thank you, Bill Graham, for pointing out that “the ‘vanilla’ CDT from http://www.eclipse.org […] doesn’t support remote target debugging”, thus leading me in my Google search to your article which doesn’t tell me how to fix it, nor does it tell me much of anything… There, I vented. (The answer, BTW, is to get Target Management, who knew…)