Skip to content

Author a full course with an LLM

This is the canonical end-to-end flow: from an empty tenant to a playable course. Each step uses the parent id returned by the previous one. An LLM connected to itera-mcp can run this from natural language — the tool sequence below is what it emits.

  1. Find your tenant + confirm you can author.

    whoami → read an id from author_tenant_ids. (Or list_tenants → pick the id for your slug.)

    whoami → result
    { "memberships": [ { "tenant_id": "8f3c…", "role": "author" } ], "author_tenant_ids": ["8f3c…"] }
  2. Create the program.

    create_program → args
    { "tenant_id": "8f3c…", "slug": "intro-kafka", "title_i18n": { "en": "Intro to Kafka" } }

    { "success": true, "program": { "id": "P1", … } }

  3. Add a unit (parent = the program id P1).

    create_unit → args
    { "program_id": "P1", "ordinal": 0, "title_i18n": { "en": "Fundamentals" } }

    { "success": true, "unit": { "id": "U1", … } }

  4. Add a lesson (parent = the unit id U1).

    create_lesson → args
    { "unit_id": "U1", "ordinal": 0, "title_i18n": { "en": "Topics & partitions" } }

    { "success": true, "lesson": { "id": "L1", … } }

  5. Add activities (parent = the lesson id L1). Mix kinds — a concept explainer, then a single_choice check, then a judged document task.

    create_activity → args (concept)
    { "lesson_id": "L1", "kind": "concept", "ordinal": 0,
    "spec": { "body": "A topic is split into partitions; each partition is an ordered log." } }
    create_activity → args (single_choice)
    { "lesson_id": "L1", "kind": "single_choice", "ordinal": 1,
    "spec": { "prompt": "What guarantees ordering in Kafka?",
    "options": [ { "id": "a", "label": "The topic" }, { "id": "b", "label": "The partition" } ],
    "answer": "b" } }
    create_activity → args (document, judged)
    { "lesson_id": "L1", "kind": "document", "ordinal": 2,
    "spec": { "prompt": "Explain when you'd increase a topic's partition count." },
    "rubric": { "criteria": [ { "criterion": "Mentions consumer parallelism", "weight": 1 } ], "pass_score": 70 } }
  6. Verify the graph with a single read:

    get_program with { "program_id": "P1" } → returns the program with units → lessons → activities nested and ordered. Confirm it matches what you built.

  • Change order with update_unit / update_lesson / update_activity (ordinal).
  • Fix a title with update_* (title_i18n).
  • Remember: update_activity replaces spec/rubric wholesale — read, edit, write back the full object.

Once a program is a good template, instantiate_program deep-copies the entire graph into a new program in one call:

instantiate_program → args
{ "source_program_id": "P1", "new_slug": "intro-kafka-2026-q3", "new_title_i18n": { "en": "Intro to Kafka — 2026 Q3" } }

{ "success": true, "new_program_id": "P2" }