@epic-web/workshop-mcp 5.22.3 → 5.22.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,iBAAiB;;;CAQ7B,CAAA;AAED,wBAAsB,MAAM,CAAC,EAC5B,iBAAiB,EACjB,cAAc,EAAE,sBAAsB,GACtC,EAAE;IACF,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,OAAO,CAAC,eAAe,CAAC,CAqC3B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAO5C"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,eAAO,MAAM,iBAAiB;;;CAQ7B,CAAA;AAED,wBAAsB,MAAM,CAAC,EAC5B,iBAAiB,EACjB,cAAc,EAAE,sBAAsB,GACtC,EAAE;IACF,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,OAAO,CAAC,eAAe,CAAC,CAqC3B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAO5C"}
@@ -2,13 +2,13 @@ import { getExercises } from '@epic-web/workshop-utils/apps.server';
2
2
  import { getWorkshopConfig } from '@epic-web/workshop-utils/config.server';
3
3
  import { z } from 'zod';
4
4
  import { exerciseContextResource } from './resources.js';
5
- import { handleWorkshopDirectory } from './utils.js';
5
+ import { handleWorkshopDirectory, workshopDirectoryInputSchema, } from './utils.js';
6
6
  export const quizMeInputSchema = {
7
- workshopDirectory: z.string().describe('The workshop directory'),
7
+ workshopDirectory: workshopDirectoryInputSchema,
8
8
  exerciseNumber: z
9
9
  .string()
10
10
  .optional()
11
- .describe('The exercise number to get quizzed on (leave blank for a random exercise).'),
11
+ .describe(`The exercise number to get quizzed on (e.g., \`4\`). Leave blank for a random exercise.`),
12
12
  };
