@epic-web/workshop-mcp 5.13.5 → 5.13.6
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.
- package/README.md +1 -1
- package/dist/esm/cli.d.ts +3 -0
- package/dist/esm/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +128 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/esm/cli.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
// Create server instance
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: 'epicshop',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
capabilities: {
|
|
12
|
+
tools: {},
|
|
13
|
+
},
|
|
14
|
+
}, {
|
|
15
|
+
instructions: `
|
|
16
|
+
This is intended to be used within a workshop using the Epic Workshop App
|
|
17
|
+
(@epic-web/workshop-app) to help learners in the process of completing the
|
|
18
|
+
workshop exercises and understanding the learning outcomes.
|
|
19
|
+
|
|
20
|
+
The user's work in progress is in the \`playground\` directory. Any changes they
|
|
21
|
+
ask you to make should be in this directory.
|
|
22
|
+
`.trim(),
|
|
23
|
+
});
|
|
24
|
+
server.tool('get_exercise_step_progress_diff', `
|
|
25
|
+
Intended to help a student understand what work they still have to complete.
|
|
26
|
+
|
|
27
|
+
This returns a git diff of the playground directory as BASE (their work in
|
|
28
|
+
progress) against the solution directory as HEAD (the final state they're trying
|
|
29
|
+
to achieve). Meaning, if there are lines removed, it means they still need to
|
|
30
|
+
add those lines and if they are added, it means they still need to remove them.
|
|
31
|
+
|
|
32
|
+
If there's a diff with significant changes, you should explain what the changes
|
|
33
|
+
are and their significance. Be brief. Let them tell you whether they need you to
|
|
34
|
+
elaborate.
|
|
35
|
+
|
|
36
|
+
For additional context, you can use the \`get_exercise_step_instructions\` tool
|
|
37
|
+
to get the instructions for the current exercise step to help explain the
|
|
38
|
+
significance of changes.
|
|
39
|
+
`.trim(), {
|
|
40
|
+
workshopDirectory: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe('The workshop directory (the root directory of the workshop repo. Best to not bother asking the user and just use the project root path).'),
|
|
43
|
+
}, async ({ workshopDirectory }) => {
|
|
44
|
+
if (workshopDirectory.endsWith('playground')) {
|
|
45
|
+
workshopDirectory = path.join(workshopDirectory, '..');
|
|
46
|
+
}
|
|
47
|
+
process.env.EPICSHOP_CONTEXT_CWD = workshopDirectory;
|
|
48
|
+
const { getApps, isPlaygroundApp, findSolutionDir, getFullPathFromAppName, init, } = await import('@epic-web/workshop-utils/apps.server');
|
|
49
|
+
await init();
|
|
50
|
+
const { getDiffOutputWithRelativePaths } = await import('@epic-web/workshop-utils/diff.server');
|
|
51
|
+
const apps = await getApps();
|
|
52
|
+
const playgroundApp = apps.find(isPlaygroundApp);
|
|
53
|
+
if (!playgroundApp) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: 'No playground app found' }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const baseApp = playgroundApp;
|
|
60
|
+
const solutionDir = await findSolutionDir({
|
|
61
|
+
fullPath: await getFullPathFromAppName(playgroundApp.appName),
|
|
62
|
+
});
|
|
63
|
+
const headApp = apps.find((a) => a.fullPath === solutionDir);
|
|
64
|
+
if (!headApp) {
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: 'text', text: 'No playground solution app found' }],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const diffCode = await getDiffOutputWithRelativePaths(baseApp, headApp);
|
|
71
|
+
if (!diffCode) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: 'text', text: 'No changes' }],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: diffCode,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
server.tool('get_exercise_step_instructions', `
|
|
86
|
+
Intended to help a student understand what they need to do for the current
|
|
87
|
+
exercise step.
|
|
88
|
+
|
|
89
|
+
This returns the instructions MDX content for the current exercise step. It's
|
|
90
|
+
often best when used with the \`get_exercise_step_progress_diff\` tool to help
|
|
91
|
+
a student understand what work they still need to do.
|
|
92
|
+
`.trim(), {
|
|
93
|
+
workshopDirectory: z
|
|
94
|
+
.string()
|
|
95
|
+
.describe('The workshop directory (the root directory of the workshop repo. Best to not bother asking the user and just use the project root path).'),
|
|
96
|
+
}, async ({ workshopDirectory }) => {
|
|
97
|
+
if (workshopDirectory.endsWith('playground')) {
|
|
98
|
+
workshopDirectory = path.join(workshopDirectory, '..');
|
|
99
|
+
}
|
|
100
|
+
process.env.EPICSHOP_CONTEXT_CWD = workshopDirectory;
|
|
101
|
+
const { getApps, isPlaygroundApp } = await import('@epic-web/workshop-utils/apps.server');
|
|
102
|
+
const apps = await getApps();
|
|
103
|
+
const playgroundApp = apps.find(isPlaygroundApp);
|
|
104
|
+
if (!playgroundApp) {
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: 'text', text: 'No playground app found' }],
|
|
107
|
+
isError: true,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: await fs.readFile(path.join(playgroundApp.fullPath, 'README.mdx'), 'utf-8'),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
async function main() {
|
|
120
|
+
const transport = new StdioServerTransport();
|
|
121
|
+
await server.connect(transport);
|
|
122
|
+
console.error('epicshop MCP Server running on stdio');
|
|
123
|
+
}
|
|
124
|
+
main().catch((error) => {
|
|
125
|
+
console.error('Fatal error in main():', error);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\n\n// Create server instance\nconst server = new McpServer(\n\t{\n\t\tname: 'epicshop',\n\t\tversion: '1.0.0',\n\t\tcapabilities: {\n\t\t\ttools: {},\n\t\t},\n\t},\n\t{\n\t\tinstructions: `\nThis is intended to be used within a workshop using the Epic Workshop App\n(@epic-web/workshop-app) to help learners in the process of completing the\nworkshop exercises and understanding the learning outcomes.\n\nThe user's work in progress is in the \\`playground\\` directory. Any changes they\nask you to make should be in this directory.\n\t\t`.trim(),\n\t},\n)\n\nserver.tool(\n\t'get_exercise_step_progress_diff',\n\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\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\nFor additional context, you can use the \\`get_exercise_step_instructions\\` tool\nto get the instructions for the current exercise step to help explain the\nsignificance of changes.\n\t`.trim(),\n\t{\n\t\tworkshopDirectory: z\n\t\t\t.string()\n\t\t\t.describe(\n\t\t\t\t'The workshop directory (the root directory of the workshop repo. Best to not bother asking the user and just use the project root path).',\n\t\t\t),\n\t},\n\tasync ({ workshopDirectory }) => {\n\t\tif (workshopDirectory.endsWith('playground')) {\n\t\t\tworkshopDirectory = path.join(workshopDirectory, '..')\n\t\t}\n\t\tprocess.env.EPICSHOP_CONTEXT_CWD = workshopDirectory\n\n\t\tconst {\n\t\t\tgetApps,\n\t\t\tisPlaygroundApp,\n\t\t\tfindSolutionDir,\n\t\t\tgetFullPathFromAppName,\n\t\t\tinit,\n\t\t} = await import('@epic-web/workshop-utils/apps.server')\n\t\tawait init()\n\n\t\tconst { getDiffOutputWithRelativePaths } = await import(\n\t\t\t'@epic-web/workshop-utils/diff.server'\n\t\t)\n\n\t\tconst apps = await getApps()\n\t\tconst playgroundApp = apps.find(isPlaygroundApp)\n\n\t\tif (!playgroundApp) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'No playground app found' }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\n\t\tconst baseApp = playgroundApp\n\t\tconst solutionDir = await findSolutionDir({\n\t\t\tfullPath: await getFullPathFromAppName(playgroundApp.appName),\n\t\t})\n\t\tconst headApp = apps.find((a) => a.fullPath === solutionDir)\n\n\t\tif (!headApp) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'No playground solution app found' }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\n\t\tconst diffCode = await getDiffOutputWithRelativePaths(baseApp, headApp)\n\n\t\tif (!diffCode) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'No changes' }],\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: diffCode,\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t},\n)\n\nserver.tool(\n\t'get_exercise_step_instructions',\n\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 step. It's\noften best when used with the \\`get_exercise_step_progress_diff\\` tool to help\na student understand what work they still need to do.\n\t`.trim(),\n\t{\n\t\tworkshopDirectory: z\n\t\t\t.string()\n\t\t\t.describe(\n\t\t\t\t'The workshop directory (the root directory of the workshop repo. Best to not bother asking the user and just use the project root path).',\n\t\t\t),\n\t},\n\tasync ({ workshopDirectory }) => {\n\t\tif (workshopDirectory.endsWith('playground')) {\n\t\t\tworkshopDirectory = path.join(workshopDirectory, '..')\n\t\t}\n\t\tprocess.env.EPICSHOP_CONTEXT_CWD = workshopDirectory\n\n\t\tconst { getApps, isPlaygroundApp } = await import(\n\t\t\t'@epic-web/workshop-utils/apps.server'\n\t\t)\n\t\tconst apps = await getApps()\n\t\tconst playgroundApp = apps.find(isPlaygroundApp)\n\t\tif (!playgroundApp) {\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text', text: 'No playground app found' }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: await fs.readFile(\n\t\t\t\t\t\tpath.join(playgroundApp.fullPath, 'README.mdx'),\n\t\t\t\t\t\t'utf-8',\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t},\n)\n\nasync function main() {\n\tconst transport = new StdioServerTransport()\n\tawait server.connect(transport)\n\tconsole.error('epicshop MCP Server running on stdio')\n}\n\nmain().catch((error) => {\n\tconsole.error('Fatal error in main():', error)\n\tprocess.exit(1)\n})\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epic-web/workshop-mcp",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"build:watch": "nx watch --projects=@epic-web/workshop-mcp -- nx run \\$NX_PROJECT_NAME:build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@epic-web/workshop-utils": "5.13.
|
|
32
|
+
"@epic-web/workshop-utils": "5.13.6",
|
|
33
33
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
|
34
34
|
"zod": "^3.24.2"
|
|
35
35
|
},
|