Recipe-Inhalt ist auf Englisch. Englisches Original lesen →
← Alle Recipes
Phase 16 · Claude Code Hooks·6 steps

Academy hook bundle, auto-recipe lookup, session-start dashboard

Two hooks for the Academy MCP itself. UserPromptSubmit points the assistant at a matching recipe when you ask 'how do I X'. SessionStart shows XP, rank and next lesson without typing a command.

6 steps0%
Du liest ohne Account. Mit Login speichern wir Step-Fortschritt + Notes.

Academy hook bundle, auto-recipe lookup, session-start dashboard

The Academy MCP comes in two shapes, and which one you run decides which hooks can work at all.

As a library: npx -y mcp-academy over stdio. The whole curriculum is baked into the package: lessons, playbooks, recipes, three languages, no account, no network. You get the 10 reading tools plus the generic search and fetch. Nothing personal, because there is nobody to be personal about.

As a course: the hosted connector at https://mcp.studiomeyer.academy/mcp. Same reading tools, plus the account tools: academy_stats, academy_next_lesson, academy_progress_complete, academy_quiz, academy_quiz_submit, academy_review, academy_review_grade, academy_certificates, academy_tutor. Those need a login, because progress has to live somewhere.

Step 1 works either way. Step 2 and Step 4 need the account tools, so they need the connector.

The server field is your own config key

Every mcp_tool hook below has a "server" field. That is the name you gave the server in your own config, not the npm package name. If you added it with

claude mcp add academy -s user -- npx -y mcp-academy

then your key is academy and the examples below work as written. If you called it studiomeyer-academy, every "server": "academy" has to say "studiomeyer-academy" instead. Get this wrong and the hook fails silently. No error, just nothing happening. Check yours with claude mcp list.

The same rule applies to the matcher in Step 4: mcp__academy__academy_progress_complete is mcp__<your-key>__<tool-name>.

Schritt 1: UserPromptSubmit hook → auto-recipe lookup

When you ask "how do I publish to npm" or "recipe for OAuth setup" or "wie mache ich X", the Academy probably has a recipe for it. Nudge the assistant to go look:

{
  "UserPromptSubmit": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "~/.claude/hooks/academy-recipe-trigger.sh",
          "timeout": 3
        }
      ]
    }
  ]
}

Where academy-recipe-trigger.sh:

#!/usr/bin/env bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt // .user_prompt // ""')
# Trigger phrases (DE + EN) that indicate the user wants step-by-step guidance
if echo "$prompt" | grep -qiE 'how (do|to)|recipe for|wie mache ich|wie geht|step.by.step|schritt für schritt'; then
  echo '{"hookSpecificOutput":{"additionalContext":"User asked for how-to guidance. Call academy_search({query: \"<topic>\"}) to find matching material, then academy_recipes({phase: <n>}) to list recipes in that phase, then academy_recipe({slug}) for the full text of the best match."}}'
fi

Why inject context instead of calling a tool directly? Because the topic has to be pulled out of a free-text prompt, and bash regex is the wrong instrument for that. The injected sentence tells the assistant which tools to call and in what order; it does the extraction.

Note the tool names: academy_search, academy_recipes (list), academy_recipe (one, by slug). Recipes are English-only and take no locale: lessons and playbooks do.

Schritt 2: SessionStart hook → dashboard injection

Requires the hosted connector and a login. Every session then starts knowing where you stand:

{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "mcp_tool",
          "server": "academy",
          "tool": "academy_stats",
          "input": {},
          "timeout": 10,
          "statusMessage": "Academy: loading stats..."
        },
        {
          "type": "mcp_tool",
          "server": "academy",
          "tool": "academy_next_lesson",
          "input": { "locale": "en" },
          "timeout": 10,
          "statusMessage": "Academy: finding next lesson..."
        }
      ]
    }
  ]
}

Two parallel hooks, both pure reads. Result: every session you start, the assistant already knows your progress and what to suggest next, without you typing a command.

If you are running the stdio library instead, these two tools do not exist and both hooks fail silently. That is the single most common reason this recipe "does not work".

Schritt 3: Privacy and idempotency notes

  • Idempotent. academy_stats and academy_next_lesson are pure reads, as is academy_search.
  • Deterministic. Same state, same answer.
  • Side-effect-free without a user trigger. Nothing in Step 1 or 2 writes anything.
  • Where your data goes. With the library, nowhere: the curriculum is local and there is no account. With the connector, your progress lives in your own Academy account on studiomeyer.academy and is not shown to anyone else.

Schritt 4: Optional, quiz auto-trigger on lesson completion

When you mark a lesson complete, offer the quiz for it:

{
  "PostToolUse": [
    {
      "matcher": "mcp__academy__academy_progress_complete",
      "hooks": [
        {
          "type": "mcp_tool",
          "server": "academy",
          "tool": "academy_quiz",
          "input": {
            "level": "${tool_input.level}",
            "lessonSlug": "${tool_input.slug}"
          },
          "timeout": 10,
          "statusMessage": "Academy: loading quiz..."
        }
      ]
    }
  ]
}

Watch the parameter names. academy_progress_complete takes level and slug (both required, plus optional locale). academy_quiz takes lessonSlug, camelCase, and a different name for the same thing. Passing lesson_slug gets you an empty quiz and no error.

The cascade is: lesson complete → quiz fetched → assistant presents it inline → you answer → it calls academy_quiz_submit({slug, answers}). No "and now check the quiz" prompt needed.

Schritt 5: Verify the bundle works

# Edit settings.json with the SessionStart hooks above
claude

You should see both status messages flash on session start. The assistant's first response should reference your progress and a recommended lesson without you asking. Nothing happening at all usually means one of two things: wrong server key (Step 0), or you are on the stdio library where those tools do not exist.

# In the running session:
# "wie mache ich npm publish"

No status message here. UserPromptSubmit only injects context, it does not call a tool. But the answer should now point at a concrete Academy recipe.

Schritt 6: Why these hooks and not others

Hooks are for what the assistant should do on its own. Everything that needs your input stays explicit: submitting a quiz answer, grading a review, asking the tutor a question. Wiring those to lifecycle events produces noise, not help.

If you live in the Academy, two more are defensible:

  • Stopacademy_review to pull up what is due for review
  • SubagentStopacademy_tutor for follow-up suggestions (needs message and level)

Both are taste, not necessity. Start with Step 1 and Step 2.

Done

Five StudioMeyer MCPs (Memory, CRM, GEO, Crew, Academy) now have working hook bundles. Your session picks up context by itself instead of waiting for you to remember a tool name.

Next: distribute these as plugins

The bundles can be packaged as Claude Code plugins on studiomeyer-io/studiomeyer-marketplace: one install, all hooks at once. That is recipe 9.6.

Sources

GEO + Crew hook bundles, auto-Recipes-Index →