13
13
  export async function quizMe({ workshopDirectory, exerciseNumber: providedExerciseNumber, }) {
14
14
  const workshopRoot = await handleWorkshopDirectory(workshopDirectory);
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAG1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;IAChE,cAAc,EAAE,CAAC;SACf,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACR,4EAA4E,CAC5E;CACF,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAC5B,iBAAiB,EACjB,cAAc,EAAE,sBAAsB,GAItC;IACA,MAAM,YAAY,GAAG,MAAM,uBAAuB,CAAC,iBAAiB,CAAC,CAAA;IACrE,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,IAAI,cAAc,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAA;IACnD,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAA;QACtC,MAAM,cAAc,GACnB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;QACxD,cAAc,GAAG,cAAc,EAAE,cAAc,IAAI,CAAC,CAAA;IACrD,CAAC;IACD,OAAO;QACN,QAAQ,EAAE;YACT;gBACC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;;;kCAGuB,cAAc,4BAA4B,MAAM,CAAC,KAAK,iBAAiB,MAAM,CAAC,QAAQ,eAAe,YAAY;;;QAG3I,CAAC,IAAI,EAAE;iBACV;aACD;YACD;gBACC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,MAAM,uBAAuB,CAAC,WAAW,CAAC;wBACnD,iBAAiB;wBACjB,cAAc;qBACd,CAAC;iBACF;aACD;SACD;KACD,CAAA;AACF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC5C,MAAM,CAAC,MAAM,CACZ,SAAS,EACT,6DAA6D,EAC7D,iBAAiB,EACjB,MAAM,CACN,CAAA;AACF,CAAC","sourcesContent":["import { getExercises } from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type GetPromptResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\nimport { exerciseContextResource } from './resources.js'\nimport { handleWorkshopDirectory } from './utils.js'\n\nexport const quizMeInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n\texerciseNumber: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\n\t\t\t'The exercise number to get quizzed on (leave blank for a random exercise).',\n\t\t),\n}\n\nexport async function quizMe({\n\tworkshopDirectory,\n\texerciseNumber: providedExerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: string\n}): Promise<GetPromptResult> {\n\tconst workshopRoot = await handleWorkshopDirectory(workshopDirectory)\n\tconst config = getWorkshopConfig()\n\tlet exerciseNumber = Number(providedExerciseNumber)\n\tif (!providedExerciseNumber) {\n\t\tconst exercises = await getExercises()\n\t\tconst randomExercise =\n\t\t\texercises[Math.floor(Math.random() * exercises.length)]\n\t\texerciseNumber = randomExercise?.exerciseNumber ?? 0\n\t}\n\treturn {\n\t\tmessages: [\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tcontent: {\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: `\nYou are an expert teacher.\n\nBelow is context about exercise ${exerciseNumber} in the workshop titled \"${config.title}\" (subtitled \"${config.subtitle}\") found at ${workshopRoot}.\n\nPlease use this context to provide quiz questions, one at a time, to me to help me solidify my understanding of this material. Ask me the question, I will provide a response, you will either congratulate my correct understanding and move on or guide me to the correct answer with follow-up questions and hints until I either ask you to tell me the answer or ask you to continue to the next question.\n\t\t\t\t\t\t\t`.trim(),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tcontent: {\n\t\t\t\t\ttype: 'resource',\n\t\t\t\t\tresource: await exerciseContextResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t}\n}\n\nexport function initPrompts(server: McpServer) {\n\tserver.prompt(\n\t\t'quiz_me',\n\t\t'Have the LLM quiz you on topics from the workshop exercises',\n\t\tquizMeInputSchema,\n\t\tquizMe,\n\t)\n}\n"]}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAG1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EACN,uBAAuB,EACvB,4BAA4B,GAC5B,MAAM,YAAY,CAAA;AAEnB,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,iBAAiB,EAAE,4BAA4B;IAC/C,cAAc,EAAE,CAAC;SACf,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACR,yFAAyF,CACzF;CACF,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAC5B,iBAAiB,EACjB,cAAc,EAAE,sBAAsB,GAItC;IACA,MAAM,YAAY,GAAG,MAAM,uBAAuB,CAAC,iBAAiB,CAAC,CAAA;IACrE,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,IAAI,cAAc,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAA;IACnD,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAA;QACtC,MAAM,cAAc,GACnB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;QACxD,cAAc,GAAG,cAAc,EAAE,cAAc,IAAI,CAAC,CAAA;IACrD,CAAC;IACD,OAAO;QACN,QAAQ,EAAE;YACT;gBACC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;;;kCAGuB,cAAc,4BAA4B,MAAM,CAAC,KAAK,iBAAiB,MAAM,CAAC,QAAQ,eAAe,YAAY;;;QAG3I,CAAC,IAAI,EAAE;iBACV;aACD;YACD;gBACC,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,MAAM,uBAAuB,CAAC,WAAW,CAAC;wBACnD,iBAAiB;wBACjB,cAAc;qBACd,CAAC;iBACF;aACD;SACD;KACD,CAAA;AACF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC5C,MAAM,CAAC,MAAM,CACZ,SAAS,EACT,6DAA6D,EAC7D,iBAAiB,EACjB,MAAM,CACN,CAAA;AACF,CAAC","sourcesContent":["import { getExercises } from '@epic-web/workshop-utils/apps.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type GetPromptResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\nimport { exerciseContextResource } from './resources.js'\nimport {\n\thandleWorkshopDirectory,\n\tworkshopDirectoryInputSchema,\n} from './utils.js'\n\nexport const quizMeInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n\texerciseNumber: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\n\t\t\t`The exercise number to get quizzed on (e.g., \\`4\\`). Leave blank for a random exercise.`,\n\t\t),\n}\n\nexport async function quizMe({\n\tworkshopDirectory,\n\texerciseNumber: providedExerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: string\n}): Promise<GetPromptResult> {\n\tconst workshopRoot = await handleWorkshopDirectory(workshopDirectory)\n\tconst config = getWorkshopConfig()\n\tlet exerciseNumber = Number(providedExerciseNumber)\n\tif (!providedExerciseNumber) {\n\t\tconst exercises = await getExercises()\n\t\tconst randomExercise =\n\t\t\texercises[Math.floor(Math.random() * exercises.length)]\n\t\texerciseNumber = randomExercise?.exerciseNumber ?? 0\n\t}\n\treturn {\n\t\tmessages: [\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tcontent: {\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: `\nYou are an expert teacher.\n\nBelow is context about exercise ${exerciseNumber} in the workshop titled \"${config.title}\" (subtitled \"${config.subtitle}\") found at ${workshopRoot}.\n\nPlease use this context to provide quiz questions, one at a time, to me to help me solidify my understanding of this material. Ask me the question, I will provide a response, you will either congratulate my correct understanding and move on or guide me to the correct answer with follow-up questions and hints until I either ask you to tell me the answer or ask you to continue to the next question.\n\t\t\t\t\t\t\t`.trim(),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: 'user',\n\t\t\t\tcontent: {\n\t\t\t\t\ttype: 'resource',\n\t\t\t\t\tresource: await exerciseContextResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t}\n}\n\nexport function initPrompts(server: McpServer) {\n\tserver.prompt(\n\t\t'quiz_me',\n\t\t'Have the LLM quiz you on topics from the workshop exercises',\n\t\tquizMeInputSchema,\n\t\tquizMe,\n\t)\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAoBA,OAAO,EACN,KAAK,SAAS,EACd,gBAAgB,EAChB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAC5E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,YAAY,CAAA;AAEnB,eAAO,MAAM,6BAA6B;;CAMzC,CAAA;AAED,wBAAsB,kBAAkB,CAAC,EACxC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,6BAA6B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAqBrC,KAAK,CAAC,GAAG,CAAC;GA0D5B;AAOD,wBAAsB,0BAA0B,CAAC,EAChD,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,6BAA6B,CAAC,GAAG,OAAO,CACjE,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAQA;AAED,eAAO,MAAM,uBAAuB;;;;;;;;CAMnC,CAAA;AA+LD,iBAAe,0BAA0B,CAAC,EACzC,iBAAiB,EACjB,cAAc,GACd,EAAE;IACF,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAalD;AAED,eAAO,MAAM,uBAAuB;;;;;;;;;CAMnC,CAAA;AAED,QAAA,MAAM,0BAA0B;;;;CAI/B,CAAA;AA4CD,iBAAe,0BAA0B,CAAC,EACzC,iBAAiB,EACjB,IAAI,EACJ,IAAI,GACJ,EAAE,eAAe,CAAC,OAAO,0BAA0B,CAAC,GAAG,OAAO,CAC9D,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAYA;AAOD,eAAO,MAAM,uBAAuB;;;;;;;;;;CAMnC,CAAA;AAED,QAAA,MAAM,sCAAsC;;CAE3C,CAAA;AAoCD,iBAAe,mCAAmC,CAAC,EAClD,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,sCAAsC,CAAC,GAAG,OAAO,CAC1E,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAUA;AAED,eAAO,MAAM,gCAAgC;;;;;;;;CAM5C,CAAA;AAED,QAAA,MAAM,sBAAsB;;CAE3B,CAAA;AAOD,iBAAe,mBAAmB,CAAC,EAClC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,sBAAsB,CAAC;;;;GAShD;AAED,eAAO,MAAM,gBAAgB;;;;;;;;CAM5B,CAAA;AAED,QAAA,MAAM,wBAAwB;;CAE7B,CAAA;AAOD,iBAAe,qBAAqB,CAAC,EACpC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,wBAAwB,CAAC;;;;GASlD;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAM9B,CAAA;AAED,QAAA,MAAM,uBAAuB;;CAE5B,CAAA;AAOD,iBAAe,uBAAuB,CAAC,EACtC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,uBAAuB,CAAC;;;;GASjD;AAED,eAAO,MAAM,oBAAoB;;;;;;;;CAMhC,CAAA;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,QA0I9C"}
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAoBA,OAAO,EACN,KAAK,SAAS,EACd,gBAAgB,EAChB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAC5E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAEN,KAAK,eAAe,EAGpB,MAAM,YAAY,CAAA;AAEnB,eAAO,MAAM,6BAA6B;;CAEzC,CAAA;AAED,wBAAsB,kBAAkB,CAAC,EACxC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,6BAA6B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAqBrC,KAAK,CAAC,GAAG,CAAC;GA0D5B;AAOD,wBAAsB,0BAA0B,CAAC,EAChD,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,6BAA6B,CAAC,GAAG,OAAO,CACjE,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAQA;AAED,eAAO,MAAM,uBAAuB;;;;;;;;CAMnC,CAAA;AA2LD,iBAAe,0BAA0B,CAAC,EACzC,iBAAiB,EACjB,cAAc,GACd,EAAE;IACF,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAalD;AAED,eAAO,MAAM,uBAAuB;;;;;;;;;CAMnC,CAAA;AAED,QAAA,MAAM,0BAA0B;;;;CAI/B,CAAA;AA4CD,iBAAe,0BAA0B,CAAC,EACzC,iBAAiB,EACjB,IAAI,EACJ,IAAI,GACJ,EAAE,eAAe,CAAC,OAAO,0BAA0B,CAAC,GAAG,OAAO,CAC9D,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAYA;AAOD,eAAO,MAAM,uBAAuB;;;;;;;;;;CAMnC,CAAA;AAED,QAAA,MAAM,sCAAsC;;CAE3C,CAAA;AAoCD,iBAAe,mCAAmC,CAAC,EAClD,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,sCAAsC,CAAC,GAAG,OAAO,CAC1E,kBAAkB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CACtC,CAUA;AAED,eAAO,MAAM,gCAAgC;;;;;;;;CAM5C,CAAA;AAED,QAAA,MAAM,sBAAsB;;CAE3B,CAAA;AAOD,iBAAe,mBAAmB,CAAC,EAClC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,sBAAsB,CAAC;;;;GAShD;AAED,eAAO,MAAM,gBAAgB;;;;;;;;CAM5B,CAAA;AAED,QAAA,MAAM,wBAAwB;;CAE7B,CAAA;AAOD,iBAAe,qBAAqB,CAAC,EACpC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,wBAAwB,CAAC;;;;GASlD;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAM9B,CAAA;AAED,QAAA,MAAM,uBAAuB;;CAE5B,CAAA;AAOD,iBAAe,uBAAuB,CAAC,EACtC,iBAAiB,GACjB,EAAE,eAAe,CAAC,OAAO,uBAAuB,CAAC;;;;GASjD;AAED,eAAO,MAAM,oBAAoB;;;;;;;;CAMhC,CAAA;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,QAgJ9C"}
@@ -5,11 +5,9 @@ import { getAuthInfo } from '@epic-web/workshop-utils/db.server';
5
5
  import { getEpicVideoInfos, userHasAccessToWorkshop, getUserInfo, getProgress, } from '@epic-web/workshop-utils/epic-api.server';
6
6
  import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
7
7
  import { z } from 'zod';
8
- import { handleWorkshopDirectory, safeReadFile, } from './utils.js';
8
+ import { handleWorkshopDirectory, safeReadFile, workshopDirectoryInputSchema, } from './utils.js';
9
9
  export const getWorkshopContextInputSchema = {
10
- workshopDirectory: z
11
- .string()
12
- .describe('The workshop directory (the root directory of the workshop repo.).'),
10
+ workshopDirectory: workshopDirectoryInputSchema,
13
11
  };
14
12
  export async function getWorkshopContext({ workshopDirectory, }) {
15
13
  const workshopRoot = await handleWorkshopDirectory(workshopDirectory);
@@ -93,9 +91,7 @@ export const workshopContextResource = {
93
91
  inputSchema: getWorkshopContextInputSchema,
94
92
  };
95
93
  const getExerciseContextInputSchema = {
96
- workshopDirectory: z
97
- .string()
98
- .describe('The workshop directory (the root directory of the workshop repo.).'),
94
+ workshopDirectory: workshopDirectoryInputSchema,
99
95
  exerciseNumber: z.coerce
100
96
  .number()
101
97
  .optional()
@@ -265,7 +261,7 @@ export const exerciseContextResource = {
265
261
  inputSchema: getExerciseContextInputSchema,
266
262
  };
267
263
  const diffBetweenAppsInputSchema = {
268
- workshopDirectory: z.string().describe('The workshop directory'),
264
+ workshopDirectory: workshopDirectoryInputSchema,
269
265
  app1: z.string().describe('The ID of the first app'),
270
266
  app2: z.string().describe('The ID of the second app'),
271
267
  };
@@ -312,7 +308,7 @@ export const diffBetweenAppsResource = {
312
308
  inputSchema: diffBetweenAppsInputSchema,
313
309
  };
314
310
  const getExerciseStepProgressDiffInputSchema = {
315
- workshopDirectory: z.string().describe('The workshop directory'),
311
+ workshopDirectory: workshopDirectoryInputSchema,
316
312
  };
317
313
  async function getExerciseStepProgressDiff({ workshopDirectory, }) {
318
314
  await handleWorkshopDirectory(workshopDirectory);
@@ -349,7 +345,7 @@ export const exerciseStepProgressDiffResource = {
349
345
  inputSchema: getExerciseStepProgressDiffInputSchema,
350
346
  };
351
347
  const getUserInfoInputSchema = {
352
- workshopDirectory: z.string().describe('The workshop directory'),
348
+ workshopDirectory: workshopDirectoryInputSchema,
353
349
  };
354
350
  const userInfoUri = new ResourceTemplate('epicshop://{workshopDirectory}/user/info', { list: undefined });
355
351
  async function getUserInfoResource({ workshopDirectory, }) {
@@ -370,7 +366,7 @@ export const userInfoResource = {
370
366
  inputSchema: getUserInfoInputSchema,
371
367
  };
372
368
  const getUserAccessInputSchema = {
373
- workshopDirectory: z.string().describe('The workshop directory'),
369
+ workshopDirectory: workshopDirectoryInputSchema,
374
370
  };
375
371
  const userAccessUriTemplate = new ResourceTemplate('epicshop://{workshopDirectory}/user/access', { list: undefined });
376
372
  async function getUserAccessResource({ workshopDirectory, }) {
@@ -391,7 +387,7 @@ export const userAccessResource = {
391
387
  inputSchema: getUserAccessInputSchema,
392
388
  };
393
389
  const userProgressInputSchema = {
394
- workshopDirectory: z.string().describe('The workshop directory'),
390
+ workshopDirectory: workshopDirectoryInputSchema,
395
391
  };
396
392
  const userProgressUriTemplate = new ResourceTemplate('epicshop://{workshopDirectory}/user/progress', { list: undefined });
397
393
  async function getUserProgressResource({ workshopDirectory, }) {
@@ -414,6 +410,7 @@ export const userProgressResource = {
414
410
  export function initResources(server) {
415
411
  server.resource(workshopContextResource.name, workshopContextResource.uriTemplate, { description: workshopContextResource.description }, async (_uri, { workshopDirectory }) => {
416
412
  invariant(typeof workshopDirectory === 'string', 'A single workshopDirectory is required');
413
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
417
414
  const resource = await workshopContextResource.getResource({
418
415
  workshopDirectory,
419
416
  });
@@ -425,6 +422,7 @@ export function initResources(server) {
425
422
  const exerciseNumber = Number(providedExerciseNumber);
426
423
  invariant(!isNaN(exerciseNumber), 'exerciseNumber must be a number');
427
424
  invariant(exerciseNumber >= 0, 'exerciseNumber must be greater than or equal to 0');
425
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
428
426
  return {
429
427
  contents: [
430
428
  await exerciseContextResource.getResource({
@@ -438,6 +436,7 @@ export function initResources(server) {
438
436
  invariant(typeof workshopDirectory === 'string', 'A single workshopDirectory is required');
439
437
  invariant(typeof app1 === 'string', 'A single app1 is required');
440
438
  invariant(typeof app2 === 'string', 'A single app2 is required');
439
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
441
440
  return {
442
441
  contents: [
443
442
  await diffBetweenAppsResource.getResource({
@@ -450,6 +449,7 @@ export function initResources(server) {
450
449
  });
451
450
  server.resource(exerciseStepProgressDiffResource.name, exerciseStepProgressDiffResource.uriTemplate, { description: exerciseStepProgressDiffResource.description }, async (_uri, { workshopDirectory }) => {
452
451
  invariant(typeof workshopDirectory === 'string', 'A single workshopDirectory is required');
452
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
453
453
  return {
454
454
  contents: [
455
455
  await exerciseStepProgressDiffResource.getResource({
@@ -466,12 +466,14 @@ export function initResources(server) {
466
466
  });
467
467
  server.resource(userAccessResource.name, userAccessResource.uriTemplate, { description: userAccessResource.description }, async (_uri, { workshopDirectory }) => {
468
468
  invariant(typeof workshopDirectory === 'string', 'A single workshopDirectory is required');
469
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
469
470
  return {
470
471
  contents: [await userAccessResource.getResource({ workshopDirectory })],
471
472
  };
472
473
  });
473
474
  server.resource(userProgressResource.name, userProgressResource.uriTemplate, { description: userProgressResource.description }, async (_uri, { workshopDirectory }) => {
474
475
  invariant(typeof workshopDirectory === 'string', 'A single workshopDirectory is required');
476
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
475
477
  return {
476
478
  contents: [
477
479
  await userProgressResource.getResource({ workshopDirectory }),
@@ -1 +1 @@
1
- {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path'\nimport { invariant } from '@epic-web/invariant'\nimport {\n\textractNumbersAndTypeFromAppNameOrPath,\n\tfindSolutionDir,\n\tgetApps,\n\tgetExercise,\n\tgetExercises,\n\tgetFullPathFromAppName,\n\tgetPlaygroundApp,\n\tisExerciseStepApp,\n\tisPlaygroundApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getAuthInfo } from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetEpicVideoInfos,\n\tuserHasAccessToWorkshop,\n\tgetUserInfo,\n\tgetProgress,\n} from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\ttype McpServer,\n\tResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\nimport {\n\thandleWorkshopDirectory,\n\ttype InputSchemaType,\n\tsafeReadFile,\n} from './utils.js'\n\nexport const getWorkshopContextInputSchema = {\n\tworkshopDirectory: z\n\t\t.string()\n\t\t.describe(\n\t\t\t'The workshop directory (the root directory of the workshop repo.).',\n\t\t),\n}\n\nexport async function getWorkshopContext({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getWorkshopContextInputSchema>) {\n\tconst workshopRoot = await handleWorkshopDirectory(workshopDirectory)\n\tconst inWorkshop = (...d: Array<string>) => path.join(workshopRoot, ...d)\n\tconst readInWorkshop = (...d: Array<string>) => safeReadFile(inWorkshop(...d))\n\tconst progress = await getProgress()\n\n\tconst output = {\n\t\tmeta: {\n\t\t\t'README.md': await readInWorkshop('README.md'),\n\t\t\tconfig: (\n\t\t\t\tJSON.parse((await readInWorkshop('package.json')) || '{}') as any\n\t\t\t).epicshop,\n\t\t\tinstructions: {\n\t\t\t\tcontent: await readInWorkshop('exercise', 'README.mdx'),\n\t\t\t\tprogress: progress.find((p) => p.type === 'instructions'),\n\t\t\t},\n\t\t\tfinishedInstructions: {\n\t\t\t\tcontent: await readInWorkshop('exercise', 'FINISHED.mdx'),\n\t\t\t\tprogress: progress.find((p) => p.type === 'finished'),\n\t\t\t},\n\t\t},\n\t\texercises: [] as Array<any>,\n\t}\n\n\tconst exercises = await getExercises()\n\tfor (const exercise of exercises) {\n\t\tconst exerciseInfo = {\n\t\t\tfullPath: exercise.fullPath,\n\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\ttitle: exercise.title,\n\t\t\tinstructions: {\n\t\t\t\tcontent: await safeReadFile(path.join(exercise.fullPath, 'README.mdx')),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'instructions' &&\n\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\tfinishedInstructions: {\n\t\t\t\tcontent: await safeReadFile(\n\t\t\t\t\tpath.join(exercise.fullPath, 'FINISHED.mdx'),\n\t\t\t\t),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'finished' &&\n\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\tsteps: exercise.steps.map((step) => {\n\t\t\t\treturn {\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t\tprogress: progress.find(\n\t\t\t\t\t\t(p) =>\n\t\t\t\t\t\t\tp.type === 'step' &&\n\t\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber &&\n\t\t\t\t\t\t\tp.stepNumber === step.stepNumber,\n\t\t\t\t\t),\n\t\t\t\t\ttitle: step.problem?.title ?? step.solution?.title ?? null,\n\t\t\t\t\tproblem: step.problem\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfullPath: step.problem.fullPath,\n\t\t\t\t\t\t\t\ttestConfig: step.problem.test,\n\t\t\t\t\t\t\t\tdevConfig: step.problem.dev,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: 'No problem app',\n\t\t\t\t\tsolution: step.solution\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfullPath: step.solution.fullPath,\n\t\t\t\t\t\t\t\ttestConfig: step.solution.test,\n\t\t\t\t\t\t\t\tdevConfig: step.solution.dev,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: 'No solution app',\n\t\t\t\t}\n\t\t\t}),\n\t\t}\n\t\toutput.exercises.push(exerciseInfo)\n\t}\n\n\treturn output\n}\n\nconst workshopContextUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/workshop-context',\n\t{ list: undefined },\n)\n\nexport async function getWorkshopContextResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getWorkshopContextInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: workshopContextUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(await getWorkshopContext({ workshopDirectory })),\n\t}\n}\n\nexport const workshopContextResource = {\n\tname: 'workshop_context',\n\tdescription: 'The context of the workshop',\n\turiTemplate: workshopContextUriTemplate,\n\tgetResource: getWorkshopContextResource,\n\tinputSchema: getWorkshopContextInputSchema,\n}\n\nconst getExerciseContextInputSchema = {\n\tworkshopDirectory: z\n\t\t.string()\n\t\t.describe(\n\t\t\t'The workshop directory (the root directory of the workshop repo.).',\n\t\t),\n\texerciseNumber: z.coerce\n\t\t.number()\n\t\t.optional()\n\t\t.describe(\n\t\t\t`The exercise number to get the context for (defaults to the exercise number the playground is currently set to)`,\n\t\t),\n}\n\nasync function getExerciseContext({\n\tworkshopDirectory,\n\texerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: number\n}) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\tconst userHasAccess = await userHasAccessToWorkshop()\n\tconst authInfo = await getAuthInfo()\n\tconst progress = await getProgress()\n\tlet stepNumber = 1\n\tconst playgroundApp = await getPlaygroundApp()\n\tinvariant(playgroundApp, 'No playground app found')\n\tconst numbers = extractNumbersAndTypeFromAppNameOrPath(playgroundApp.appName)\n\tconst isCurrentExercise =\n\t\texerciseNumber === undefined ||\n\t\texerciseNumber === Number(numbers?.exerciseNumber)\n\tif (exerciseNumber === undefined) {\n\t\tinvariant(numbers, 'No numbers found in playground app name')\n\t\texerciseNumber = Number(numbers.exerciseNumber)\n\t\tstepNumber = Number(numbers.stepNumber)\n\t}\n\tconst exercise = await getExercise(exerciseNumber)\n\tinvariant(exercise, `No exercise found for exercise number ${exerciseNumber}`)\n\n\tconst videoInfos = await getEpicVideoInfos([\n\t\t...(exercise.instructionsEpicVideoEmbeds ?? []),\n\t\t...exercise.steps.flatMap((s) => s.problem?.epicVideoEmbeds ?? []),\n\t\t...exercise.steps.flatMap((s) => s.solution?.epicVideoEmbeds ?? []),\n\t\t...(exercise.finishedEpicVideoEmbeds ?? []),\n\t])\n\n\tfunction getTranscripts(embeds?: Array<string>) {\n\t\tif (!embeds) return []\n\t\tif (!userHasAccess && embeds.length) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tmessage: `User must upgrade before they can get access to ${embeds.length} transcript${embeds.length === 1 ? '' : 's'}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn embeds.map((embed) => {\n\t\t\tconst info = videoInfos[embed]\n\t\t\tif (info) {\n\t\t\t\tif (info.status === 'error') {\n\t\t\t\t\tif (info.type === 'region-restricted') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tembed,\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\ttype: info.type,\n\t\t\t\t\t\t\trequestedCountry: info.requestCountry,\n\t\t\t\t\t\t\trestrictedCountry: info.restrictedCountry,\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tembed,\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\ttype: info.type,\n\t\t\t\t\t\t\tstatusCode: info.statusCode,\n\t\t\t\t\t\t\tstatusText: info.statusText,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tembed,\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\ttranscript: info.transcript,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\tembed,\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\ttype: 'not-found',\n\t\t\t\t\tmessage: 'No transcript found',\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tasync function getFileContent(filePath: string) {\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tcontent: (await safeReadFile(filePath)) ?? 'None found',\n\t\t}\n\t}\n\n\tconst context = {\n\t\tcurrentContext: {\n\t\t\tuser: {\n\t\t\t\thasAccess: userHasAccess,\n\t\t\t\tisAuthenticated: Boolean(authInfo),\n\t\t\t\temail: authInfo?.email,\n\t\t\t},\n\t\t\tplayground: isCurrentExercise\n\t\t\t\t? {\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t\tstepNumber,\n\t\t\t\t\t}\n\t\t\t\t: 'currently set to a different exercise',\n\t\t},\n\t\texerciseInfo: {\n\t\t\tnumber: exerciseNumber,\n\t\t\tintro: {\n\t\t\t\tcontent: await getFileContent(\n\t\t\t\t\tpath.join(exercise.fullPath, 'README.mdx'),\n\t\t\t\t),\n\t\t\t\ttranscripts: getTranscripts(exercise.instructionsEpicVideoEmbeds),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'instructions' && p.exerciseNumber === exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\toutro: {\n\t\t\t\tcontent: await getFileContent(\n\t\t\t\t\tpath.join(exercise.fullPath, 'FINISHED.mdx'),\n\t\t\t\t),\n\t\t\t\ttranscripts: getTranscripts(exercise.finishedEpicVideoEmbeds),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) => p.type === 'finished' && p.exerciseNumber === exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t\tsteps: exercise.steps\n\t\t\t? await Promise.all(\n\t\t\t\t\texercise.steps.map(async (app) => ({\n\t\t\t\t\t\tnumber: app.stepNumber,\n\t\t\t\t\t\tisCurrent: isCurrentExercise && app.stepNumber === stepNumber,\n\t\t\t\t\t\tprogress: progress.find(\n\t\t\t\t\t\t\t(p) =>\n\t\t\t\t\t\t\t\tp.type === 'step' &&\n\t\t\t\t\t\t\t\tp.exerciseNumber === exerciseNumber &&\n\t\t\t\t\t\t\t\tp.stepNumber === app.stepNumber,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tproblem: {\n\t\t\t\t\t\t\tcontent: app.problem\n\t\t\t\t\t\t\t\t? await getFileContent(\n\t\t\t\t\t\t\t\t\t\tpath.join(app.problem.fullPath, 'README.mdx'),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 'No problem found',\n\t\t\t\t\t\t\ttranscripts: getTranscripts(app.problem?.epicVideoEmbeds),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsolution: {\n\t\t\t\t\t\t\tcontent: app.solution\n\t\t\t\t\t\t\t\t? await getFileContent(\n\t\t\t\t\t\t\t\t\t\tpath.join(app.solution.fullPath, 'README.mdx'),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 'No solution found',\n\t\t\t\t\t\t\ttranscripts: getTranscripts(app.solution?.epicVideoEmbeds),\n\t\t\t\t\t\t},\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t: [],\n\t\tnotes: [] as Array<string>,\n\t}\n\n\tif (exercise.steps) {\n\t\tif (isCurrentExercise) {\n\t\t\tcontext.notes.push(\n\t\t\t\t`Reminder, the current step is ${stepNumber} of ${exercise.steps.length + 1}. The most relevant information will be in the context above within the current step.`,\n\t\t\t)\n\t\t}\n\t} else {\n\t\tcontext.notes.push('Unusually, this exercise has no steps.')\n\t}\n\n\treturn context\n}\n\nconst exerciseContextUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/exercise/{exerciseNumber}',\n\t{ list: undefined },\n)\n\nasync function getExerciseContextResource({\n\tworkshopDirectory,\n\texerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: number\n}): Promise<ReadResourceResult['contents'][number]> {\n\tconst context = await getExerciseContext({\n\t\tworkshopDirectory,\n\t\texerciseNumber,\n\t})\n\treturn {\n\t\turi: exerciseContextUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t\texerciseNumber: String(context.exerciseInfo.number),\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(context),\n\t}\n}\n\nexport const exerciseContextResource = {\n\tname: 'exercise_context',\n\tdescription: 'The context of the exercise',\n\turiTemplate: exerciseContextUriTemplate,\n\tgetResource: getExerciseContextResource,\n\tinputSchema: getExerciseContextInputSchema,\n}\n\nconst diffBetweenAppsInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n\tapp1: z.string().describe('The ID of the first app'),\n\tapp2: z.string().describe('The ID of the second app'),\n}\n\nasync function getDiffBetweenApps({\n\tworkshopDirectory,\n\tapp1,\n\tapp2,\n}: InputSchemaType<typeof diffBetweenAppsInputSchema>) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\n\tconst { getDiffOutputWithRelativePaths } = await import(\n\t\t'@epic-web/workshop-utils/diff.server'\n\t)\n\n\tconst app1Name = extractNumbersAndTypeFromAppNameOrPath(app1)\n\tconst app2Name = extractNumbersAndTypeFromAppNameOrPath(app2)\n\n\tconst apps = await getApps()\n\tconst app1App = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.find(\n\t\t\t(a) =>\n\t\t\t\ta.exerciseNumber === Number(app1Name?.exerciseNumber) &&\n\t\t\t\ta.stepNumber === Number(app1Name?.stepNumber) &&\n\t\t\t\ta.type === app1Name?.type,\n\t\t)\n\tconst app2App = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.find(\n\t\t\t(a) =>\n\t\t\t\ta.exerciseNumber === Number(app2Name?.exerciseNumber) &&\n\t\t\t\ta.stepNumber === Number(app2Name?.stepNumber) &&\n\t\t\t\ta.type === app2Name?.type,\n\t\t)\n\n\tinvariant(app1App, `No app found for ${app1}`)\n\tinvariant(app2App, `No app found for ${app2}`)\n\n\tconst diffCode = await getDiffOutputWithRelativePaths(app1App, app2App)\n\n\tif (!diffCode) return 'No changes'\n\n\treturn diffCode\n}\n\nasync function getDiffBetweenAppsResource({\n\tworkshopDirectory,\n\tapp1,\n\tapp2,\n}: InputSchemaType<typeof diffBetweenAppsInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: diffBetweenAppsUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t\tapp1,\n\t\t\tapp2,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(\n\t\t\tawait getDiffBetweenApps({ workshopDirectory, app1, app2 }),\n\t\t),\n\t}\n}\n\nconst diffBetweenAppsUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/diff-between-apps/{app1}__vs___{app2}',\n\t{ list: undefined },\n)\n\nexport const diffBetweenAppsResource = {\n\tname: 'diff_between_apps',\n\tdescription: 'The diff between two apps',\n\turiTemplate: diffBetweenAppsUriTemplate,\n\tgetResource: getDiffBetweenAppsResource,\n\tinputSchema: diffBetweenAppsInputSchema,\n}\n\nconst getExerciseStepProgressDiffInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n}\n\nasync function getExerciseStepProgressDiff({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getExerciseStepProgressDiffInputSchema>) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\n\tconst { getDiffOutputWithRelativePaths } = await import(\n\t\t'@epic-web/workshop-utils/diff.server'\n\t)\n\n\tconst apps = await getApps()\n\tconst playgroundApp = apps.find(isPlaygroundApp)\n\n\tinvariant(playgroundApp, 'No playground app found')\n\n\tconst baseApp = playgroundApp\n\tconst solutionDir = await findSolutionDir({\n\t\tfullPath: await getFullPathFromAppName(playgroundApp.appName),\n\t})\n\tconst headApp = apps.find((a) => a.fullPath === solutionDir)\n\n\tinvariant(headApp, 'No playground solution app found')\n\n\tconst diffCode = await getDiffOutputWithRelativePaths(baseApp, headApp)\n\n\tif (!diffCode) return 'No changes'\n\n\treturn diffCode\n}\n\nconst exerciseStepProgressDiffUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/exercise-step-progress-diff',\n\t{ list: undefined },\n)\n\nasync function getExerciseStepProgressDiffResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getExerciseStepProgressDiffInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: exerciseStepProgressDiffUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(\n\t\t\tawait getExerciseStepProgressDiff({ workshopDirectory }),\n\t\t),\n\t}\n}\n\nexport const exerciseStepProgressDiffResource = {\n\tname: 'exercise_step_progress_diff',\n\tdescription: 'The diff between the current exercise step and the solution',\n\turiTemplate: exerciseStepProgressDiffUriTemplate,\n\tgetResource: getExerciseStepProgressDiffResource,\n\tinputSchema: getExerciseStepProgressDiffInputSchema,\n}\n\nconst getUserInfoInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n}\n\nconst userInfoUri = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/info',\n\t{ list: undefined },\n)\n\nasync function getUserInfoResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getUserInfoInputSchema>) {\n\tconst userInfo = await getUserInfo()\n\treturn {\n\t\turi: userInfoUri.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(userInfo),\n\t}\n}\n\nexport const userInfoResource = {\n\tname: 'user_info',\n\tdescription: 'Information about the current user',\n\turiTemplate: userInfoUri,\n\tgetResource: getUserInfoResource,\n\tinputSchema: getUserInfoInputSchema,\n}\n\nconst getUserAccessInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n}\n\nconst userAccessUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/access',\n\t{ list: undefined },\n)\n\nasync function getUserAccessResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getUserAccessInputSchema>) {\n\tconst userHasAccess = await userHasAccessToWorkshop()\n\treturn {\n\t\turi: userAccessUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify({ userHasAccess }),\n\t}\n}\n\nexport const userAccessResource = {\n\tname: 'user_access',\n\tdescription: 'Whether the current user has access to the workshop',\n\turiTemplate: userAccessUriTemplate,\n\tgetResource: getUserAccessResource,\n\tinputSchema: getUserAccessInputSchema,\n}\n\nconst userProgressInputSchema = {\n\tworkshopDirectory: z.string().describe('The workshop directory'),\n}\n\nconst userProgressUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/progress',\n\t{ list: undefined },\n)\n\nasync function getUserProgressResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof userProgressInputSchema>) {\n\tconst userProgress = await getProgress()\n\treturn {\n\t\turi: userProgressUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(userProgress),\n\t}\n}\n\nexport const userProgressResource = {\n\tname: 'user_progress',\n\tdescription: 'The progress of the current user',\n\turiTemplate: userProgressUriTemplate,\n\tgetResource: getUserProgressResource,\n\tinputSchema: userProgressInputSchema,\n}\n\nexport function initResources(server: McpServer) {\n\tserver.resource(\n\t\tworkshopContextResource.name,\n\t\tworkshopContextResource.uriTemplate,\n\t\t{ description: workshopContextResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tconst resource = await workshopContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn { contents: [resource] }\n\t\t},\n\t)\n\n\tserver.resource(\n\t\texerciseContextResource.name,\n\t\texerciseContextResource.uriTemplate,\n\t\t{ description: exerciseContextResource.description },\n\t\tasync (\n\t\t\t_uri,\n\t\t\t{ workshopDirectory, exerciseNumber: providedExerciseNumber },\n\t\t) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tinvariant(\n\t\t\t\ttypeof providedExerciseNumber === 'string',\n\t\t\t\t'A single exerciseNumber is required',\n\t\t\t)\n\t\t\tconst exerciseNumber = Number(providedExerciseNumber)\n\t\t\tinvariant(!isNaN(exerciseNumber), 'exerciseNumber must be a number')\n\t\t\tinvariant(\n\t\t\t\texerciseNumber >= 0,\n\t\t\t\t'exerciseNumber must be greater than or equal to 0',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait exerciseContextResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tdiffBetweenAppsResource.name,\n\t\tdiffBetweenAppsResource.uriTemplate,\n\t\t{ description: diffBetweenAppsResource.description },\n\t\tasync (_uri, { workshopDirectory, app1, app2 }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tinvariant(typeof app1 === 'string', 'A single app1 is required')\n\t\t\tinvariant(typeof app2 === 'string', 'A single app2 is required')\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait diffBetweenAppsResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\tapp1,\n\t\t\t\t\t\tapp2,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\texerciseStepProgressDiffResource.name,\n\t\texerciseStepProgressDiffResource.uriTemplate,\n\t\t{ description: exerciseStepProgressDiffResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait exerciseStepProgressDiffResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserInfoResource.name,\n\t\tuserInfoResource.uriTemplate,\n\t\t{ description: userInfoResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [await userInfoResource.getResource({ workshopDirectory })],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserAccessResource.name,\n\t\tuserAccessResource.uriTemplate,\n\t\t{ description: userAccessResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [await userAccessResource.getResource({ workshopDirectory })],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserProgressResource.name,\n\t\tuserProgressResource.uriTemplate,\n\t\t{ description: userProgressResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait userProgressResource.getResource({ workshopDirectory }),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n}\n"]}
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path'\nimport { invariant } from '@epic-web/invariant'\nimport {\n\textractNumbersAndTypeFromAppNameOrPath,\n\tfindSolutionDir,\n\tgetApps,\n\tgetExercise,\n\tgetExercises,\n\tgetFullPathFromAppName,\n\tgetPlaygroundApp,\n\tisExerciseStepApp,\n\tisPlaygroundApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { getAuthInfo } from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetEpicVideoInfos,\n\tuserHasAccessToWorkshop,\n\tgetUserInfo,\n\tgetProgress,\n} from '@epic-web/workshop-utils/epic-api.server'\nimport {\n\ttype McpServer,\n\tResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\nimport {\n\thandleWorkshopDirectory,\n\ttype InputSchemaType,\n\tsafeReadFile,\n\tworkshopDirectoryInputSchema,\n} from './utils.js'\n\nexport const getWorkshopContextInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n}\n\nexport async function getWorkshopContext({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getWorkshopContextInputSchema>) {\n\tconst workshopRoot = await handleWorkshopDirectory(workshopDirectory)\n\tconst inWorkshop = (...d: Array<string>) => path.join(workshopRoot, ...d)\n\tconst readInWorkshop = (...d: Array<string>) => safeReadFile(inWorkshop(...d))\n\tconst progress = await getProgress()\n\n\tconst output = {\n\t\tmeta: {\n\t\t\t'README.md': await readInWorkshop('README.md'),\n\t\t\tconfig: (\n\t\t\t\tJSON.parse((await readInWorkshop('package.json')) || '{}') as any\n\t\t\t).epicshop,\n\t\t\tinstructions: {\n\t\t\t\tcontent: await readInWorkshop('exercise', 'README.mdx'),\n\t\t\t\tprogress: progress.find((p) => p.type === 'instructions'),\n\t\t\t},\n\t\t\tfinishedInstructions: {\n\t\t\t\tcontent: await readInWorkshop('exercise', 'FINISHED.mdx'),\n\t\t\t\tprogress: progress.find((p) => p.type === 'finished'),\n\t\t\t},\n\t\t},\n\t\texercises: [] as Array<any>,\n\t}\n\n\tconst exercises = await getExercises()\n\tfor (const exercise of exercises) {\n\t\tconst exerciseInfo = {\n\t\t\tfullPath: exercise.fullPath,\n\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\ttitle: exercise.title,\n\t\t\tinstructions: {\n\t\t\t\tcontent: await safeReadFile(path.join(exercise.fullPath, 'README.mdx')),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'instructions' &&\n\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\tfinishedInstructions: {\n\t\t\t\tcontent: await safeReadFile(\n\t\t\t\t\tpath.join(exercise.fullPath, 'FINISHED.mdx'),\n\t\t\t\t),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'finished' &&\n\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\tsteps: exercise.steps.map((step) => {\n\t\t\t\treturn {\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t\tprogress: progress.find(\n\t\t\t\t\t\t(p) =>\n\t\t\t\t\t\t\tp.type === 'step' &&\n\t\t\t\t\t\t\tp.exerciseNumber === exercise.exerciseNumber &&\n\t\t\t\t\t\t\tp.stepNumber === step.stepNumber,\n\t\t\t\t\t),\n\t\t\t\t\ttitle: step.problem?.title ?? step.solution?.title ?? null,\n\t\t\t\t\tproblem: step.problem\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfullPath: step.problem.fullPath,\n\t\t\t\t\t\t\t\ttestConfig: step.problem.test,\n\t\t\t\t\t\t\t\tdevConfig: step.problem.dev,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: 'No problem app',\n\t\t\t\t\tsolution: step.solution\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfullPath: step.solution.fullPath,\n\t\t\t\t\t\t\t\ttestConfig: step.solution.test,\n\t\t\t\t\t\t\t\tdevConfig: step.solution.dev,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: 'No solution app',\n\t\t\t\t}\n\t\t\t}),\n\t\t}\n\t\toutput.exercises.push(exerciseInfo)\n\t}\n\n\treturn output\n}\n\nconst workshopContextUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/workshop-context',\n\t{ list: undefined },\n)\n\nexport async function getWorkshopContextResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getWorkshopContextInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: workshopContextUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(await getWorkshopContext({ workshopDirectory })),\n\t}\n}\n\nexport const workshopContextResource = {\n\tname: 'workshop_context',\n\tdescription: 'The context of the workshop',\n\turiTemplate: workshopContextUriTemplate,\n\tgetResource: getWorkshopContextResource,\n\tinputSchema: getWorkshopContextInputSchema,\n}\n\nconst getExerciseContextInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n\texerciseNumber: z.coerce\n\t\t.number()\n\t\t.optional()\n\t\t.describe(\n\t\t\t`The exercise number to get the context for (defaults to the exercise number the playground is currently set to)`,\n\t\t),\n}\n\nasync function getExerciseContext({\n\tworkshopDirectory,\n\texerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: number\n}) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\tconst userHasAccess = await userHasAccessToWorkshop()\n\tconst authInfo = await getAuthInfo()\n\tconst progress = await getProgress()\n\tlet stepNumber = 1\n\tconst playgroundApp = await getPlaygroundApp()\n\tinvariant(playgroundApp, 'No playground app found')\n\tconst numbers = extractNumbersAndTypeFromAppNameOrPath(playgroundApp.appName)\n\tconst isCurrentExercise =\n\t\texerciseNumber === undefined ||\n\t\texerciseNumber === Number(numbers?.exerciseNumber)\n\tif (exerciseNumber === undefined) {\n\t\tinvariant(numbers, 'No numbers found in playground app name')\n\t\texerciseNumber = Number(numbers.exerciseNumber)\n\t\tstepNumber = Number(numbers.stepNumber)\n\t}\n\tconst exercise = await getExercise(exerciseNumber)\n\tinvariant(exercise, `No exercise found for exercise number ${exerciseNumber}`)\n\n\tconst videoInfos = await getEpicVideoInfos([\n\t\t...(exercise.instructionsEpicVideoEmbeds ?? []),\n\t\t...exercise.steps.flatMap((s) => s.problem?.epicVideoEmbeds ?? []),\n\t\t...exercise.steps.flatMap((s) => s.solution?.epicVideoEmbeds ?? []),\n\t\t...(exercise.finishedEpicVideoEmbeds ?? []),\n\t])\n\n\tfunction getTranscripts(embeds?: Array<string>) {\n\t\tif (!embeds) return []\n\t\tif (!userHasAccess && embeds.length) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tmessage: `User must upgrade before they can get access to ${embeds.length} transcript${embeds.length === 1 ? '' : 's'}.`,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn embeds.map((embed) => {\n\t\t\tconst info = videoInfos[embed]\n\t\t\tif (info) {\n\t\t\t\tif (info.status === 'error') {\n\t\t\t\t\tif (info.type === 'region-restricted') {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tembed,\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\ttype: info.type,\n\t\t\t\t\t\t\trequestedCountry: info.requestCountry,\n\t\t\t\t\t\t\trestrictedCountry: info.restrictedCountry,\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tembed,\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\ttype: info.type,\n\t\t\t\t\t\t\tstatusCode: info.statusCode,\n\t\t\t\t\t\t\tstatusText: info.statusText,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tembed,\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\ttranscript: info.transcript,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\tembed,\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\ttype: 'not-found',\n\t\t\t\t\tmessage: 'No transcript found',\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tasync function getFileContent(filePath: string) {\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tcontent: (await safeReadFile(filePath)) ?? 'None found',\n\t\t}\n\t}\n\n\tconst context = {\n\t\tcurrentContext: {\n\t\t\tuser: {\n\t\t\t\thasAccess: userHasAccess,\n\t\t\t\tisAuthenticated: Boolean(authInfo),\n\t\t\t\temail: authInfo?.email,\n\t\t\t},\n\t\t\tplayground: isCurrentExercise\n\t\t\t\t? {\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t\tstepNumber,\n\t\t\t\t\t}\n\t\t\t\t: 'currently set to a different exercise',\n\t\t},\n\t\texerciseInfo: {\n\t\t\tnumber: exerciseNumber,\n\t\t\tintro: {\n\t\t\t\tcontent: await getFileContent(\n\t\t\t\t\tpath.join(exercise.fullPath, 'README.mdx'),\n\t\t\t\t),\n\t\t\t\ttranscripts: getTranscripts(exercise.instructionsEpicVideoEmbeds),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.type === 'instructions' && p.exerciseNumber === exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t\toutro: {\n\t\t\t\tcontent: await getFileContent(\n\t\t\t\t\tpath.join(exercise.fullPath, 'FINISHED.mdx'),\n\t\t\t\t),\n\t\t\t\ttranscripts: getTranscripts(exercise.finishedEpicVideoEmbeds),\n\t\t\t\tprogress: progress.find(\n\t\t\t\t\t(p) => p.type === 'finished' && p.exerciseNumber === exerciseNumber,\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t\tsteps: exercise.steps\n\t\t\t? await Promise.all(\n\t\t\t\t\texercise.steps.map(async (app) => ({\n\t\t\t\t\t\tnumber: app.stepNumber,\n\t\t\t\t\t\tisCurrent: isCurrentExercise && app.stepNumber === stepNumber,\n\t\t\t\t\t\tprogress: progress.find(\n\t\t\t\t\t\t\t(p) =>\n\t\t\t\t\t\t\t\tp.type === 'step' &&\n\t\t\t\t\t\t\t\tp.exerciseNumber === exerciseNumber &&\n\t\t\t\t\t\t\t\tp.stepNumber === app.stepNumber,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tproblem: {\n\t\t\t\t\t\t\tcontent: app.problem\n\t\t\t\t\t\t\t\t? await getFileContent(\n\t\t\t\t\t\t\t\t\t\tpath.join(app.problem.fullPath, 'README.mdx'),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 'No problem found',\n\t\t\t\t\t\t\ttranscripts: getTranscripts(app.problem?.epicVideoEmbeds),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsolution: {\n\t\t\t\t\t\t\tcontent: app.solution\n\t\t\t\t\t\t\t\t? await getFileContent(\n\t\t\t\t\t\t\t\t\t\tpath.join(app.solution.fullPath, 'README.mdx'),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: 'No solution found',\n\t\t\t\t\t\t\ttranscripts: getTranscripts(app.solution?.epicVideoEmbeds),\n\t\t\t\t\t\t},\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t: [],\n\t\tnotes: [] as Array<string>,\n\t}\n\n\tif (exercise.steps) {\n\t\tif (isCurrentExercise) {\n\t\t\tcontext.notes.push(\n\t\t\t\t`Reminder, the current step is ${stepNumber} of ${exercise.steps.length + 1}. The most relevant information will be in the context above within the current step.`,\n\t\t\t)\n\t\t}\n\t} else {\n\t\tcontext.notes.push('Unusually, this exercise has no steps.')\n\t}\n\n\treturn context\n}\n\nconst exerciseContextUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/exercise/{exerciseNumber}',\n\t{ list: undefined },\n)\n\nasync function getExerciseContextResource({\n\tworkshopDirectory,\n\texerciseNumber,\n}: {\n\tworkshopDirectory: string\n\texerciseNumber?: number\n}): Promise<ReadResourceResult['contents'][number]> {\n\tconst context = await getExerciseContext({\n\t\tworkshopDirectory,\n\t\texerciseNumber,\n\t})\n\treturn {\n\t\turi: exerciseContextUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t\texerciseNumber: String(context.exerciseInfo.number),\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(context),\n\t}\n}\n\nexport const exerciseContextResource = {\n\tname: 'exercise_context',\n\tdescription: 'The context of the exercise',\n\turiTemplate: exerciseContextUriTemplate,\n\tgetResource: getExerciseContextResource,\n\tinputSchema: getExerciseContextInputSchema,\n}\n\nconst diffBetweenAppsInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n\tapp1: z.string().describe('The ID of the first app'),\n\tapp2: z.string().describe('The ID of the second app'),\n}\n\nasync function getDiffBetweenApps({\n\tworkshopDirectory,\n\tapp1,\n\tapp2,\n}: InputSchemaType<typeof diffBetweenAppsInputSchema>) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\n\tconst { getDiffOutputWithRelativePaths } = await import(\n\t\t'@epic-web/workshop-utils/diff.server'\n\t)\n\n\tconst app1Name = extractNumbersAndTypeFromAppNameOrPath(app1)\n\tconst app2Name = extractNumbersAndTypeFromAppNameOrPath(app2)\n\n\tconst apps = await getApps()\n\tconst app1App = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.find(\n\t\t\t(a) =>\n\t\t\t\ta.exerciseNumber === Number(app1Name?.exerciseNumber) &&\n\t\t\t\ta.stepNumber === Number(app1Name?.stepNumber) &&\n\t\t\t\ta.type === app1Name?.type,\n\t\t)\n\tconst app2App = apps\n\t\t.filter(isExerciseStepApp)\n\t\t.find(\n\t\t\t(a) =>\n\t\t\t\ta.exerciseNumber === Number(app2Name?.exerciseNumber) &&\n\t\t\t\ta.stepNumber === Number(app2Name?.stepNumber) &&\n\t\t\t\ta.type === app2Name?.type,\n\t\t)\n\n\tinvariant(app1App, `No app found for ${app1}`)\n\tinvariant(app2App, `No app found for ${app2}`)\n\n\tconst diffCode = await getDiffOutputWithRelativePaths(app1App, app2App)\n\n\tif (!diffCode) return 'No changes'\n\n\treturn diffCode\n}\n\nasync function getDiffBetweenAppsResource({\n\tworkshopDirectory,\n\tapp1,\n\tapp2,\n}: InputSchemaType<typeof diffBetweenAppsInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: diffBetweenAppsUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t\tapp1,\n\t\t\tapp2,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(\n\t\t\tawait getDiffBetweenApps({ workshopDirectory, app1, app2 }),\n\t\t),\n\t}\n}\n\nconst diffBetweenAppsUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/diff-between-apps/{app1}__vs___{app2}',\n\t{ list: undefined },\n)\n\nexport const diffBetweenAppsResource = {\n\tname: 'diff_between_apps',\n\tdescription: 'The diff between two apps',\n\turiTemplate: diffBetweenAppsUriTemplate,\n\tgetResource: getDiffBetweenAppsResource,\n\tinputSchema: diffBetweenAppsInputSchema,\n}\n\nconst getExerciseStepProgressDiffInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n}\n\nasync function getExerciseStepProgressDiff({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getExerciseStepProgressDiffInputSchema>) {\n\tawait handleWorkshopDirectory(workshopDirectory)\n\n\tconst { getDiffOutputWithRelativePaths } = await import(\n\t\t'@epic-web/workshop-utils/diff.server'\n\t)\n\n\tconst apps = await getApps()\n\tconst playgroundApp = apps.find(isPlaygroundApp)\n\n\tinvariant(playgroundApp, 'No playground app found')\n\n\tconst baseApp = playgroundApp\n\tconst solutionDir = await findSolutionDir({\n\t\tfullPath: await getFullPathFromAppName(playgroundApp.appName),\n\t})\n\tconst headApp = apps.find((a) => a.fullPath === solutionDir)\n\n\tinvariant(headApp, 'No playground solution app found')\n\n\tconst diffCode = await getDiffOutputWithRelativePaths(baseApp, headApp)\n\n\tif (!diffCode) return 'No changes'\n\n\treturn diffCode\n}\n\nconst exerciseStepProgressDiffUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/exercise-step-progress-diff',\n\t{ list: undefined },\n)\n\nasync function getExerciseStepProgressDiffResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getExerciseStepProgressDiffInputSchema>): Promise<\n\tReadResourceResult['contents'][number]\n> {\n\treturn {\n\t\turi: exerciseStepProgressDiffUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(\n\t\t\tawait getExerciseStepProgressDiff({ workshopDirectory }),\n\t\t),\n\t}\n}\n\nexport const exerciseStepProgressDiffResource = {\n\tname: 'exercise_step_progress_diff',\n\tdescription: 'The diff between the current exercise step and the solution',\n\turiTemplate: exerciseStepProgressDiffUriTemplate,\n\tgetResource: getExerciseStepProgressDiffResource,\n\tinputSchema: getExerciseStepProgressDiffInputSchema,\n}\n\nconst getUserInfoInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n}\n\nconst userInfoUri = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/info',\n\t{ list: undefined },\n)\n\nasync function getUserInfoResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getUserInfoInputSchema>) {\n\tconst userInfo = await getUserInfo()\n\treturn {\n\t\turi: userInfoUri.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(userInfo),\n\t}\n}\n\nexport const userInfoResource = {\n\tname: 'user_info',\n\tdescription: 'Information about the current user',\n\turiTemplate: userInfoUri,\n\tgetResource: getUserInfoResource,\n\tinputSchema: getUserInfoInputSchema,\n}\n\nconst getUserAccessInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n}\n\nconst userAccessUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/access',\n\t{ list: undefined },\n)\n\nasync function getUserAccessResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof getUserAccessInputSchema>) {\n\tconst userHasAccess = await userHasAccessToWorkshop()\n\treturn {\n\t\turi: userAccessUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify({ userHasAccess }),\n\t}\n}\n\nexport const userAccessResource = {\n\tname: 'user_access',\n\tdescription: 'Whether the current user has access to the workshop',\n\turiTemplate: userAccessUriTemplate,\n\tgetResource: getUserAccessResource,\n\tinputSchema: getUserAccessInputSchema,\n}\n\nconst userProgressInputSchema = {\n\tworkshopDirectory: workshopDirectoryInputSchema,\n}\n\nconst userProgressUriTemplate = new ResourceTemplate(\n\t'epicshop://{workshopDirectory}/user/progress',\n\t{ list: undefined },\n)\n\nasync function getUserProgressResource({\n\tworkshopDirectory,\n}: InputSchemaType<typeof userProgressInputSchema>) {\n\tconst userProgress = await getProgress()\n\treturn {\n\t\turi: userProgressUriTemplate.uriTemplate.expand({\n\t\t\tworkshopDirectory,\n\t\t}),\n\t\tmimeType: 'application/json',\n\t\ttext: JSON.stringify(userProgress),\n\t}\n}\n\nexport const userProgressResource = {\n\tname: 'user_progress',\n\tdescription: 'The progress of the current user',\n\turiTemplate: userProgressUriTemplate,\n\tgetResource: getUserProgressResource,\n\tinputSchema: userProgressInputSchema,\n}\n\nexport function initResources(server: McpServer) {\n\tserver.resource(\n\t\tworkshopContextResource.name,\n\t\tworkshopContextResource.uriTemplate,\n\t\t{ description: workshopContextResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await workshopContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn { contents: [resource] }\n\t\t},\n\t)\n\n\tserver.resource(\n\t\texerciseContextResource.name,\n\t\texerciseContextResource.uriTemplate,\n\t\t{ description: exerciseContextResource.description },\n\t\tasync (\n\t\t\t_uri,\n\t\t\t{ workshopDirectory, exerciseNumber: providedExerciseNumber },\n\t\t) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tinvariant(\n\t\t\t\ttypeof providedExerciseNumber === 'string',\n\t\t\t\t'A single exerciseNumber is required',\n\t\t\t)\n\t\t\tconst exerciseNumber = Number(providedExerciseNumber)\n\t\t\tinvariant(!isNaN(exerciseNumber), 'exerciseNumber must be a number')\n\t\t\tinvariant(\n\t\t\t\texerciseNumber >= 0,\n\t\t\t\t'exerciseNumber must be greater than or equal to 0',\n\t\t\t)\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait exerciseContextResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\texerciseNumber,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tdiffBetweenAppsResource.name,\n\t\tdiffBetweenAppsResource.uriTemplate,\n\t\t{ description: diffBetweenAppsResource.description },\n\t\tasync (_uri, { workshopDirectory, app1, app2 }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tinvariant(typeof app1 === 'string', 'A single app1 is required')\n\t\t\tinvariant(typeof app2 === 'string', 'A single app2 is required')\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait diffBetweenAppsResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t\tapp1,\n\t\t\t\t\t\tapp2,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\texerciseStepProgressDiffResource.name,\n\t\texerciseStepProgressDiffResource.uriTemplate,\n\t\t{ description: exerciseStepProgressDiffResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait exerciseStepProgressDiffResource.getResource({\n\t\t\t\t\t\tworkshopDirectory,\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserInfoResource.name,\n\t\tuserInfoResource.uriTemplate,\n\t\t{ description: userInfoResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tcontents: [await userInfoResource.getResource({ workshopDirectory })],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserAccessResource.name,\n\t\tuserAccessResource.uriTemplate,\n\t\t{ description: userAccessResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\treturn {\n\t\t\t\tcontents: [await userAccessResource.getResource({ workshopDirectory })],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.resource(\n\t\tuserProgressResource.name,\n\t\tuserProgressResource.uriTemplate,\n\t\t{ description: userProgressResource.description },\n\t\tasync (_uri, { workshopDirectory }) => {\n\t\t\tinvariant(\n\t\t\t\ttypeof workshopDirectory === 'string',\n\t\t\t\t'A single workshopDirectory is required',\n\t\t\t)\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\treturn {\n\t\t\t\tcontents: [\n\t\t\t\t\tawait userProgressResource.getResource({ workshopDirectory }),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAmBxE,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,QAiU1C;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,QAuLlD;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAuBhD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAsBxE,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,QAiU1C;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,QAyLlD;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QA4BhD"}
package/dist/esm/tools.js CHANGED
@@ -8,12 +8,12 @@ import * as client from 'openid-client';
8
8
  import { z } from 'zod';
9
9
  import { quizMe, quizMeInputSchema } from './prompts.js';
10
10
  import { diffBetweenAppsResource, exerciseContextResource, exerciseStepProgressDiffResource, userAccessResource, userInfoResource, userProgressResource, workshopContextResource, } from './resources.js';
11
- import { handleWorkshopDirectory } from './utils.js';
11
+ import { handleWorkshopDirectory, workshopDirectoryInputSchema, } from './utils.js';
12
12
  // not enough support for this yet
13
13
  const clientSupportsEmbeddedResources = false;
14
14
  export function initTools(server) {
15
15
  server.tool('login', `Allow the user to login (or sign up) to the workshop. First`.trim(), {
16
- workshopDirectory: z.string().describe('The workshop directory'),
16
+ workshopDirectory: workshopDirectoryInputSchema,
17
17
  }, async ({ workshopDirectory }) => {
18
18
  await handleWorkshopDirectory(workshopDirectory);
19
19
  const { product: { host }, } = getWorkshopConfig();
@@ -89,7 +89,7 @@ export function initTools(server) {
89
89
  }
90
90
  });
91
91
  server.tool('logout', `Allow the user to logout of the workshop (based on the workshop's host) and delete cache data.`, {
92
- workshopDirectory: z.string().describe('The workshop directory'),
92
+ workshopDirectory: workshopDirectoryInputSchema,
93
93
  }, async ({ workshopDirectory }) => {
94
94
  await handleWorkshopDirectory(workshopDirectory);
95
95
  await logout();
@@ -128,7 +128,7 @@ F. Set to the first step problem of the fifth exercise
128
128
 
129
129
  An error will be returned if no app is found for the given arguments.
130
130
  `.trim(), {
131
- workshopDirectory: z.string().describe('The workshop directory'),
131
+ workshopDirectory: workshopDirectoryInputSchema,
132
132
  exerciseNumber: z.coerce
133
133
  .number()
134
134
  .optional()
@@ -142,7 +142,7 @@ An error will be returned if no app is found for the given arguments.
142
142
  .optional()
143
143
  .describe('The type of app to set the playground to'),
144
144
  }, async ({ workshopDirectory, exerciseNumber, stepNumber, type }) => {
145
- await handleWorkshopDirectory(workshopDirectory);
145
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
146
146
  const authInfo = await getAuthInfo();
147
147
  if (authInfo) {
148
148
  const progress = await getProgress();
@@ -238,7 +238,7 @@ Intended to help you mark an Epic lesson as complete or incomplete.
238
238
 
239
239
  This will mark the Epic lesson as complete or incomplete and update the user's progress (get updated progress with the \`get_user_progress\` tool, the \`get_exercise_context\` tool, or the \`get_workshop_context\` tool).
240
240
  `.trim(), {
241
- workshopDirectory: z.string().describe('The workshop directory'),
241
+ workshopDirectory: workshopDirectoryInputSchema,
242
242
  epicLessonSlug: z
243
243
  .string()
244
244
  .describe('The slug of the Epic lesson to mark as complete (can be retrieved from the `get_exercise_context` tool or the `get_workshop_context` tool)'),
@@ -271,7 +271,7 @@ workshop. This doesn't go into as much detail per exercise as the
271
271
  \`get_exercise_context\` tool, but it is a good starting point to orient
272
272
  yourself on the workshop as a whole.
273
273
  `.trim(), workshopContextResource.inputSchema, async ({ workshopDirectory }) => {
274
- await handleWorkshopDirectory(workshopDirectory);
274
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
275
275
  const resource = await workshopContextResource.getResource({
276
276
  workshopDirectory,
277
277
  });
@@ -294,7 +294,7 @@ more than once.
294
294
  \`get_exercise_step_progress_diff\` tool to help a student understand what
295
295
  work they still need to do and answer any questions about the exercise.
296
296
  `.trim(), exerciseContextResource.inputSchema, async ({ workshopDirectory, exerciseNumber }) => {
297
- await handleWorkshopDirectory(workshopDirectory);
297
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
298
298
  const resource = await exerciseContextResource.getResource({
299
299
  workshopDirectory,
300
300
  exerciseNumber,
@@ -316,7 +316,7 @@ App IDs are formatted as \`{exerciseNumber}.{stepNumber}.{type}\`.
316
316
 
317
317
  If the user asks for the diff for 2.3, then use 02.03.problem for app1 and 02.03.solution for app2.
318
318
  `, diffBetweenAppsResource.inputSchema, async ({ workshopDirectory, app1, app2 }) => {
319
- await handleWorkshopDirectory(workshopDirectory);
319
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
320
320
  const resource = await diffBetweenAppsResource.getResource({
321
321
  workshopDirectory,
322
322
  app1,
@@ -329,10 +329,12 @@ If the user asks for the diff for 2.3, then use 02.03.problem for app1 and 02.03
329
329
  server.tool('get_exercise_step_progress_diff', `
330
330
  Intended to help a student understand what work they still have to complete.
331
331
 
332
- This returns a git diff of the playground directory as BASE (their work in
333
- progress) against the solution directory as HEAD (the final state they're trying
334
- to achieve). Meaning, if there are lines removed, it means they still need to
335
- add those lines and if they are added, it means they still need to remove them.
332
+ This is not a typical diff. It's a diff of the user's work in progress against
333
+ the solution.
334
+
335
+ - Lines starting with \`-\` show code that needs to be removed from the user's solution
336
+ - Lines starting with \`+\` show code that needs to be added to the user's solution
337
+ - If there are differences, the user's work is incomplete
336
338
 
337
339
  Only tell the user they have more work to do if the diff output affects the
338
340
  required behavior, API, or user experience. If the differences are only
@@ -349,7 +351,7 @@ For additional context, you can use the \`get_exercise_instructions\` tool
349
351
  to get the instructions for the current exercise step to help explain the
350
352
  significance of changes.
351
353
  `.trim(), exerciseStepProgressDiffResource.inputSchema, async ({ workshopDirectory }) => {
352
- await handleWorkshopDirectory(workshopDirectory);
354
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
353
355
  const resource = await exerciseStepProgressDiffResource.getResource({
354
356
  workshopDirectory,
355
357
  });
@@ -365,7 +367,7 @@ whether the user is logged in and know who they are.
365
367
 
366
368
  If the user is not logged in, tell them to log in by running the \`login\` tool.
367
369
  `.trim(), userInfoResource.inputSchema, async ({ workshopDirectory }) => {
368
- await handleWorkshopDirectory(workshopDirectory);
370
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
369
371
  const resource = await userInfoResource.getResource({ workshopDirectory });
370
372
  return {
371
373
  content: [getEmbeddedResourceContent(resource)],
@@ -384,7 +386,7 @@ Paid features include:
384
386
 
385
387
  Encourage the user to upgrade if they need access to the paid features.
386
388
  `.trim(), userAccessResource.inputSchema, async ({ workshopDirectory }) => {
387
- await handleWorkshopDirectory(workshopDirectory);
389
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
388
390
  const resource = await userAccessResource.getResource({
389
391
  workshopDirectory,
390
392
  });
@@ -398,7 +400,7 @@ to know what the next step that needs to be completed is. Make sure to provide
398
400
  the user with the URL of relevant incomplete lessons so they can watch them and
399
401
  then mark them as complete.
400
402
  `.trim(), userProgressResource.inputSchema, async ({ workshopDirectory }) => {
401
- await handleWorkshopDirectory(workshopDirectory);
403
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
402
404
  const resource = await userProgressResource.getResource({
403
405
  workshopDirectory,
404
406
  });
@@ -412,7 +414,12 @@ export function initPromptTools(server) {
412
414
  server.tool('get_quiz_instructions', `
413
415
  If the user asks you to quiz them on a topic from the workshop, use this tool to
414
416
  retrieve the instructions for how to do so.
417
+
418
+ - If the user asks for a specific exercise, supply that exercise number.
419
+ - If they ask for a specific exericse, supply that exercise number.
420
+ - If they ask for a topic and you don't know which exercise that topic is in, use \`get_workshop_context\` to get the list of exercises and their topics and then supply the appropriate exercise number.
415
421
  `.trim(), quizMeInputSchema, async ({ workshopDirectory, exerciseNumber }) => {
422
+ workshopDirectory = await handleWorkshopDirectory(workshopDirectory);
416
423
  const result = await quizMe({ workshopDirectory, exerciseNumber });
417
424
  return {
418
425
  // QUESTION: will a prompt ever return messages that have role: 'assistant'?
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from '@epic-web/invariant'\nimport {\n\tgetApps,\n\tgetExerciseApp,\n\tgetPlaygroundAppName,\n\tisExerciseStepApp,\n\tisProblemApp,\n\tsetPlayground,\n\ttype ExerciseStepApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { deleteCache } from '@epic-web/workshop-utils/cache.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport {\n\tgetAuthInfo,\n\tlogout,\n\tsetAuthInfo,\n} from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetProgress,\n\tgetUserInfo,\n\tupdateProgress,\n} from '@epic-web/workshop-utils/epic-api.server'\nimport { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\nimport * as client from 'openid-client'\nimport { z } from 'zod'\nimport { quizMe, quizMeInputSchema } from './prompts.js'\nimport {\n\tdiffBetweenAppsResource,\n\texerciseContextResource,\n\texerciseStepProgressDiffResource,\n\tuserAccessResource,\n\tuserInfoResource,\n\tuserProgressResource,\n\tworkshopContextResource,\n} from './resources.js'\nimport { handleWorkshopDirectory } from './utils.js'\n\n// not enough support for this yet\nconst clientSupportsEmbeddedResources = false\n\nexport function initTools(server: McpServer) {\n\tserver.tool(\n\t\t'login',\n\t\t`Allow the user to login (or sign up) to the workshop. First`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: z.string().describe('The workshop directory'),\n\t\t},\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst {\n\t\t\t\tproduct: { host },\n\t\t\t} = getWorkshopConfig()\n\t\t\tconst ISSUER = `https://${host}/oauth`\n\t\t\tconst config = await client.discovery(new URL(ISSUER), 'EPICSHOP_APP')\n\t\t\tconst deviceResponse = await client.initiateDeviceAuthorization(\n\t\t\t\tconfig,\n\t\t\t\t{},\n\t\t\t)\n\n\t\t\tvoid handleAuthFlow()\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Please go to ${deviceResponse.verification_uri_complete}. Verify the code on the page is \"${deviceResponse.user_code}\" to login.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\n\t\t\tasync function handleAuthFlow() {\n\t\t\t\tconst UserInfoSchema = z.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\temail: z.string(),\n\t\t\t\t\tname: z.string().nullable().optional(),\n\t\t\t\t})\n\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\tvoid server.server.notification({\n\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\tmessage: 'Device authorization timed out',\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t}, deviceResponse.expires_in * 1000)\n\n\t\t\t\ttry {\n\t\t\t\t\tconst tokenSet = await client.pollDeviceAuthorizationGrant(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tdeviceResponse,\n\t\t\t\t\t)\n\t\t\t\t\tclearTimeout(timeout)\n\n\t\t\t\t\tif (!tokenSet) {\n\t\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\t\tmessage: 'No token set',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tconst protectedResourceResponse = await client.fetchProtectedResource(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\ttokenSet.access_token,\n\t\t\t\t\t\tnew URL(`${ISSUER}/userinfo`),\n\t\t\t\t\t\t'GET',\n\t\t\t\t\t)\n\t\t\t\t\tconst userinfoRaw = await protectedResourceResponse.json()\n\t\t\t\t\tconst userinfoResult = UserInfoSchema.safeParse(userinfoRaw)\n\t\t\t\t\tif (!userinfoResult.success) {\n\t\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\t\tmessage: `Failed to parse user info: ${userinfoResult.error.message}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst userinfo = userinfoResult.data\n\n\t\t\t\t\tawait setAuthInfo({\n\t\t\t\t\t\tid: userinfo.id,\n\t\t\t\t\t\ttokenSet,\n\t\t\t\t\t\temail: userinfo.email,\n\t\t\t\t\t\tname: userinfo.name,\n\t\t\t\t\t})\n\n\t\t\t\t\tawait getUserInfo({ forceFresh: true })\n\n\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\tmessage: 'Authentication successful',\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'logout',\n\t\t`Allow the user to logout of the workshop (based on the workshop's host) and delete cache data.`,\n\t\t{\n\t\t\tworkshopDirectory: z.string().describe('The workshop directory'),\n\t\t},\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tawait logout()\n\t\t\tawait deleteCache()\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'Logged out' }],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'set_playground',\n\t\t`\nSets the playground environment so the user can continue to that exercise or see\nwhat that step looks like in their playground environment.\n\nNOTE: this will override their current exercise step work in the playground!\n\nGenerally, it is better to not provide an exerciseNumber, stepNumber, and type\nand let the user continue to the next exercise. Only provide these arguments if\nthe user explicitely asks to go to a specific exercise or step. If the user asks\nto start an exercise, specify stepNumber 1 and type 'problem' unless otherwise\ndirected.\n\nArgument examples:\nA. If logged in and there is an incomplete exercise step, set to next incomplete exercise step based on the user's progress - Most common\n\t- [No arguments]\nB. If not logged in or all exercises are complete, set to next exercise step from current (or first if there is none)\n\t- [No arguments]\nC. Set to a specific exercise step\n\t- exerciseNumber: 1\n\t- stepNumber: 1\n\t- type: 'solution'\nD. Set to the solution of the current exercise step\n\t- type: 'solution'\nE. Set to the second step problem of the current exercise\n\t- stepNumber: 2\nF. Set to the first step problem of the fifth exercise\n\t- exerciseNumber: 5\n\nAn error will be returned if no app is found for the given arguments.\n\t`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: z.string().describe('The workshop directory'),\n\t\t\texerciseNumber: z.coerce\n\t\t\t\t.number()\n\t\t\t\t.optional()\n\t\t\t\t.describe('The exercise number to set the playground to'),\n\t\t\tstepNumber: z.coerce\n\t\t\t\t.number()\n\t\t\t\t.optional()\n\t\t\t\t.describe('The step number to set the playground to'),\n\t\t\ttype: z\n\t\t\t\t.enum(['problem', 'solution'])\n\t\t\t\t.optional()\n\t\t\t\t.describe('The type of app to set the playground to'),\n\t\t},\n\t\tasync ({ workshopDirectory, exerciseNumber, stepNumber, type }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst authInfo = await getAuthInfo()\n\n\t\t\tif (authInfo) {\n\t\t\t\tconst progress = await getProgress()\n\t\t\t\tconst scoreProgress = (a: (typeof progress)[number]) => {\n\t\t\t\t\tif (a.type === 'workshop-instructions') return 0\n\t\t\t\t\tif (a.type === 'workshop-finished') return 10000\n\t\t\t\t\tif (a.type === 'instructions') return a.exerciseNumber * 100\n\t\t\t\t\tif (a.type === 'step') return a.exerciseNumber * 100 + a.stepNumber\n\t\t\t\t\tif (a.type === 'finished') return a.exerciseNumber * 100 + 100\n\n\t\t\t\t\tif (a.type === 'unknown') return 100000\n\t\t\t\t\treturn -1\n\t\t\t\t}\n\t\t\t\tconst sortedProgress = progress.sort((a, b) => {\n\t\t\t\t\treturn scoreProgress(a) - scoreProgress(b)\n\t\t\t\t})\n\t\t\t\tconst nextProgress = sortedProgress.find((p) => !p.epicCompletedAt)\n\t\t\t\tif (nextProgress) {\n\t\t\t\t\tif (nextProgress.type === 'step') {\n\t\t\t\t\t\tconst exerciseApp = await getExerciseApp({\n\t\t\t\t\t\t\texerciseNumber: nextProgress.exerciseNumber.toString(),\n\t\t\t\t\t\t\tstepNumber: nextProgress.stepNumber.toString(),\n\t\t\t\t\t\t\ttype: 'problem',\n\t\t\t\t\t\t})\n\t\t\t\t\t\tinvariant(exerciseApp, 'No exercise app found')\n\t\t\t\t\t\tawait setPlayground(exerciseApp.fullPath)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\t\t\t\ttext: `Playground set to ${exerciseApp.exerciseNumber}.${exerciseApp.stepNumber}.${exerciseApp.type}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tnextProgress.type === 'instructions' ||\n\t\t\t\t\t\tnextProgress.type === 'finished'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`The user needs to mark the ${nextProgress.exerciseNumber} ${nextProgress.type === 'instructions' ? 'instructions' : 'finished'} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tnextProgress.type === 'workshop-instructions' ||\n\t\t\t\t\t\tnextProgress.type === 'workshop-finished'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`The user needs to mark the ${nextProgress.exerciseNumber} ${nextProgress.type === 'workshop-instructions' ? 'Workshop instructions' : 'Workshop finished'} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`The user needs to mark ${nextProgress.epicLessonSlug} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst apps = await getApps()\n\t\t\tconst exerciseStepApps = apps.filter(isExerciseStepApp)\n\n\t\t\tconst playgroundAppName = await getPlaygroundAppName()\n\t\t\tconst currentExerciseStepAppIndex = exerciseStepApps.findIndex(\n\t\t\t\t(a) => a.name === playgroundAppName,\n\t\t\t)\n\n\t\t\tlet desiredApp: ExerciseStepApp | undefined\n\t\t\t// if nothing was provided, set to the next step problem app\n\t\t\tconst noArgumentsProvided = !exerciseNumber && !stepNumber && !type\n\t\t\tif (noArgumentsProvided) {\n\t\t\t\tdesiredApp = exerciseStepApps\n\t\t\t\t\t.slice(currentExerciseStepAppIndex + 1)\n\t\t\t\t\t.find(isProblemApp)\n\t\t\t\tinvariant(desiredApp, 'No next problem app found to set playground to')\n\t\t\t} else {\n\t\t\t\tconst currentExerciseStepApp =\n\t\t\t\t\texerciseStepApps[currentExerciseStepAppIndex]\n\n\t\t\t\t// otherwise, default to the current exercise step app for arguments\n\t\t\t\texerciseNumber ??= currentExerciseStepApp?.exerciseNumber\n\t\t\t\tstepNumber ??= currentExerciseStepApp?.stepNumber\n\t\t\t\ttype ??= currentExerciseStepApp?.type\n\n\t\t\t\tdesiredApp = exerciseStepApps.find(\n\t\t\t\t\t(a) =>\n\t\t\t\t\t\ta.exerciseNumber === exerciseNumber &&\n\t\t\t\t\t\ta.stepNumber === stepNumber &&\n\t\t\t\t\t\ta.type === type,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tinvariant(\n\t\t\t\tdesiredApp,\n\t\t\t\t`No app found for values derived by the arguments: ${exerciseNumber}.${stepNumber}.${type}`,\n\t\t\t)\n\t\t\tawait setPlayground(desiredApp.fullPath)\n\t\t\tconst exerciseContext = await exerciseContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\texerciseNumber: desiredApp.exerciseNumber,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Playground set to ${desiredApp.name}.`,\n\t\t\t\t\t},\n\t\t\t\t\tgetEmbeddedResourceContent(exerciseContext),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'update_progress',\n\t\t`\nIntended to help you mark an Epic lesson as complete or incomplete.\n\nThis will mark the Epic lesson as complete or incomplete and update the user's progress (get updated progress with the \\`get_user_progress\\` tool, the \\`get_exercise_context\\` tool, or the \\`get_workshop_context\\` tool).\n\t\t`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: z.string().describe('The workshop directory'),\n\t\t\tepicLessonSlug: z\n\t\t\t\t.string()\n\t\t\t\t.describe(\n\t\t\t\t\t'The slug of the Epic lesson to mark as complete (can be retrieved from the `get_exercise_context` tool or the `get_workshop_context` tool)',\n\t\t\t\t),\n\t\t\tcomplete: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.default(true)\n\t\t\t\t.describe(\n\t\t\t\t\t'Whether to mark the lesson as complete or incomplete (defaults to true)',\n\t\t\t\t),\n\t\t},\n\t\tasync ({ workshopDirectory, epicLessonSlug, complete }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tawait updateProgress({ lessonSlug: epicLessonSlug, complete })\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Lesson with slug ${epicLessonSlug} marked as ${complete ? 'complete' : 'incomplete'}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\t// TODO: add a tool to run the dev/test script for the given app\n}\n\n// These are tools that retrieve resources. Not all resources should be\n// accessible via tools, but allowing the LLM to access them on demand is useful\n// for some situations.\nexport function initResourceTools(server: McpServer) {\n\tserver.tool(\n\t\t'get_workshop_context',\n\t\t`\nIndended to help you get wholistic context of the topics covered in this\nworkshop. This doesn't go into as much detail per exercise as the\n\\`get_exercise_context\\` tool, but it is a good starting point to orient\nyourself on the workshop as a whole.\n\t\t`.trim(),\n\t\tworkshopContextResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await workshopContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_exercise_context',\n\t\t`\nIntended to help a student understand what they need to do for the current\nexercise step.\n\nThis returns the instructions MDX content for the current exercise and each\nexercise step. If the user is has the paid version of the workshop, it will also\ninclude the transcript from each of the videos as well.\n\nThe output for this will rarely change, so it's unnecessary to call this tool\nmore than once.\n\n\\`get_exercise_context\\` is often best when used with the\n\\`get_exercise_step_progress_diff\\` tool to help a student understand what\nwork they still need to do and answer any questions about the exercise.\n\t\t`.trim(),\n\t\texerciseContextResource.inputSchema,\n\t\tasync ({ workshopDirectory, exerciseNumber }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await exerciseContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\texerciseNumber,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_diff_between_apps',\n\t\t`\nIntended to give context about the changes between two apps.\n\nThe output is a git diff of the playground directory as BASE (their work in\nprogress) against the solution directory as HEAD (the final state they're trying\nto achieve).\n\nThe output is formatted as a git diff.\n\nApp IDs are formatted as \\`{exerciseNumber}.{stepNumber}.{type}\\`.\n\nIf the user asks for the diff for 2.3, then use 02.03.problem for app1 and 02.03.solution for app2.\n\t\t`,\n\t\tdiffBetweenAppsResource.inputSchema,\n\t\tasync ({ workshopDirectory, app1, app2 }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await diffBetweenAppsResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\tapp1,\n\t\t\t\tapp2,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_exercise_step_progress_diff',\n\t\t`\nIntended to help a student understand what work they still have to complete.\n\nThis returns a git diff of the playground directory as BASE (their work in\nprogress) against the solution directory as HEAD (the final state they're trying\nto achieve). Meaning, if there are lines removed, it means they still need to\nadd those lines and if they are added, it means they still need to remove them.\n\nOnly tell the user they have more work to do if the diff output affects the\nrequired behavior, API, or user experience. If the differences are only\nstylistic or organizational, explain that things look different, but they are\nstill valid and ready to be tested.\n\nIf there's a diff with significant changes, you should explain what the changes\nare and their significance. Be brief. Let them tell you whether they need you to\nelaborate.\n\nThe output for this changes over time so it's useful to call multiple times.\n\nFor additional context, you can use the \\`get_exercise_instructions\\` tool\nto get the instructions for the current exercise step to help explain the\nsignificance of changes.\n\t\t`.trim(),\n\t\texerciseStepProgressDiffResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await exerciseStepProgressDiffResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_info',\n\t\t`\nIntended to help you get information about the current user.\n\nThis includes the user's name, email, etc. It's mostly useful to determine\nwhether the user is logged in and know who they are.\n\nIf the user is not logged in, tell them to log in by running the \\`login\\` tool.\n\t\t`.trim(),\n\t\tuserInfoResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userInfoResource.getResource({ workshopDirectory })\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_access',\n\t\t`\nWill tell you whether the user has access to the paid features of the workshop.\n\nPaid features include:\n- Transcripts\n- Progress tracking\n- Access to videos\n- Access to the discord chat\n- Test tab support\n- Diff tab support\n\nEncourage the user to upgrade if they need access to the paid features.\n\t\t`.trim(),\n\t\tuserAccessResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userAccessResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_progress',\n\t\t`\nIntended to help you get the progress of the current user. Can often be helpful\nto know what the next step that needs to be completed is. Make sure to provide\nthe user with the URL of relevant incomplete lessons so they can watch them and\nthen mark them as complete.\n\t\t`.trim(),\n\t\tuserProgressResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userProgressResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n}\n\n// Sometimes the user will ask the LLM to select a prompt to use so they don't have to.\nexport function initPromptTools(server: McpServer) {\n\tserver.tool(\n\t\t'get_quiz_instructions',\n\t\t`\nIf the user asks you to quiz them on a topic from the workshop, use this tool to\nretrieve the instructions for how to do so.\n\t\t`.trim(),\n\t\tquizMeInputSchema,\n\t\tasync ({ workshopDirectory, exerciseNumber }) => {\n\t\t\tconst result = await quizMe({ workshopDirectory, exerciseNumber })\n\t\t\treturn {\n\t\t\t\t// QUESTION: will a prompt ever return messages that have role: 'assistant'?\n\t\t\t\t// if so, this may be a little confusing for the LLM, but I can't think of a\n\t\t\t\t// good use case for that so 🤷‍♂️\n\t\t\t\tcontent: result.messages.map((m) => {\n\t\t\t\t\tif (m.content.type === 'resource') {\n\t\t\t\t\t\treturn getEmbeddedResourceContent(m.content.resource)\n\t\t\t\t\t}\n\t\t\t\t\treturn m.content\n\t\t\t\t}),\n\t\t\t}\n\t\t},\n\t)\n}\n\nfunction getEmbeddedResourceContent(\n\tresource: ReadResourceResult['contents'][number],\n) {\n\tif (clientSupportsEmbeddedResources) {\n\t\treturn {\n\t\t\ttype: 'resource' as const,\n\t\t\tresource,\n\t\t}\n\t} else if (typeof resource.text === 'string') {\n\t\treturn {\n\t\t\ttype: 'text' as const,\n\t\t\ttext: resource.text,\n\t\t}\n\t} else {\n\t\tthrow new Error(\n\t\t\t`Unknown resource type: ${resource.type} for ${resource.uri}`,\n\t\t)\n\t}\n}\n"]}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from '@epic-web/invariant'\nimport {\n\tgetApps,\n\tgetExerciseApp,\n\tgetPlaygroundAppName,\n\tisExerciseStepApp,\n\tisProblemApp,\n\tsetPlayground,\n\ttype ExerciseStepApp,\n} from '@epic-web/workshop-utils/apps.server'\nimport { deleteCache } from '@epic-web/workshop-utils/cache.server'\nimport { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport {\n\tgetAuthInfo,\n\tlogout,\n\tsetAuthInfo,\n} from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetProgress,\n\tgetUserInfo,\n\tupdateProgress,\n} from '@epic-web/workshop-utils/epic-api.server'\nimport { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'\nimport * as client from 'openid-client'\nimport { z } from 'zod'\nimport { quizMe, quizMeInputSchema } from './prompts.js'\nimport {\n\tdiffBetweenAppsResource,\n\texerciseContextResource,\n\texerciseStepProgressDiffResource,\n\tuserAccessResource,\n\tuserInfoResource,\n\tuserProgressResource,\n\tworkshopContextResource,\n} from './resources.js'\nimport {\n\thandleWorkshopDirectory,\n\tworkshopDirectoryInputSchema,\n} from './utils.js'\n\n// not enough support for this yet\nconst clientSupportsEmbeddedResources = false\n\nexport function initTools(server: McpServer) {\n\tserver.tool(\n\t\t'login',\n\t\t`Allow the user to login (or sign up) to the workshop. First`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: workshopDirectoryInputSchema,\n\t\t},\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst {\n\t\t\t\tproduct: { host },\n\t\t\t} = getWorkshopConfig()\n\t\t\tconst ISSUER = `https://${host}/oauth`\n\t\t\tconst config = await client.discovery(new URL(ISSUER), 'EPICSHOP_APP')\n\t\t\tconst deviceResponse = await client.initiateDeviceAuthorization(\n\t\t\t\tconfig,\n\t\t\t\t{},\n\t\t\t)\n\n\t\t\tvoid handleAuthFlow()\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Please go to ${deviceResponse.verification_uri_complete}. Verify the code on the page is \"${deviceResponse.user_code}\" to login.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\n\t\t\tasync function handleAuthFlow() {\n\t\t\t\tconst UserInfoSchema = z.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\temail: z.string(),\n\t\t\t\t\tname: z.string().nullable().optional(),\n\t\t\t\t})\n\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\tvoid server.server.notification({\n\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\tmessage: 'Device authorization timed out',\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t}, deviceResponse.expires_in * 1000)\n\n\t\t\t\ttry {\n\t\t\t\t\tconst tokenSet = await client.pollDeviceAuthorizationGrant(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tdeviceResponse,\n\t\t\t\t\t)\n\t\t\t\t\tclearTimeout(timeout)\n\n\t\t\t\t\tif (!tokenSet) {\n\t\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\t\tmessage: 'No token set',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tconst protectedResourceResponse = await client.fetchProtectedResource(\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\ttokenSet.access_token,\n\t\t\t\t\t\tnew URL(`${ISSUER}/userinfo`),\n\t\t\t\t\t\t'GET',\n\t\t\t\t\t)\n\t\t\t\t\tconst userinfoRaw = await protectedResourceResponse.json()\n\t\t\t\t\tconst userinfoResult = UserInfoSchema.safeParse(userinfoRaw)\n\t\t\t\t\tif (!userinfoResult.success) {\n\t\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\t\tmessage: `Failed to parse user info: ${userinfoResult.error.message}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst userinfo = userinfoResult.data\n\n\t\t\t\t\tawait setAuthInfo({\n\t\t\t\t\t\tid: userinfo.id,\n\t\t\t\t\t\ttokenSet,\n\t\t\t\t\t\temail: userinfo.email,\n\t\t\t\t\t\tname: userinfo.name,\n\t\t\t\t\t})\n\n\t\t\t\t\tawait getUserInfo({ forceFresh: true })\n\n\t\t\t\t\tawait server.server.notification({\n\t\t\t\t\t\tmethod: 'notification',\n\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\tmessage: 'Authentication successful',\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tclearTimeout(timeout)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'logout',\n\t\t`Allow the user to logout of the workshop (based on the workshop's host) and delete cache data.`,\n\t\t{\n\t\t\tworkshopDirectory: workshopDirectoryInputSchema,\n\t\t},\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tawait logout()\n\t\t\tawait deleteCache()\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'Logged out' }],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'set_playground',\n\t\t`\nSets the playground environment so the user can continue to that exercise or see\nwhat that step looks like in their playground environment.\n\nNOTE: this will override their current exercise step work in the playground!\n\nGenerally, it is better to not provide an exerciseNumber, stepNumber, and type\nand let the user continue to the next exercise. Only provide these arguments if\nthe user explicitely asks to go to a specific exercise or step. If the user asks\nto start an exercise, specify stepNumber 1 and type 'problem' unless otherwise\ndirected.\n\nArgument examples:\nA. If logged in and there is an incomplete exercise step, set to next incomplete exercise step based on the user's progress - Most common\n\t- [No arguments]\nB. If not logged in or all exercises are complete, set to next exercise step from current (or first if there is none)\n\t- [No arguments]\nC. Set to a specific exercise step\n\t- exerciseNumber: 1\n\t- stepNumber: 1\n\t- type: 'solution'\nD. Set to the solution of the current exercise step\n\t- type: 'solution'\nE. Set to the second step problem of the current exercise\n\t- stepNumber: 2\nF. Set to the first step problem of the fifth exercise\n\t- exerciseNumber: 5\n\nAn error will be returned if no app is found for the given arguments.\n\t`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: workshopDirectoryInputSchema,\n\t\t\texerciseNumber: z.coerce\n\t\t\t\t.number()\n\t\t\t\t.optional()\n\t\t\t\t.describe('The exercise number to set the playground to'),\n\t\t\tstepNumber: z.coerce\n\t\t\t\t.number()\n\t\t\t\t.optional()\n\t\t\t\t.describe('The step number to set the playground to'),\n\t\t\ttype: z\n\t\t\t\t.enum(['problem', 'solution'])\n\t\t\t\t.optional()\n\t\t\t\t.describe('The type of app to set the playground to'),\n\t\t},\n\t\tasync ({ workshopDirectory, exerciseNumber, stepNumber, type }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst authInfo = await getAuthInfo()\n\n\t\t\tif (authInfo) {\n\t\t\t\tconst progress = await getProgress()\n\t\t\t\tconst scoreProgress = (a: (typeof progress)[number]) => {\n\t\t\t\t\tif (a.type === 'workshop-instructions') return 0\n\t\t\t\t\tif (a.type === 'workshop-finished') return 10000\n\t\t\t\t\tif (a.type === 'instructions') return a.exerciseNumber * 100\n\t\t\t\t\tif (a.type === 'step') return a.exerciseNumber * 100 + a.stepNumber\n\t\t\t\t\tif (a.type === 'finished') return a.exerciseNumber * 100 + 100\n\n\t\t\t\t\tif (a.type === 'unknown') return 100000\n\t\t\t\t\treturn -1\n\t\t\t\t}\n\t\t\t\tconst sortedProgress = progress.sort((a, b) => {\n\t\t\t\t\treturn scoreProgress(a) - scoreProgress(b)\n\t\t\t\t})\n\t\t\t\tconst nextProgress = sortedProgress.find((p) => !p.epicCompletedAt)\n\t\t\t\tif (nextProgress) {\n\t\t\t\t\tif (nextProgress.type === 'step') {\n\t\t\t\t\t\tconst exerciseApp = await getExerciseApp({\n\t\t\t\t\t\t\texerciseNumber: nextProgress.exerciseNumber.toString(),\n\t\t\t\t\t\t\tstepNumber: nextProgress.stepNumber.toString(),\n\t\t\t\t\t\t\ttype: 'problem',\n\t\t\t\t\t\t})\n\t\t\t\t\t\tinvariant(exerciseApp, 'No exercise app found')\n\t\t\t\t\t\tawait setPlayground(exerciseApp.fullPath)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\t\t\t\ttext: `Playground set to ${exerciseApp.exerciseNumber}.${exerciseApp.stepNumber}.${exerciseApp.type}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tnextProgress.type === 'instructions' ||\n\t\t\t\t\t\tnextProgress.type === 'finished'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`The user needs to mark the ${nextProgress.exerciseNumber} ${nextProgress.type === 'instructions' ? 'instructions' : 'finished'} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tnextProgress.type === 'workshop-instructions' ||\n\t\t\t\t\t\tnextProgress.type === 'workshop-finished'\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`The user needs to mark the ${nextProgress.exerciseNumber} ${nextProgress.type === 'workshop-instructions' ? 'Workshop instructions' : 'Workshop finished'} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`The user needs to mark ${nextProgress.epicLessonSlug} as complete before they can continue. Have them watch the video at ${nextProgress.epicLessonUrl}, then mark it as complete.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst apps = await getApps()\n\t\t\tconst exerciseStepApps = apps.filter(isExerciseStepApp)\n\n\t\t\tconst playgroundAppName = await getPlaygroundAppName()\n\t\t\tconst currentExerciseStepAppIndex = exerciseStepApps.findIndex(\n\t\t\t\t(a) => a.name === playgroundAppName,\n\t\t\t)\n\n\t\t\tlet desiredApp: ExerciseStepApp | undefined\n\t\t\t// if nothing was provided, set to the next step problem app\n\t\t\tconst noArgumentsProvided = !exerciseNumber && !stepNumber && !type\n\t\t\tif (noArgumentsProvided) {\n\t\t\t\tdesiredApp = exerciseStepApps\n\t\t\t\t\t.slice(currentExerciseStepAppIndex + 1)\n\t\t\t\t\t.find(isProblemApp)\n\t\t\t\tinvariant(desiredApp, 'No next problem app found to set playground to')\n\t\t\t} else {\n\t\t\t\tconst currentExerciseStepApp =\n\t\t\t\t\texerciseStepApps[currentExerciseStepAppIndex]\n\n\t\t\t\t// otherwise, default to the current exercise step app for arguments\n\t\t\t\texerciseNumber ??= currentExerciseStepApp?.exerciseNumber\n\t\t\t\tstepNumber ??= currentExerciseStepApp?.stepNumber\n\t\t\t\ttype ??= currentExerciseStepApp?.type\n\n\t\t\t\tdesiredApp = exerciseStepApps.find(\n\t\t\t\t\t(a) =>\n\t\t\t\t\t\ta.exerciseNumber === exerciseNumber &&\n\t\t\t\t\t\ta.stepNumber === stepNumber &&\n\t\t\t\t\t\ta.type === type,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tinvariant(\n\t\t\t\tdesiredApp,\n\t\t\t\t`No app found for values derived by the arguments: ${exerciseNumber}.${stepNumber}.${type}`,\n\t\t\t)\n\t\t\tawait setPlayground(desiredApp.fullPath)\n\t\t\tconst exerciseContext = await exerciseContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\texerciseNumber: desiredApp.exerciseNumber,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Playground set to ${desiredApp.name}.`,\n\t\t\t\t\t},\n\t\t\t\t\tgetEmbeddedResourceContent(exerciseContext),\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'update_progress',\n\t\t`\nIntended to help you mark an Epic lesson as complete or incomplete.\n\nThis will mark the Epic lesson as complete or incomplete and update the user's progress (get updated progress with the \\`get_user_progress\\` tool, the \\`get_exercise_context\\` tool, or the \\`get_workshop_context\\` tool).\n\t\t`.trim(),\n\t\t{\n\t\t\tworkshopDirectory: workshopDirectoryInputSchema,\n\t\t\tepicLessonSlug: z\n\t\t\t\t.string()\n\t\t\t\t.describe(\n\t\t\t\t\t'The slug of the Epic lesson to mark as complete (can be retrieved from the `get_exercise_context` tool or the `get_workshop_context` tool)',\n\t\t\t\t),\n\t\t\tcomplete: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.default(true)\n\t\t\t\t.describe(\n\t\t\t\t\t'Whether to mark the lesson as complete or incomplete (defaults to true)',\n\t\t\t\t),\n\t\t},\n\t\tasync ({ workshopDirectory, epicLessonSlug, complete }) => {\n\t\t\tawait handleWorkshopDirectory(workshopDirectory)\n\t\t\tawait updateProgress({ lessonSlug: epicLessonSlug, complete })\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: `Lesson with slug ${epicLessonSlug} marked as ${complete ? 'complete' : 'incomplete'}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\t\t},\n\t)\n\n\t// TODO: add a tool to run the dev/test script for the given app\n}\n\n// These are tools that retrieve resources. Not all resources should be\n// accessible via tools, but allowing the LLM to access them on demand is useful\n// for some situations.\nexport function initResourceTools(server: McpServer) {\n\tserver.tool(\n\t\t'get_workshop_context',\n\t\t`\nIndended to help you get wholistic context of the topics covered in this\nworkshop. This doesn't go into as much detail per exercise as the\n\\`get_exercise_context\\` tool, but it is a good starting point to orient\nyourself on the workshop as a whole.\n\t\t`.trim(),\n\t\tworkshopContextResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await workshopContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_exercise_context',\n\t\t`\nIntended to help a student understand what they need to do for the current\nexercise step.\n\nThis returns the instructions MDX content for the current exercise and each\nexercise step. If the user is has the paid version of the workshop, it will also\ninclude the transcript from each of the videos as well.\n\nThe output for this will rarely change, so it's unnecessary to call this tool\nmore than once.\n\n\\`get_exercise_context\\` is often best when used with the\n\\`get_exercise_step_progress_diff\\` tool to help a student understand what\nwork they still need to do and answer any questions about the exercise.\n\t\t`.trim(),\n\t\texerciseContextResource.inputSchema,\n\t\tasync ({ workshopDirectory, exerciseNumber }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await exerciseContextResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\texerciseNumber,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_diff_between_apps',\n\t\t`\nIntended to give context about the changes between two apps.\n\nThe output is a git diff of the playground directory as BASE (their work in\nprogress) against the solution directory as HEAD (the final state they're trying\nto achieve).\n\nThe output is formatted as a git diff.\n\nApp IDs are formatted as \\`{exerciseNumber}.{stepNumber}.{type}\\`.\n\nIf the user asks for the diff for 2.3, then use 02.03.problem for app1 and 02.03.solution for app2.\n\t\t`,\n\t\tdiffBetweenAppsResource.inputSchema,\n\t\tasync ({ workshopDirectory, app1, app2 }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await diffBetweenAppsResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t\tapp1,\n\t\t\t\tapp2,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_exercise_step_progress_diff',\n\t\t`\nIntended to help a student understand what work they still have to complete.\n\nThis is not a typical diff. It's a diff of the user's work in progress against\nthe solution.\n\n- Lines starting with \\`-\\` show code that needs to be removed from the user's solution\n- Lines starting with \\`+\\` show code that needs to be added to the user's solution\n- If there are differences, the user's work is incomplete\n\nOnly tell the user they have more work to do if the diff output affects the\nrequired behavior, API, or user experience. If the differences are only\nstylistic or organizational, explain that things look different, but they are\nstill valid and ready to be tested.\n\nIf there's a diff with significant changes, you should explain what the changes\nare and their significance. Be brief. Let them tell you whether they need you to\nelaborate.\n\nThe output for this changes over time so it's useful to call multiple times.\n\nFor additional context, you can use the \\`get_exercise_instructions\\` tool\nto get the instructions for the current exercise step to help explain the\nsignificance of changes.\n\t\t`.trim(),\n\t\texerciseStepProgressDiffResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await exerciseStepProgressDiffResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_info',\n\t\t`\nIntended to help you get information about the current user.\n\nThis includes the user's name, email, etc. It's mostly useful to determine\nwhether the user is logged in and know who they are.\n\nIf the user is not logged in, tell them to log in by running the \\`login\\` tool.\n\t\t`.trim(),\n\t\tuserInfoResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userInfoResource.getResource({ workshopDirectory })\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_access',\n\t\t`\nWill tell you whether the user has access to the paid features of the workshop.\n\nPaid features include:\n- Transcripts\n- Progress tracking\n- Access to videos\n- Access to the discord chat\n- Test tab support\n- Diff tab support\n\nEncourage the user to upgrade if they need access to the paid features.\n\t\t`.trim(),\n\t\tuserAccessResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userAccessResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n\n\tserver.tool(\n\t\t'get_user_progress',\n\t\t`\nIntended to help you get the progress of the current user. Can often be helpful\nto know what the next step that needs to be completed is. Make sure to provide\nthe user with the URL of relevant incomplete lessons so they can watch them and\nthen mark them as complete.\n\t\t`.trim(),\n\t\tuserProgressResource.inputSchema,\n\t\tasync ({ workshopDirectory }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst resource = await userProgressResource.getResource({\n\t\t\t\tworkshopDirectory,\n\t\t\t})\n\t\t\treturn {\n\t\t\t\tcontent: [getEmbeddedResourceContent(resource)],\n\t\t\t}\n\t\t},\n\t)\n}\n\n// Sometimes the user will ask the LLM to select a prompt to use so they don't have to.\nexport function initPromptTools(server: McpServer) {\n\tserver.tool(\n\t\t'get_quiz_instructions',\n\t\t`\nIf the user asks you to quiz them on a topic from the workshop, use this tool to\nretrieve the instructions for how to do so.\n\n- If the user asks for a specific exercise, supply that exercise number.\n- If they ask for a specific exericse, supply that exercise number.\n- If they ask for a topic and you don't know which exercise that topic is in, use \\`get_workshop_context\\` to get the list of exercises and their topics and then supply the appropriate exercise number.\n\t\t`.trim(),\n\t\tquizMeInputSchema,\n\t\tasync ({ workshopDirectory, exerciseNumber }) => {\n\t\t\tworkshopDirectory = await handleWorkshopDirectory(workshopDirectory)\n\t\t\tconst result = await quizMe({ workshopDirectory, exerciseNumber })\n\t\t\treturn {\n\t\t\t\t// QUESTION: will a prompt ever return messages that have role: 'assistant'?\n\t\t\t\t// if so, this may be a little confusing for the LLM, but I can't think of a\n\t\t\t\t// good use case for that so 🤷‍♂️\n\t\t\t\tcontent: result.messages.map((m) => {\n\t\t\t\t\tif (m.content.type === 'resource') {\n\t\t\t\t\t\treturn getEmbeddedResourceContent(m.content.resource)\n\t\t\t\t\t}\n\t\t\t\t\treturn m.content\n\t\t\t\t}),\n\t\t\t}\n\t\t},\n\t)\n}\n\nfunction getEmbeddedResourceContent(\n\tresource: ReadResourceResult['contents'][number],\n) {\n\tif (clientSupportsEmbeddedResources) {\n\t\treturn {\n\t\t\ttype: 'resource' as const,\n\t\t\tresource,\n\t\t}\n\t} else if (typeof resource.text === 'string') {\n\t\treturn {\n\t\t\ttype: 'text' as const,\n\t\t\ttext: resource.text,\n\t\t}\n\t} else {\n\t\tthrow new Error(\n\t\t\t`Unknown resource type: ${resource.type} for ${resource.uri}`,\n\t\t)\n\t}\n}\n"]}
@@ -1,4 +1,5 @@
1
- import { type z } from 'zod';
1
+ import { z } from 'zod';
2
+ export declare const workshopDirectoryInputSchema: z.ZodString;
2
3
  export declare function handleWorkshopDirectory(workshopDirectory: string): Promise<string>;
3
4
  export declare function safeReadFile(filePath: string): Promise<string | null>;
4
5
  export type InputSchemaType<T extends {
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,wBAAsB,uBAAuB,CAAC,iBAAiB,EAAE,MAAM,mBAOtE;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,0BAMlD;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,CAAA;CAAE,IAAI;KAClE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7B,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,4BAA4B,aAIvC,CAAA;AAyBF,wBAAsB,uBAAuB,CAAC,iBAAiB,EAAE,MAAM,mBAuBtE;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,0BAMlD;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,CAAA;CAAE,IAAI;KAClE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7B,CAAA"}
package/dist/esm/utils.js CHANGED
@@ -1,10 +1,46 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { init as initApps } from '@epic-web/workshop-utils/apps.server';
4
+ import { z } from 'zod';
5
+ export const workshopDirectoryInputSchema = z
6
+ .string()
7
+ .describe('The workshop directory (the root directory of the workshop repo). This should be an absolute path.');
8
+ async function isWorkshopDirectory(workshopDirectory) {
9
+ console.error('isWorkshopDirectory', workshopDirectory);
10
+ const packageJson = await safeReadFile(path.join(workshopDirectory, 'package.json'));
11
+ if (!packageJson)
12
+ return false;
13
+ let pkgJson;
14
+ try {
15
+ pkgJson = JSON.parse(packageJson);
16
+ }
17
+ catch (error) {
18
+ if (error instanceof SyntaxError) {
19
+ throw new Error(`Syntax error in package.json in "${workshopDirectory}": ${error.message}`);
20
+ }
21
+ throw error;
22
+ }
23
+ console.error('isWorkshopDirectory', Boolean(pkgJson.epicshop));
24
+ return Boolean(pkgJson.epicshop);
25
+ }
4
26
  export async function handleWorkshopDirectory(workshopDirectory) {
5
- if (workshopDirectory.endsWith('playground')) {
27
+ workshopDirectory = workshopDirectory.trim();
28
+ if (!workshopDirectory)
29
+ throw new Error('The workshop directory is required');
30
+ if (!path.isAbsolute(workshopDirectory)) {
31
+ throw new Error('The workshop directory must be an absolute path');
32
+ }
33
+ if (workshopDirectory.endsWith(`${path.sep}playground`)) {
6
34
  workshopDirectory = path.join(workshopDirectory, '..');
7
35
  }
36
+ while (true) {
37
+ if (await isWorkshopDirectory(workshopDirectory))
38
+ break;
39
+ if (workshopDirectory === path.dirname(workshopDirectory)) {
40
+ throw new Error(`No workshop directory found in "${workshopDirectory}"`);
41
+ }
42
+ workshopDirectory = path.dirname(workshopDirectory);
43
+ }
8
44
  await initApps(workshopDirectory);
9
45
  return workshopDirectory;
10
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,sCAAsC,CAAA;AAGvE,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,iBAAyB;IACtE,IAAI,iBAAiB,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9C,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAA;IACvD,CAAC;IAED,MAAM,QAAQ,CAAC,iBAAiB,CAAC,CAAA;IACjC,OAAO,iBAAiB,CAAA;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IAClD,IAAI,CAAC;QACJ,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC","sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { init as initApps } from '@epic-web/workshop-utils/apps.server'\nimport { type z } from 'zod'\n\nexport async function handleWorkshopDirectory(workshopDirectory: string) {\n\tif (workshopDirectory.endsWith('playground')) {\n\t\tworkshopDirectory = path.join(workshopDirectory, '..')\n\t}\n\n\tawait initApps(workshopDirectory)\n\treturn workshopDirectory\n}\n\nexport async function safeReadFile(filePath: string) {\n\ttry {\n\t\treturn await fs.readFile(filePath, 'utf-8')\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport type InputSchemaType<T extends { [K: string]: z.ZodType }> = {\n\t[K in keyof T]: z.infer<T[K]>\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,sCAAsC,CAAA;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC;KAC3C,MAAM,EAAE;KACR,QAAQ,CACR,oGAAoG,CACpG,CAAA;AAEF,KAAK,UAAU,mBAAmB,CAAC,iBAAyB;IAC3D,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAA;IACvD,MAAM,WAAW,GAAG,MAAM,YAAY,CACrC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAC5C,CAAA;IACD,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAA;IAE9B,IAAI,OAAY,CAAA;IAChB,IAAI,CAAC;QACJ,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACd,oCAAoC,iBAAiB,MAAM,KAAK,CAAC,OAAO,EAAE,CAC1E,CAAA;QACF,CAAC;QACD,MAAM,KAAK,CAAA;IACZ,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;IAE/D,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,iBAAyB;IACtE,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAA;IAE5C,IAAI,CAAC,iBAAiB;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IAE7E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACnE,CAAC;IAED,IAAI,iBAAiB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC;QACzD,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAA;IACvD,CAAC;IAED,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,MAAM,mBAAmB,CAAC,iBAAiB,CAAC;YAAE,MAAK;QACvD,IAAI,iBAAiB,KAAK,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,mCAAmC,iBAAiB,GAAG,CAAC,CAAA;QACzE,CAAC;QACD,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACpD,CAAC;IAED,MAAM,QAAQ,CAAC,iBAAiB,CAAC,CAAA;IACjC,OAAO,iBAAiB,CAAA;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IAClD,IAAI,CAAC;QACJ,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC","sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { init as initApps } from '@epic-web/workshop-utils/apps.server'\nimport { z } from 'zod'\n\nexport const workshopDirectoryInputSchema = z\n\t.string()\n\t.describe(\n\t\t'The workshop directory (the root directory of the workshop repo). This should be an absolute path.',\n\t)\n\nasync function isWorkshopDirectory(workshopDirectory: string) {\n\tconsole.error('isWorkshopDirectory', workshopDirectory)\n\tconst packageJson = await safeReadFile(\n\t\tpath.join(workshopDirectory, 'package.json'),\n\t)\n\tif (!packageJson) return false\n\n\tlet pkgJson: any\n\ttry {\n\t\tpkgJson = JSON.parse(packageJson)\n\t} catch (error) {\n\t\tif (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Syntax error in package.json in \"${workshopDirectory}\": ${error.message}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n\tconsole.error('isWorkshopDirectory', Boolean(pkgJson.epicshop))\n\n\treturn Boolean(pkgJson.epicshop)\n}\n\nexport async function handleWorkshopDirectory(workshopDirectory: string) {\n\tworkshopDirectory = workshopDirectory.trim()\n\n\tif (!workshopDirectory) throw new Error('The workshop directory is required')\n\n\tif (!path.isAbsolute(workshopDirectory)) {\n\t\tthrow new Error('The workshop directory must be an absolute path')\n\t}\n\n\tif (workshopDirectory.endsWith(`${path.sep}playground`)) {\n\t\tworkshopDirectory = path.join(workshopDirectory, '..')\n\t}\n\n\twhile (true) {\n\t\tif (await isWorkshopDirectory(workshopDirectory)) break\n\t\tif (workshopDirectory === path.dirname(workshopDirectory)) {\n\t\t\tthrow new Error(`No workshop directory found in \"${workshopDirectory}\"`)\n\t\t}\n\t\tworkshopDirectory = path.dirname(workshopDirectory)\n\t}\n\n\tawait initApps(workshopDirectory)\n\treturn workshopDirectory\n}\n\nexport async function safeReadFile(filePath: string) {\n\ttry {\n\t\treturn await fs.readFile(filePath, 'utf-8')\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport type InputSchemaType<T extends { [K: string]: z.ZodType }> = {\n\t[K in keyof T]: z.infer<T[K]>\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-mcp",
3
- "version": "5.22.3",
3
+ "version": "5.22.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -27,12 +27,14 @@
27
27
  "dev": "tsx src/index.ts",
28
28
  "typecheck": "tsc -b --noEmit",
29
29
  "build": "tshy",
30
+ "inspect": "mcp-inspector",
30
31
  "build:watch": "nx watch --projects=@epic-web/workshop-mcp -- nx run \\$NX_PROJECT_NAME:build"
31
32
  },
32
33
  "dependencies": {
33
34
  "@epic-web/invariant": "^1.0.0",
34
- "@epic-web/workshop-utils": "5.22.3",
35
+ "@epic-web/workshop-utils": "5.22.5",
35
36
  "@modelcontextprotocol/sdk": "^1.9.0",
37
+ "@modelcontextprotocol/inspector": "^0.14.0",
36
38
  "openid-client": "^6.5.0",
37
39
  "zod": "^3.24.2"
38
40
  },