@epic-web/workshop-mcp 6.60.0 → 6.61.0
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/dist/server-metadata.d.ts +54 -0
- package/dist/server-metadata.js +83 -0
- package/dist/tools.js +87 -2
- package/package.json +2 -2
|
@@ -110,6 +110,60 @@ export declare const toolDocs: {
|
|
|
110
110
|
openWorldHint: false;
|
|
111
111
|
};
|
|
112
112
|
};
|
|
113
|
+
list_saved_playgrounds: {
|
|
114
|
+
title: string;
|
|
115
|
+
summary: string;
|
|
116
|
+
inputs: {
|
|
117
|
+
name: string;
|
|
118
|
+
type: string;
|
|
119
|
+
required: true;
|
|
120
|
+
description: string;
|
|
121
|
+
examples: string[];
|
|
122
|
+
}[];
|
|
123
|
+
returns: string;
|
|
124
|
+
examples: {
|
|
125
|
+
description: string;
|
|
126
|
+
params: string;
|
|
127
|
+
}[];
|
|
128
|
+
nextSteps: string[];
|
|
129
|
+
errorNextSteps: string[];
|
|
130
|
+
annotations: {
|
|
131
|
+
readOnlyHint: true;
|
|
132
|
+
destructiveHint: false;
|
|
133
|
+
idempotentHint: true;
|
|
134
|
+
openWorldHint: false;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
set_saved_playground: {
|
|
138
|
+
title: string;
|
|
139
|
+
summary: string;
|
|
140
|
+
inputs: ({
|
|
141
|
+
name: string;
|
|
142
|
+
type: string;
|
|
143
|
+
required: true;
|
|
144
|
+
description: string;
|
|
145
|
+
examples: string[];
|
|
146
|
+
} | {
|
|
147
|
+
name: string;
|
|
148
|
+
type: string;
|
|
149
|
+
required: false;
|
|
150
|
+
description: string;
|
|
151
|
+
examples: string[];
|
|
152
|
+
})[];
|
|
153
|
+
returns: string;
|
|
154
|
+
examples: {
|
|
155
|
+
description: string;
|
|
156
|
+
params: string;
|
|
157
|
+
}[];
|
|
158
|
+
nextSteps: string[];
|
|
159
|
+
errorNextSteps: string[];
|
|
160
|
+
annotations: {
|
|
161
|
+
readOnlyHint: false;
|
|
162
|
+
destructiveHint: false;
|
|
163
|
+
idempotentHint: false;
|
|
164
|
+
openWorldHint: false;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
113
167
|
update_progress: {
|
|
114
168
|
title: string;
|
|
115
169
|
summary: string;
|
package/dist/server-metadata.js
CHANGED
|
@@ -5,6 +5,7 @@ Quick start
|
|
|
5
5
|
- Use \`set_playground\` to move to a step, then \`open_exercise_step_files\` to open relevant files.
|
|
6
6
|
|
|
7
7
|
Default behavior
|
|
8
|
+
- Use \`list_saved_playgrounds\` and \`set_saved_playground\` to restore saved copies when persistence is enabled.
|
|
8
9
|
- \`workshopDirectory\` is required and must be an absolute path to the workshop root.
|
|
9
10
|
- Passing a \`/playground\` path is normalized to the workshop root.
|
|
10
11
|
- The user's work-in-progress lives in the \`playground\` directory.
|
|
@@ -141,6 +142,88 @@ export const toolDocs = {
|
|
|
141
142
|
openWorldHint: false,
|
|
142
143
|
},
|
|
143
144
|
},
|
|
145
|
+
list_saved_playgrounds: {
|
|
146
|
+
title: 'List Saved Playgrounds',
|
|
147
|
+
summary: 'List saved playground copies when playground persistence is enabled.',
|
|
148
|
+
inputs: [
|
|
149
|
+
{
|
|
150
|
+
name: 'workshopDirectory',
|
|
151
|
+
type: 'string',
|
|
152
|
+
required: true,
|
|
153
|
+
description: 'Absolute path to the workshop root.',
|
|
154
|
+
examples: ['/Users/alice/workshops/react-fundamentals'],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
returns: '{ savedPlaygrounds: [{ id, appName, displayName, createdAt, createdAtMs, fullPath }] }',
|
|
158
|
+
examples: [
|
|
159
|
+
{
|
|
160
|
+
description: 'List saved playgrounds',
|
|
161
|
+
params: '{ "workshopDirectory": "/Users/alice/workshops/react" }',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
nextSteps: [
|
|
165
|
+
'Call `set_saved_playground` with a savedPlaygroundId to restore a copy.',
|
|
166
|
+
],
|
|
167
|
+
errorNextSteps: [
|
|
168
|
+
'Enable playground persistence in Preferences and set the playground at least once.',
|
|
169
|
+
],
|
|
170
|
+
annotations: {
|
|
171
|
+
readOnlyHint: true,
|
|
172
|
+
destructiveHint: false,
|
|
173
|
+
idempotentHint: true,
|
|
174
|
+
openWorldHint: false,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
set_saved_playground: {
|
|
178
|
+
title: 'Set Saved Playground',
|
|
179
|
+
summary: 'Restore the playground from a saved copy.',
|
|
180
|
+
inputs: [
|
|
181
|
+
{
|
|
182
|
+
name: 'workshopDirectory',
|
|
183
|
+
type: 'string',
|
|
184
|
+
required: true,
|
|
185
|
+
description: 'Absolute path to the workshop root.',
|
|
186
|
+
examples: ['/Users/alice/workshops/react-fundamentals'],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'savedPlaygroundId',
|
|
190
|
+
type: 'string',
|
|
191
|
+
required: false,
|
|
192
|
+
description: 'Saved playground id (directory name). Omit to restore the most recent saved copy.',
|
|
193
|
+
examples: ['2026.01.18_11.12.00_01.01.problem'],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'latest',
|
|
197
|
+
type: 'boolean',
|
|
198
|
+
required: false,
|
|
199
|
+
description: 'Use the most recent saved playground when true.',
|
|
200
|
+
examples: ['true'],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
returns: '{ savedPlayground: { id, appName, displayName, createdAt, fullPath } }',
|
|
204
|
+
examples: [
|
|
205
|
+
{
|
|
206
|
+
description: 'Restore the most recent saved playground',
|
|
207
|
+
params: '{ "workshopDirectory": "/Users/alice/workshops/react" }',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
description: 'Restore a specific saved playground',
|
|
211
|
+
params: '{ "workshopDirectory": "/Users/alice/workshops/react", "savedPlaygroundId": "2026.01.18_11.12.00_01.01.problem" }',
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
nextSteps: [
|
|
215
|
+
'Open relevant files with `open_exercise_step_files` or `open_file`.',
|
|
216
|
+
],
|
|
217
|
+
errorNextSteps: [
|
|
218
|
+
'Call `list_saved_playgrounds` to get valid savedPlaygroundId values.',
|
|
219
|
+
],
|
|
220
|
+
annotations: {
|
|
221
|
+
readOnlyHint: false,
|
|
222
|
+
destructiveHint: false,
|
|
223
|
+
idempotentHint: false,
|
|
224
|
+
openWorldHint: false,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
144
227
|
update_progress: {
|
|
145
228
|
title: 'Update Progress',
|
|
146
229
|
summary: 'Mark an Epic lesson as complete or incomplete for the current user.',
|
package/dist/tools.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { invariant } from '@epic-web/invariant';
|
|
3
|
-
import { getAppByName, getApps, getExercise, getExerciseApp, getExercises, getPlaygroundApp, getPlaygroundAppName, isExerciseStepApp, isProblemApp, setPlayground, } from '@epic-web/workshop-utils/apps.server';
|
|
3
|
+
import { getAppByName, getAppDisplayName, getApps, getExercise, getExerciseApp, getExercises, getPlaygroundApp, getPlaygroundAppName, getSavedPlaygrounds, isExerciseStepApp, isProblemApp, setPlayground, } from '@epic-web/workshop-utils/apps.server';
|
|
4
4
|
import { deleteCache } from '@epic-web/workshop-utils/cache.server';
|
|
5
5
|
import { getWorkshopConfig } from '@epic-web/workshop-utils/config.server';
|
|
6
|
-
import { getAuthInfo, logout, setAuthInfo, } from '@epic-web/workshop-utils/db.server';
|
|
6
|
+
import { getAuthInfo, getPreferences, logout, setAuthInfo, } from '@epic-web/workshop-utils/db.server';
|
|
7
7
|
import { getDiffFiles } from '@epic-web/workshop-utils/diff.server';
|
|
8
8
|
import { getProgress, getUserInfo, updateProgress, } from '@epic-web/workshop-utils/epic-api.server';
|
|
9
9
|
import { launchEditor } from '@epic-web/workshop-utils/launch-editor.server';
|
|
@@ -62,6 +62,15 @@ function createToolErrorResult(toolName, error) {
|
|
|
62
62
|
});
|
|
63
63
|
return { ...response, isError: true };
|
|
64
64
|
}
|
|
65
|
+
function formatSavedPlaygroundTimestamp(createdAt) {
|
|
66
|
+
const createdAtDate = new Date(createdAt);
|
|
67
|
+
if (Number.isNaN(createdAtDate.getTime()))
|
|
68
|
+
return createdAt;
|
|
69
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
70
|
+
dateStyle: 'medium',
|
|
71
|
+
timeStyle: 'short',
|
|
72
|
+
}).format(createdAtDate);
|
|
73
|
+
}
|
|
65
74
|
function parseResourceText(resource) {
|
|
66
75
|
if (typeof resource.text === 'string') {
|
|
67
76
|
try {
|
|
@@ -308,6 +317,82 @@ export function initTools(server) {
|
|
|
308
317
|
},
|
|
309
318
|
});
|
|
310
319
|
});
|
|
320
|
+
registerTool(server, 'list_saved_playgrounds', {
|
|
321
|
+
workshopDirectory: workshopDirectoryInputSchema,
|
|
322
|
+
}, async ({ workshopDirectory }) => {
|
|
323
|
+
await handleWorkshopDirectory(workshopDirectory);
|
|
324
|
+
const persistEnabled = (await getPreferences())?.playground?.persist ?? false;
|
|
325
|
+
invariant(persistEnabled, 'Playground persistence is disabled. Enable it in Preferences to use saved playgrounds.');
|
|
326
|
+
const [savedPlaygrounds, apps] = await Promise.all([
|
|
327
|
+
getSavedPlaygrounds(),
|
|
328
|
+
getApps(),
|
|
329
|
+
]);
|
|
330
|
+
const savedPlaygroundEntries = savedPlaygrounds.map((entry) => {
|
|
331
|
+
const matchingApp = apps.find((app) => app.name === entry.appName);
|
|
332
|
+
const displayName = matchingApp
|
|
333
|
+
? getAppDisplayName(matchingApp, apps)
|
|
334
|
+
: entry.appName;
|
|
335
|
+
return { ...entry, displayName };
|
|
336
|
+
});
|
|
337
|
+
const details = savedPlaygroundEntries.slice(0, 5).map((entry) => {
|
|
338
|
+
const timestamp = formatSavedPlaygroundTimestamp(entry.createdAt);
|
|
339
|
+
return `${entry.displayName} (${entry.appName}) — ${timestamp} — ${entry.id}`;
|
|
340
|
+
});
|
|
341
|
+
const summary = savedPlaygroundEntries.length
|
|
342
|
+
? `${savedPlaygroundEntries.length} saved playgrounds found.`
|
|
343
|
+
: 'No saved playgrounds found.';
|
|
344
|
+
return createToolResponse({
|
|
345
|
+
toolName: 'list_saved_playgrounds',
|
|
346
|
+
summary,
|
|
347
|
+
details: details.length ? details : undefined,
|
|
348
|
+
structuredContent: {
|
|
349
|
+
savedPlaygrounds: savedPlaygroundEntries,
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
registerTool(server, 'set_saved_playground', {
|
|
354
|
+
workshopDirectory: workshopDirectoryInputSchema,
|
|
355
|
+
savedPlaygroundId: z
|
|
356
|
+
.string()
|
|
357
|
+
.optional()
|
|
358
|
+
.describe('Saved playground id to restore (directory name).'),
|
|
359
|
+
latest: z
|
|
360
|
+
.boolean()
|
|
361
|
+
.optional()
|
|
362
|
+
.default(false)
|
|
363
|
+
.describe('Use the most recent saved playground when true.'),
|
|
364
|
+
}, async ({ workshopDirectory, savedPlaygroundId, latest }) => {
|
|
365
|
+
await handleWorkshopDirectory(workshopDirectory);
|
|
366
|
+
const persistEnabled = (await getPreferences())?.playground?.persist ?? false;
|
|
367
|
+
invariant(persistEnabled, 'Playground persistence is disabled. Enable it in Preferences to use saved playgrounds.');
|
|
368
|
+
const [savedPlaygrounds, apps] = await Promise.all([
|
|
369
|
+
getSavedPlaygrounds(),
|
|
370
|
+
getApps(),
|
|
371
|
+
]);
|
|
372
|
+
invariant(savedPlaygrounds.length, 'No saved playgrounds found.');
|
|
373
|
+
const useLatest = latest || !savedPlaygroundId;
|
|
374
|
+
const selected = savedPlaygroundId
|
|
375
|
+
? savedPlaygrounds.find((entry) => entry.id === savedPlaygroundId)
|
|
376
|
+
: useLatest
|
|
377
|
+
? savedPlaygrounds[0]
|
|
378
|
+
: undefined;
|
|
379
|
+
invariant(selected, `Saved playground not found: ${savedPlaygroundId}`);
|
|
380
|
+
await setPlayground(selected.fullPath);
|
|
381
|
+
const matchingApp = apps.find((app) => app.name === selected.appName);
|
|
382
|
+
const displayName = matchingApp
|
|
383
|
+
? getAppDisplayName(matchingApp, apps)
|
|
384
|
+
: selected.appName;
|
|
385
|
+
return createToolResponse({
|
|
386
|
+
toolName: 'set_saved_playground',
|
|
387
|
+
summary: `Playground set from saved copy: ${displayName}.`,
|
|
388
|
+
structuredContent: {
|
|
389
|
+
savedPlayground: {
|
|
390
|
+
...selected,
|
|
391
|
+
displayName,
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
});
|
|
311
396
|
registerTool(server, 'update_progress', {
|
|
312
397
|
workshopDirectory: workshopDirectoryInputSchema,
|
|
313
398
|
epicLessonSlug: z
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epic-web/workshop-mcp",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.61.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@epic-web/invariant": "^1.0.0",
|
|
38
|
-
"@epic-web/workshop-utils": "6.
|
|
38
|
+
"@epic-web/workshop-utils": "6.61.0",
|
|
39
39
|
"@mcp-ui/server": "^5.13.1",
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.21.1",
|
|
41
41
|
"@sentry/node": "^10.25.0",
|