James opened the tutoring transcript from Module 9.3, Chapter 1 and traced the tool calls again. "register_learner, get_learner_state, update_progress. Those are working. But then it says get_chapter_content and get_exercises." He scrolled through his project directory. "Where does the content come from?"
"From disk," Emma said. "Markdown files for chapters, JSON for exercises. No cloud storage, no CDN, no database. Files you can open in a text editor."
"That feels too simple."
Emma pulled up her phone and showed him a screenshot of her first product. "My first tutoring prototype had a CDN with a caching layer, a content management system, and a versioning pipeline. I built all of that before I had a single paying customer." She put the phone away. "Start with files on disk. Add infrastructure when you have users who need it."
You are doing exactly what James is doing. You have working state tools from Module 9.3, Chapter 3. Now you create the content your tutor will teach from, and the tools that deliver it.
This lesson has three parts. First, you create sample content files: markdown for chapters and JSON for exercises. Second, you describe get_chapter_content to Claude Code and verify it works. Third, you describe get_exercises and verify that too. Both tools enforce tier gating: free learners can access chapters 1 through 5 only.
Your MCP server needs content to serve. The structure is straightforward:
Chapters are markdown files. Exercises are JSON files. The numbering prefix (01, 02, etc.) is how the tools find the right file for a given chapter number.
You have two options for creating this content:
Option A: Write it yourself. Create the directories and write sample content. The chapters do not need to be real course material. A chapter file might be 20 lines of markdown explaining a topic. An exercise file might have three exercises with a question, a hint, and a topic tag.
Option B: Ask Claude Code to generate it. Send this message:
Either option works. What matters is the structure, not the content quality. Your tools need files to read; the files need the right names and the right format.
After creating the content (yourself or with Claude Code), confirm the structure:
Output:
You should see five markdown files in chapters/ and two JSON files in exercises/. If any are missing, create them before continuing. The tools you build next depend on these files being in place.
Now describe the first content tool. Send this to Claude Code:
Notice what this message includes:
Claude Code responds with a spec before writing code. Check these elements:
The tool description is still the most important element, just like in Module 9.3, Chapter 3. Compare it to the descriptions on your state tools. The agent needs to know: "Call get_chapter_content when the user wants to read or study chapter material. Call get_exercises when the user wants practice problems."
If the descriptions overlap, steer before approving:
Once the spec looks right:
After the build finishes, test the tool. You need two calls: one that should succeed and one that should be blocked.
Test 1: Free-tier learner requests chapter 1 (should work)
Ask Claude Code to call the tool with chapter 1 and your mock learner ID. The tool should return the chapter content.
Test 2: Free-tier learner requests chapter 10 (should be blocked)
Ask Claude Code to call the tool with chapter 10. Since you only have chapters 1 through 5 on disk and the learner is free tier, the tool should return the upgrade message instead of content.
If the tool returns content for chapter 10, the tier gating is broken. Tell Claude Code:
The second content tool delivers exercises instead of chapter text. Send this to Claude Code:
mapping what we did here to get_chapter_content: the weak_areas filter. When the pedagogy tools (Module 9.3, Chapter 5) identify a learner's weak areas, this tool can return targeted exercises instead of the full set.
Review the spec. Pay special attention to:
If the description is vague, steer it:
Approve and build. Then run three tests:
Test 1: Fetch all exercises for chapter 1 (should return the full set)
Test 2: Fetch exercises filtered by topic (should return a subset)
Compare the two results. The filtered response should contain fewer exercises than the unfiltered one. If both return the same number, the filter is not working.
Test 3: Fetch exercises for chapter 10 as a free-tier learner (should be blocked)
The upgrade message should match what get_chapter_content returns. Consistent error messages matter: a learner should not see two different upgrade explanations from two different tools.
You now have five tools in your MCP server:
The state tools (Module 9.3, Chapter 3) manage who the learner is and where they are. The content tools (this chapter) deliver what they study. In Module 9.3, Chapter 5, you build the pedagogy tools: the intelligence that decides how to teach.
Review the upgrade message your tools return when a free-tier learner requests gated content:
What you are learning: Error messages are product decisions. A good upgrade message converts free users to paid users. A bad one frustrates them.
Your server now has five tools. Ask Claude Code to evaluate whether the descriptions are distinct enough:
What you are learning: Every tool you add increases the chance of description overlap. An agent with five tools needs five clearly distinct job postings. Ten tools need ten.
Imagine you want a sixth tool that searches across all chapters for a keyword. Describe it to Claude Code, but ask for spec only:
Compare the description to get_chapter_content. If an agent could confuse the two, you have an overlap problem. How would you fix it?
What you are learning: Tool descriptions must be distinct not just from each other, but from tools that do related things. "Fetch chapter 3" and "search for 'variables'" are different operations, and the descriptions must make that obvious.
James called get_chapter_content with chapter 1 and watched the markdown flow back. Variables, code examples, output blocks. Then he called it with chapter 10.
"Access denied. Upgrade to paid for chapters 6 and above." He grinned. "Tier gating with an if statement."
"That is the point," Emma said. "Gating does not need a permissions service or a role-based access layer. It needs a rule and a check." She paused. "I spent two weeks building a permissions microservice for my first product. Role hierarchies, permission inheritance, audit trails. The entire customer base was four beta testers." She shrugged. "Build what the product needs today. The permissions microservice can wait until you have permissions to manage."
James looked at his five tools in the server output. "State tools know who the learner is. Content tools deliver what they study. What is missing?"
"The teaching. Right now your tutor is a filing cabinet: it stores records and retrieves documents. Module 9.3, Chapter 5 adds the pedagogy tools. That is where it starts actually tutoring."