The article is ready. Here is the rewritten piece:

A developer at Amazon built an MCP server for fetching YouTube transcripts, using Claude Opus 5 as a mentor that planned the project, reviewed every step, and answered questions, but never wrote a single line of code. The exercise, documented in a September 20 blog post, is a deliberate counterpoint to vibe coding: the developer typed everything by hand to learn the protocol from the inside out.

Learning by Building, Not by Prompting

Jaysinh Shukla, a software engineer at Amazon working on distributed systems in transportation and finance, wanted to learn the Model Context Protocol (MCP). Rather than passively reading docs or letting an LLM generate the codebase, he set a strict rule: the LLM could plan, review, quiz, and track progress, but it would never produce executable code.

The reasoning is straightforward. Programming is a motor skill as much as an intellectual one. The Recurse Center put it plainly in its recent position on AI in education: you cannot learn a hard skill by passively consuming output from a language model, just as you cannot learn by listening to a lecturer. Harvard research backs this up. Speed is the enemy of retention when the goal is deep understanding.

Shukla had not written production Python in seven years. Tools like uv and FastMCP did not exist the last time he touched the language. That gap made the constraint more valuable, not less. Every library lookup, every debugging session, every API decision stuck because he made it himself.

The Nine-Step Plan

Shukla prompted Claude Opus 5 with a detailed brief: he wanted a local MCP server that downloads YouTube video transcripts, connects to Claude Desktop, and works with any MCP-capable client. He explicitly told the model it would act as instructor and tutor, not as a coder.

The result was a nine-step execution plan with time estimates, reference materials, and verification criteria for each milestone. The LLM also asked follow-up questions to nail down scope: Python over Java, Streamable HTTP over stdio, and metadata beyond raw transcript text.

The steps broke down roughly like this:

  • Project scaffolding with uv and dependency pinning (~1-2 hours)
  • MCP fundamentals: reading docs on tools, resources, prompts, and the client-server-host model (~1.5-2 hours)
  • A minimal "hello world" MCP server with a trivial tool, tested via MCP Inspector (~2-3 hours)
  • A URL-to-video-ID parser with unit tests (~1.5-2 hours)
  • Transcript fetching using youtube-transcript-api, with error handling for disabled captions and unavailable videos (~2-3 hours)
  • Metadata fetching via YouTube's oEmbed endpoint, which requires no API key (~1.5-2 hours)
  • Composing the real MCP tool that ties parsing, fetching, and metadata together (~1.5-2 hours)
  • Docker containerization with a restart policy (~2-3 hours)
  • End-to-end connection to Claude Desktop, proving the server is LLM-agnostic (~1-2 hours)

The LLM estimated about 18 hours total. Shukla spent over 20 hours across roughly ten sessions, mostly on weekends and holidays. The overrun came from extending scope: he added design patterns, an abstract base class for transcript sources, a chain-of-extractors pattern for URL parsing, and a table-driven exception translation layer. None of that was in the original plan.

What the LLM Actually Did

The language model's role was structured and limited. It generated the step-by-step plan with time estimates. It reviewed code at each milestone, pointing out missing pieces (a forgotten .gitignore, an empty package directory, a leftover default file). It answered conceptual questions about MCP architecture and Python packaging. It tracked what was done and what remained.

When Shukla went beyond scope, the LLM acknowledged the additions and noted their value. When Shukla asked it to verify completed steps, the model returned checklists with status indicators and explanations. The feedback was specific: it flagged that a build-system configuration was needed for the src/ layout to work, explained why editable installs matter for test discovery, and warned about Python 3.14 compatibility risks in Docker builds.

The key constraint held throughout: no generated code entered the codebase. Shukla typed it all.

Architecture Choices That Matter

The server runs over Streamable HTTP rather than stdio. That decision is the foundation of the whole design. Because the server exposes a standard HTTP endpoint at localhost:8000/mcp, any MCP-capable client can connect to it. Claude Desktop, OpenAI's Agents SDK, or any future MCP host can use the same server without modification. Nothing in the server knows or cares which LLM is calling it.

The codebase uses a clean separation of concerns. The server module is thin, just an orchestrator. The actual logic lives in a youtube.py module with pure functions: extract_video_id parses URLs using urllib.parse, fetch_transcript pulls captions via youtube-transcript-api, and fetch_metadata hits YouTube's oEmbed endpoint with httpx.

Shukla added abstractions beyond the plan's scope. A TranscriptProviderManager picks a provider. A TranscriptProvider abstract base class means swapping in a different transcript source (not just YouTube) requires adding a class, not editing existing code. URL parsing goes through a coordinator with separate extractors for standard and short URLs. External library types never cross the boundary; mappers convert them into frozen dataclasses. Every library exception gets translated into a server-owned error type, classified as retriable or not.

One deliberate decision stands out: DataError is excluded from the exception translation layer. That ensures the developer's own bugs surface as their own errors instead of being misclassified as YouTube failures.

Practical Takeaways for Developers

The exercise demonstrates a viable middle ground between full vibe coding and pure manual work. An LLM can function as a structured mentor that enforces scope, provides accountability, and catches gaps, while the developer retains full ownership of the implementation. The approach works particularly well when learning unfamiliar technology, because the LLM can compress research time (finding the right docs, identifying the right tools, suggesting an architecture) without removing the learning that comes from writing the code.

The youtube-transcript-api library changed its API across versions. Older tutorials call YouTubeTranscriptApi.get_transcript(), while newer versions use an instance method .fetch(). Shukla discovered this by reading the installed source directly, a habit that only develops when you are the one writing the import statements and tracing the failures.

The finished server is available on GitHub. The code is containerized, runs as a daemon, and connects to Claude Desktop as a custom connector. The plan estimated 18 hours; reality took 20-plus. The difference is not overhead. It is depth.