@z7589xxz758/slidex-mcp-server 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +47 -1
  2. package/dist/server.mjs +567 -53
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,16 @@ MCP server for SlideX MotionDoc decks. It lets MCP clients create decks, read te
4
4
 
5
5
  Current authoring syntax uses `<Slide>` with `Text`, `Icon`, `Chart`, `ImageBlock`, and `VideoBlock`. New MCP block insertion only creates the current authoring blocks.
6
6
 
7
+ The package also includes built-in slide layout presets matching the SlideX Studio layout picker, such as Title & Photo, Title & Bullets, Agenda, Key Fact, Quote, Photos - 3 on a page, and Photo. Once installed, MCP clients can list layouts, fetch layout source, create a slide from a layout, append a layout slide, or replace an existing slide with a layout.
8
+
9
+ ## Quick Start
10
+
11
+ Run the server directly without installing:
12
+
13
+ ```bash
14
+ npx -y @z7589xxz758/slidex-mcp-server
15
+ ```
16
+
7
17
  ## Use With MCP Clients
8
18
 
9
19
  ```json
@@ -11,7 +21,7 @@ Current authoring syntax uses `<Slide>` with `Text`, `Icon`, `Chart`, `ImageBloc
11
21
  "mcpServers": {
12
22
  "slidex": {
13
23
  "command": "npx",
14
- "args": ["-y", "slidex-mcp-server"]
24
+ "args": ["-y", "@z7589xxz758/slidex-mcp-server"]
15
25
  }
16
26
  }
17
27
  }
@@ -51,9 +61,45 @@ When connecting through stdio, prefer the silent npm form:
51
61
  - `slidex_update_slide_props`
52
62
  - `slidex_replace_slide`
53
63
  - `slidex_add_block`
64
+ - `slidex_list_slide_layouts`
65
+ - `slidex_get_slide_layout`
66
+ - `slidex_create_slide_from_layout`
67
+ - `slidex_add_slide_from_layout`
68
+ - `slidex_replace_slide_with_layout`
54
69
  - `slidex_delete_slide`
55
70
  - `slidex_reorder_slide`
56
71
  - `slidex_export_html`
57
72
  - `slidex_list_block_types`
58
73
 
59
74
  `slidex_add_block` currently accepts `Text`, `Image`, `Video`, `ChartBar`, `ChartLine`, `ChartArea`, `ChartPie`, `ChartDonut`, and `Icon`.
75
+
76
+ ## Built-in Slide Layouts
77
+
78
+ `slidex_list_slide_layouts` currently returns 16 installable layout presets:
79
+
80
+ - `title`
81
+ - `title-photo`
82
+ - `title-alt-photo`
83
+ - `title-bullets`
84
+ - `bullets`
85
+ - `title-bullets-photo`
86
+ - `title-bullets-small-video`
87
+ - `title-bullets-large-video`
88
+ - `chapter`
89
+ - `only-title`
90
+ - `agenda`
91
+ - `statement`
92
+ - `key-fact`
93
+ - `quote`
94
+ - `photos-3`
95
+ - `photo`
96
+
97
+ Layout resources are also available at `slidex://slide-layouts` and `slidex://slide-layouts/{layoutId}`.
98
+
99
+ ## Dynamic Workspace Skills
100
+
101
+ When the server starts, it dynamically scans the current working directory (`process.cwd()`) for a `.agents/skills` folder. If found, it reads the `SKILL.md` files and automatically registers them as:
102
+ - **MCP Prompts**: `skill_<folder_name>` (e.g., `skill_high_end_visual_design`)
103
+ - **MCP Resources**: `workspace://skills/<folder_name>`
104
+
105
+ This allows the AI to adapt to the specific design guidelines, tones, and automated workflows of the workspace it is running in!
package/dist/server.mjs CHANGED
@@ -437,8 +437,8 @@ var init_parseUtil = __esm({
437
437
  init_errors();
438
438
  init_en();
439
439
  makeIssue = (params) => {
440
- const { data, path, errorMaps, issueData } = params;
441
- const fullPath = [...path, ...issueData.path || []];
440
+ const { data, path: path2, errorMaps, issueData } = params;
441
+ const fullPath = [...path2, ...issueData.path || []];
442
442
  const fullIssue = {
443
443
  ...issueData,
444
444
  path: fullPath
@@ -718,11 +718,11 @@ var init_types = __esm({
718
718
  init_parseUtil();
719
719
  init_util();
720
720
  ParseInputLazyPath = class {
721
- constructor(parent, value, path, key) {
721
+ constructor(parent, value, path2, key) {
722
722
  this._cachedPath = [];
723
723
  this.parent = parent;
724
724
  this.data = value;
725
- this._path = path;
725
+ this._path = path2;
726
726
  this._key = key;
727
727
  }
728
728
  get path() {
@@ -4221,10 +4221,10 @@ function mergeDefs(...defs) {
4221
4221
  function cloneDef(schema) {
4222
4222
  return mergeDefs(schema._zod.def);
4223
4223
  }
4224
- function getElementAtPath(obj, path) {
4225
- if (!path)
4224
+ function getElementAtPath(obj, path2) {
4225
+ if (!path2)
4226
4226
  return obj;
4227
- return path.reduce((acc, key) => acc?.[key], obj);
4227
+ return path2.reduce((acc, key) => acc?.[key], obj);
4228
4228
  }
4229
4229
  function promiseAllObject(promisesObj) {
4230
4230
  const keys = Object.keys(promisesObj);
@@ -4552,11 +4552,11 @@ function explicitlyAborted(x, startIndex = 0) {
4552
4552
  }
4553
4553
  return false;
4554
4554
  }
4555
- function prefixIssues(path, issues) {
4555
+ function prefixIssues(path2, issues) {
4556
4556
  return issues.map((iss) => {
4557
4557
  var _a3;
4558
4558
  (_a3 = iss).path ?? (_a3.path = []);
4559
- iss.path.unshift(path);
4559
+ iss.path.unshift(path2);
4560
4560
  return iss;
4561
4561
  });
4562
4562
  }
@@ -4773,16 +4773,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
4773
4773
  }
4774
4774
  function formatError(error51, mapper = (issue2) => issue2.message) {
4775
4775
  const fieldErrors = { _errors: [] };
4776
- const processError = (error52, path = []) => {
4776
+ const processError = (error52, path2 = []) => {
4777
4777
  for (const issue2 of error52.issues) {
4778
4778
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4779
- issue2.errors.map((issues) => processError({ issues }, [...path, ...issue2.path]));
4779
+ issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
4780
4780
  } else if (issue2.code === "invalid_key") {
4781
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
4781
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
4782
4782
  } else if (issue2.code === "invalid_element") {
4783
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
4783
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
4784
4784
  } else {
4785
- const fullpath = [...path, ...issue2.path];
4785
+ const fullpath = [...path2, ...issue2.path];
4786
4786
  if (fullpath.length === 0) {
4787
4787
  fieldErrors._errors.push(mapper(issue2));
4788
4788
  } else {
@@ -4809,17 +4809,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
4809
4809
  }
4810
4810
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
4811
4811
  const result = { errors: [] };
4812
- const processError = (error52, path = []) => {
4812
+ const processError = (error52, path2 = []) => {
4813
4813
  var _a3, _b;
4814
4814
  for (const issue2 of error52.issues) {
4815
4815
  if (issue2.code === "invalid_union" && issue2.errors.length) {
4816
- issue2.errors.map((issues) => processError({ issues }, [...path, ...issue2.path]));
4816
+ issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
4817
4817
  } else if (issue2.code === "invalid_key") {
4818
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
4818
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
4819
4819
  } else if (issue2.code === "invalid_element") {
4820
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
4820
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
4821
4821
  } else {
4822
- const fullpath = [...path, ...issue2.path];
4822
+ const fullpath = [...path2, ...issue2.path];
4823
4823
  if (fullpath.length === 0) {
4824
4824
  result.errors.push(mapper(issue2));
4825
4825
  continue;
@@ -4851,8 +4851,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
4851
4851
  }
4852
4852
  function toDotPath(_path) {
4853
4853
  const segs = [];
4854
- const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4855
- for (const seg of path) {
4854
+ const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4855
+ for (const seg of path2) {
4856
4856
  if (typeof seg === "number")
4857
4857
  segs.push(`[${seg}]`);
4858
4858
  else if (typeof seg === "symbol")
@@ -18532,13 +18532,13 @@ function resolveRef(ref, ctx) {
18532
18532
  if (!ref.startsWith("#")) {
18533
18533
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
18534
18534
  }
18535
- const path = ref.slice(1).split("/").filter(Boolean);
18536
- if (path.length === 0) {
18535
+ const path2 = ref.slice(1).split("/").filter(Boolean);
18536
+ if (path2.length === 0) {
18537
18537
  return ctx.rootSchema;
18538
18538
  }
18539
18539
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
18540
- if (path[0] === defsKey) {
18541
- const key = path[1];
18540
+ if (path2[0] === defsKey) {
18541
+ const key = path2[1];
18542
18542
  if (!key || !ctx.defs[key]) {
18543
18543
  throw new Error(`Reference not found: ${ref}`);
18544
18544
  }
@@ -26638,8 +26638,8 @@ var require_utils = __commonJS({
26638
26638
  }
26639
26639
  return ind;
26640
26640
  }
26641
- function removeDotSegments(path) {
26642
- let input = path;
26641
+ function removeDotSegments(path2) {
26642
+ let input = path2;
26643
26643
  const output = [];
26644
26644
  let nextSlash = -1;
26645
26645
  let len = 0;
@@ -26891,8 +26891,8 @@ var require_schemes = __commonJS({
26891
26891
  wsComponent.secure = void 0;
26892
26892
  }
26893
26893
  if (wsComponent.resourceName) {
26894
- const [path, query] = wsComponent.resourceName.split("?");
26895
- wsComponent.path = path && path !== "/" ? path : void 0;
26894
+ const [path2, query] = wsComponent.resourceName.split("?");
26895
+ wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
26896
26896
  wsComponent.query = query;
26897
26897
  wsComponent.resourceName = void 0;
26898
26898
  }
@@ -35980,12 +35980,12 @@ var require_dist = __commonJS({
35980
35980
  throw new Error(`Unknown format "${name}"`);
35981
35981
  return f;
35982
35982
  };
35983
- function addFormats(ajv, list, fs, exportName) {
35983
+ function addFormats(ajv, list, fs2, exportName) {
35984
35984
  var _a3;
35985
35985
  var _b;
35986
35986
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
35987
35987
  for (const f of list)
35988
- ajv.addFormat(f, fs[f]);
35988
+ ajv.addFormat(f, fs2[f]);
35989
35989
  }
35990
35990
  module.exports = exports = formatsPlugin;
35991
35991
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -40852,8 +40852,8 @@ function renderLucideIcon(name, strokeWidth = 2) {
40852
40852
  if (!isSlideXIconName(name)) {
40853
40853
  return "";
40854
40854
  }
40855
- const children = lucideIconPaths[name].map((path) => {
40856
- const [shape, ...parts] = path.split(" ");
40855
+ const children = lucideIconPaths[name].map((path2) => {
40856
+ const [shape, ...parts] = path2.split(" ");
40857
40857
  if (shape === "circle") {
40858
40858
  return `<circle cx="${parts[0]}" cy="${parts[1]}" r="${parts[2]}" />`;
40859
40859
  }
@@ -40863,7 +40863,7 @@ function renderLucideIcon(name, strokeWidth = 2) {
40863
40863
  if (shape === "rect") {
40864
40864
  return `<rect x="${parts[0]}" y="${parts[1]}" width="${parts[2]}" height="${parts[3]}" rx="${parts[4]}" ry="${parts[5]}" />`;
40865
40865
  }
40866
- return `<path d="${escapeAttribute(shape === "path" ? parts.join(" ") : path)}" />`;
40866
+ return `<path d="${escapeAttribute(shape === "path" ? parts.join(" ") : path2)}" />`;
40867
40867
  }).join("");
40868
40868
  return `<svg aria-hidden="true" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="${strokeWidth}" viewBox="0 0 24 24">${children}</svg>`;
40869
40869
  }
@@ -42031,7 +42031,446 @@ var init_templates = __esm({
42031
42031
  }
42032
42032
  });
42033
42033
 
42034
+ // core/motion-doc/presets/templates/slideLayouts.ts
42035
+ var slideLayouts;
42036
+ var init_slideLayouts = __esm({
42037
+ "core/motion-doc/presets/templates/slideLayouts.ts"() {
42038
+ slideLayouts = [
42039
+ {
42040
+ id: "title",
42041
+ name: "Title, Body & Author",
42042
+ source: `<Text fontSize={80} fontWeight={800} enter="fadeUp" x={8} y={25} w={84} h={15}>Presentation Title</Text>
42043
+ <Text fontSize={24} lineHeight={1.6} fontWeight={400} enter="fadeUp" delay={0.1} x={8} y={43} w={65} h={15}>Write a short paragraph to introduce the main topic or provide a brief overview of your presentation.</Text>
42044
+ <Text fontSize={18} fontWeight={600} enter="fadeUp" delay={0.2} x={8} y={82} w={40} h={10}>Author Name</Text>`
42045
+ },
42046
+ {
42047
+ id: "title-photo",
42048
+ name: "Title & Photo",
42049
+ source: `<Text fontSize={72} fontWeight={800} enter="slideRight" x={8} y={40} w={40} h={20}>Title</Text>
42050
+ <ImageBlock src="https://images.unsplash.com/photo-1551434678-e076c223a692?auto=format&fit=crop&q=80&w=1200" alt="Photo" x={50} y={10} w={42} h={80} fit="cover" radius={24} enter="fadeIn" />`
42051
+ },
42052
+ {
42053
+ id: "title-alt-photo",
42054
+ name: "Title & Alternate Photo",
42055
+ source: `<ImageBlock src="https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&q=80&w=1200" alt="Alternate Photo" x={8} y={10} w={42} h={80} fit="cover" radius={24} enter="fadeIn" />
42056
+ <Text fontSize={72} fontWeight={800} enter="slideLeft" x={54} y={40} w={40} h={20}>Title</Text>`
42057
+ },
42058
+ {
42059
+ id: "title-bullets",
42060
+ name: "Title & Bullets",
42061
+ source: `<Text fontSize={54} fontWeight={700} enter="slideLeft" x={8} y={10} w={84} h={15}>Title</Text>
42062
+ <Text listType="bullet" fontSize={24} lineHeight={1.8} enter="fadeUp" x={8} y={30} w={84} h={60}>\u2022 First point
42063
+ \u2022 Second point
42064
+ \u2022 Third point</Text>`
42065
+ },
42066
+ {
42067
+ id: "bullets",
42068
+ name: "Bullets",
42069
+ source: `<Text listType="bullet" fontSize={28} lineHeight={1.8} enter="fadeUp" x={10} y={20} w={80} h={60}>\u2022 First point
42070
+ \u2022 Second point
42071
+ \u2022 Third point</Text>`
42072
+ },
42073
+ {
42074
+ id: "title-bullets-photo",
42075
+ name: "Title, Bullets & Photo",
42076
+ source: `<Text fontSize={54} fontWeight={700} enter="slideLeft" x={8} y={10} w={84} h={15}>Title</Text>
42077
+ <Text listType="bullet" fontSize={20} lineHeight={1.6} enter="fadeUp" x={8} y={30} w={40} h={60}>\u2022 First point
42078
+ \u2022 Second point</Text>
42079
+ <ImageBlock src="https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&q=80&w=1200" alt="Photo" x={52} y={30} w={40} h={60} fit="cover" radius={24} enter="fadeIn" />`
42080
+ },
42081
+ {
42082
+ id: "title-bullets-small-video",
42083
+ name: "Title, Bullets & Small Live Video",
42084
+ source: `<Text fontSize={54} fontWeight={700} enter="slideLeft" x={8} y={10} w={84} h={15}>Title</Text>
42085
+ <Text listType="bullet" fontSize={20} lineHeight={1.6} enter="fadeUp" x={8} y={30} w={40} h={60}>\u2022 First point
42086
+ \u2022 Second point</Text>
42087
+ <VideoBlock src="https://www.w3schools.com/html/mov_bbb.mp4" controls="true" x={52} y={30} w={40} h={40} fit="cover" radius={24} enter="fadeIn" />`
42088
+ },
42089
+ {
42090
+ id: "title-bullets-large-video",
42091
+ name: "Title, Bullets & Large Live Video",
42092
+ source: `<Text fontSize={54} fontWeight={700} enter="slideLeft" x={8} y={10} w={84} h={15}>Title</Text>
42093
+ <Text listType="bullet" fontSize={20} lineHeight={1.6} enter="fadeUp" x={8} y={30} w={30} h={60}>\u2022 First point
42094
+ \u2022 Second point</Text>
42095
+ <VideoBlock src="https://www.w3schools.com/html/mov_bbb.mp4" controls="true" x={42} y={25} w={50} h={65} fit="cover" radius={24} enter="fadeIn" />`
42096
+ },
42097
+ {
42098
+ id: "chapter",
42099
+ name: "Chapter",
42100
+ source: `<Text fontSize={96} fontWeight={800} enter="zoomIn" x={10} y={40} w={80} h={20} textAlign="center" textVerticalAlign="middle">Chapter</Text>`
42101
+ },
42102
+ {
42103
+ id: "only-title",
42104
+ name: "Only Title",
42105
+ source: `<Text fontSize={64} fontWeight={800} enter="slideLeft" x={8} y={40} w={84} h={20}>Title</Text>`
42106
+ },
42107
+ {
42108
+ id: "agenda",
42109
+ name: "Agenda",
42110
+ source: `<Text fontSize={54} fontWeight={700} enter="slideLeft" x={8} y={10} w={84} h={15}>Agenda</Text>
42111
+ <Text listType="bullet" fontSize={24} lineHeight={2} enter="fadeUp" x={8} y={30} w={40} h={60}>\u2022 09:00 - Keynote
42112
+ \u2022 10:30 - Strategy
42113
+ \u2022 13:00 - Workshop</Text>`
42114
+ },
42115
+ {
42116
+ id: "statement",
42117
+ name: "Statement",
42118
+ source: `<Text fontSize={48} fontWeight={700} lineHeight={1.4} enter="fadeUp" x={15} y={35} w={70} h={30} textAlign="center">A clear, bold statement that captures attention and delivers the core message.</Text>`
42119
+ },
42120
+ {
42121
+ id: "key-fact",
42122
+ name: "Key Fact",
42123
+ source: `<Text fontSize={120} fontWeight={900} enter="zoomIn" x={10} y={30} w={80} h={25} textAlign="center">100%</Text>
42124
+ <Text fontSize={24} fontWeight={500} enter="fadeUp" delay={0.2} x={10} y={60} w={80} h={10} textAlign="center">Key Fact</Text>`
42125
+ },
42126
+ {
42127
+ id: "quote",
42128
+ name: "Quote",
42129
+ source: `<Text fontSize={60} fontWeight={600} lineHeight={1.3} enter="fadeUp" x={15} y={30} w={70} h={30}>"A profound quote goes here."</Text>
42130
+ <Text fontSize={20} fontWeight={400} enter="fadeUp" delay={0.2} x={15} y={65} w={70} h={10}>\u2014 Author</Text>`
42131
+ },
42132
+ {
42133
+ id: "photos-3",
42134
+ name: "Photos - 3 on a page",
42135
+ source: `<ImageBlock src="https://images.unsplash.com/photo-1551434678-e076c223a692?auto=format&fit=crop&q=80&w=800" alt="Photo 1" x={8} y={10} w={40} h={80} fit="cover" radius={24} enter="fadeIn" />
42136
+ <ImageBlock src="https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&q=80&w=800" alt="Photo 2" x={52} y={10} w={40} h={38} fit="cover" radius={24} enter="fadeIn" delay={0.1} />
42137
+ <ImageBlock src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=800" alt="Photo 3" x={52} y={52} w={40} h={38} fit="cover" radius={24} enter="fadeIn" delay={0.2} />`
42138
+ },
42139
+ {
42140
+ id: "photo",
42141
+ name: "Photo",
42142
+ source: `<ImageBlock src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=1200" alt="Full Photo" x={0} y={0} w={100} h={100} fit="cover" radius={0} enter="fadeIn" />`
42143
+ },
42144
+ {
42145
+ id: "blank",
42146
+ name: "Blank",
42147
+ source: ``
42148
+ }
42149
+ ];
42150
+ }
42151
+ });
42152
+
42153
+ // mcp/slideLayoutMcp.ts
42154
+ function registerSlideLayoutMcp(server) {
42155
+ registerSlideLayoutResources(server);
42156
+ registerSlideLayoutTools(server);
42157
+ }
42158
+ function registerSlideLayoutResources(server) {
42159
+ server.registerResource(
42160
+ "slidex-slide-layout-index",
42161
+ "slidex://slide-layouts",
42162
+ {
42163
+ description: "Built-in SlideX slide layout presets.",
42164
+ mimeType: "application/json",
42165
+ title: "SlideX Slide Layouts"
42166
+ },
42167
+ (uri) => ({
42168
+ contents: [
42169
+ {
42170
+ mimeType: "application/json",
42171
+ text: toJson(listSlideLayoutSummaries(false)),
42172
+ uri: uri.href
42173
+ }
42174
+ ]
42175
+ })
42176
+ );
42177
+ server.registerResource(
42178
+ "slidex-slide-layout-source",
42179
+ new ResourceTemplate("slidex://slide-layouts/{layoutId}", {
42180
+ complete: {
42181
+ layoutId: (value) => publicSlideLayouts.map((layout) => layout.id).filter((layoutId) => layoutId.startsWith(value))
42182
+ },
42183
+ list: () => ({
42184
+ resources: publicSlideLayouts.map((layout) => ({
42185
+ description: `Slide layout preset: ${layout.name}.`,
42186
+ mimeType: "text/markdown",
42187
+ name: layout.id,
42188
+ title: layout.name,
42189
+ uri: `slidex://slide-layouts/${layout.id}`
42190
+ }))
42191
+ })
42192
+ }),
42193
+ {
42194
+ description: "Built-in SlideX slide layout source by id.",
42195
+ mimeType: "text/markdown",
42196
+ title: "SlideX Slide Layout Source"
42197
+ },
42198
+ (uri, variables) => {
42199
+ const layoutId = Array.isArray(variables.layoutId) ? variables.layoutId[0] : variables.layoutId;
42200
+ const layout = getSlideLayoutOrThrow(layoutId);
42201
+ return {
42202
+ contents: [
42203
+ {
42204
+ mimeType: "text/markdown",
42205
+ text: layout.source,
42206
+ uri: uri.href
42207
+ }
42208
+ ]
42209
+ };
42210
+ }
42211
+ );
42212
+ }
42213
+ function registerSlideLayoutTools(server) {
42214
+ server.registerTool(
42215
+ "slidex_list_slide_layouts",
42216
+ {
42217
+ title: "List Slide Layouts",
42218
+ description: "List the built-in slide layout presets available to create or insert slides.",
42219
+ inputSchema: {
42220
+ includeSource: external_exports.boolean().default(false)
42221
+ }
42222
+ },
42223
+ ({ includeSource }) => jsonResult(listSlideLayoutSummaries(includeSource))
42224
+ );
42225
+ server.registerTool(
42226
+ "slidex_get_slide_layout",
42227
+ {
42228
+ title: "Get Slide Layout",
42229
+ description: "Return one built-in slide layout preset and its MDX block source.",
42230
+ inputSchema: {
42231
+ includeSource: external_exports.boolean().default(true),
42232
+ layoutId: external_exports.string().describe("Layout id from slidex_list_slide_layouts.")
42233
+ }
42234
+ },
42235
+ ({ includeSource, layoutId }) => runTool(() => formatSlideLayout(getSlideLayoutOrThrow(layoutId), includeSource))
42236
+ );
42237
+ server.registerTool(
42238
+ "slidex_create_slide_from_layout",
42239
+ {
42240
+ title: "Create Slide From Layout",
42241
+ description: "Create a complete <Slide>...</Slide> source block from a built-in layout.",
42242
+ inputSchema: {
42243
+ layoutId: external_exports.string().describe("Layout id from slidex_list_slide_layouts."),
42244
+ ...layoutSlideOptionsSchema
42245
+ }
42246
+ },
42247
+ ({ layoutId, ...options }) => runTool(() => {
42248
+ const layout = getSlideLayoutOrThrow(layoutId);
42249
+ const slideSource = createSlideSourceFromLayout(layout, void 0, options);
42250
+ return {
42251
+ layout: formatSlideLayout(layout, false),
42252
+ slideSource,
42253
+ summary: summarizeMotionDoc(`# Layout Preview
42254
+
42255
+ ${slideSource}`)
42256
+ };
42257
+ })
42258
+ );
42259
+ server.registerTool(
42260
+ "slidex_add_slide_from_layout",
42261
+ {
42262
+ title: "Add Slide From Layout",
42263
+ description: "Append or insert a new slide generated from a built-in layout.",
42264
+ inputSchema: {
42265
+ afterSlideIndex: external_exports.number().int().min(0).optional().describe("Insert after this slide index. Omit to append at the end."),
42266
+ layoutId: external_exports.string().describe("Layout id from slidex_list_slide_layouts."),
42267
+ source: external_exports.string().describe("Existing SlideX MotionDoc MDX source."),
42268
+ ...layoutSlideOptionsSchema
42269
+ }
42270
+ },
42271
+ ({ afterSlideIndex, layoutId, source, ...options }) => runTool(() => {
42272
+ const layout = getSlideLayoutOrThrow(layoutId);
42273
+ const nextSource = addSlideFromLayout(source, layout, afterSlideIndex, options);
42274
+ return {
42275
+ layout: formatSlideLayout(layout, false),
42276
+ source: nextSource,
42277
+ summary: summarizeMotionDoc(nextSource)
42278
+ };
42279
+ })
42280
+ );
42281
+ server.registerTool(
42282
+ "slidex_replace_slide_with_layout",
42283
+ {
42284
+ title: "Replace Slide With Layout",
42285
+ description: "Replace one slide with a new slide generated from a built-in layout.",
42286
+ inputSchema: {
42287
+ layoutId: external_exports.string().describe("Layout id from slidex_list_slide_layouts."),
42288
+ slideIndex: external_exports.number().int().min(0),
42289
+ source: external_exports.string().describe("Existing SlideX MotionDoc MDX source."),
42290
+ ...layoutSlideOptionsSchema
42291
+ }
42292
+ },
42293
+ ({ layoutId, slideIndex, source, ...options }) => runTool(() => {
42294
+ const layout = getSlideLayoutOrThrow(layoutId);
42295
+ const document = parseMotionDoc(source);
42296
+ const referenceSlide = getSlideOrThrow2(document.scenes, slideIndex);
42297
+ const slideSource = createSlideSourceFromLayout(layout, referenceSlide, options, {
42298
+ preserveReferenceDuration: true
42299
+ });
42300
+ const nextSource = replaceSlideContent(source, slideIndex, slideSource);
42301
+ return {
42302
+ layout: formatSlideLayout(layout, false),
42303
+ source: nextSource,
42304
+ summary: summarizeMotionDoc(nextSource)
42305
+ };
42306
+ })
42307
+ );
42308
+ }
42309
+ function listSlideLayoutSummaries(includeSource) {
42310
+ return {
42311
+ count: publicSlideLayouts.length,
42312
+ layouts: publicSlideLayouts.map((layout) => formatSlideLayout(layout, includeSource))
42313
+ };
42314
+ }
42315
+ function formatSlideLayout(layout, includeSource) {
42316
+ return {
42317
+ id: layout.id,
42318
+ name: layout.name,
42319
+ source: includeSource ? layout.source : void 0
42320
+ };
42321
+ }
42322
+ function getSlideLayoutOrThrow(layoutId) {
42323
+ const layout = publicSlideLayouts.find((item) => item.id === layoutId);
42324
+ if (!layout) {
42325
+ throw new Error(
42326
+ `Unknown layoutId: ${layoutId ?? "(missing)"}. Available layouts: ${publicSlideLayouts.map((item) => item.id).join(", ")}.`
42327
+ );
42328
+ }
42329
+ return layout;
42330
+ }
42331
+ function addSlideFromLayout(source, layout, afterSlideIndex, options) {
42332
+ const document = parseMotionDoc(source);
42333
+ const referenceIndex = afterSlideIndex ?? document.scenes.length - 1;
42334
+ const referenceSlide = document.scenes[referenceIndex];
42335
+ const slideSource = createSlideSourceFromLayout(layout, referenceSlide, options);
42336
+ const normalizedSource = source.trim() || "# Untitled Deck";
42337
+ if (afterSlideIndex === void 0) {
42338
+ return `${normalizedSource.trimEnd()}
42339
+
42340
+ ${slideSource}`;
42341
+ }
42342
+ if (!referenceSlide) {
42343
+ throw new Error(`afterSlideIndex ${afterSlideIndex} is outside the slide range.`);
42344
+ }
42345
+ const pattern = /<(Slide|Scene)\b[^>]*>[\s\S]*?<\/\1>/g;
42346
+ let currentIndex = 0;
42347
+ for (const match of normalizedSource.matchAll(pattern)) {
42348
+ if (currentIndex === afterSlideIndex && match.index !== void 0) {
42349
+ const insertAt = match.index + match[0].length;
42350
+ return `${normalizedSource.slice(0, insertAt)}
42351
+
42352
+ ${slideSource}${normalizedSource.slice(insertAt)}`;
42353
+ }
42354
+ currentIndex += 1;
42355
+ }
42356
+ throw new Error(`afterSlideIndex ${afterSlideIndex} is outside the slide range.`);
42357
+ }
42358
+ function createSlideSourceFromLayout(layout, referenceSlide, options, behavior = {}) {
42359
+ const slideProps = buildSlideProps(referenceSlide, options, behavior);
42360
+ const layoutSource = applyLayoutReplacements(layout.source, options.replacements);
42361
+ return `${formatSlideTag2(slideProps)}
42362
+ ${indentLayoutSource(layoutSource)}
42363
+ </Slide>`;
42364
+ }
42365
+ function buildSlideProps(referenceSlide, options, behavior) {
42366
+ const referenceProps = referenceSlide?.props ?? {};
42367
+ const duration3 = options.duration ?? (behavior.preserveReferenceDuration ? numberProp3(referenceProps.duration) ?? referenceSlide?.duration : void 0) ?? 5;
42368
+ const theme = options.theme ?? stringProp3(referenceProps.theme) ?? "dark";
42369
+ const background = options.background ?? stringProp3(referenceProps.background) ?? (theme === "light" ? "#f7f7f2" : "#050505");
42370
+ const accent = options.accent ?? stringProp3(referenceProps.accent) ?? (theme === "light" ? "#111111" : "#ffffff");
42371
+ const textColor = options.textColor ?? stringProp3(referenceProps.textColor);
42372
+ return {
42373
+ accent,
42374
+ background,
42375
+ duration: duration3,
42376
+ ...textColor ? { textColor } : {},
42377
+ theme
42378
+ };
42379
+ }
42380
+ function getSlideOrThrow2(slides, slideIndex) {
42381
+ const slide2 = slides[slideIndex];
42382
+ if (!slide2) {
42383
+ throw new Error(`slideIndex ${slideIndex} is outside the slide range.`);
42384
+ }
42385
+ return slide2;
42386
+ }
42387
+ function applyLayoutReplacements(source, replacements) {
42388
+ if (!replacements) return source;
42389
+ return Object.entries(replacements).reduce((currentSource, [from, to]) => {
42390
+ if (!from) return currentSource;
42391
+ return currentSource.split(from).join(safeMdxText2(to));
42392
+ }, source);
42393
+ }
42394
+ function indentLayoutSource(source) {
42395
+ if (!source.trim()) return "";
42396
+ return source.split("\n").map((line) => line.trim() ? ` ${line}` : line).join("\n");
42397
+ }
42398
+ function formatSlideTag2(props) {
42399
+ const duration3 = numberProp3(props.duration) ?? 5;
42400
+ const entries = Object.entries(props).filter(([key]) => key !== "duration");
42401
+ const attrs = entries.map(
42402
+ ([key, value]) => typeof value === "number" ? `${key}={${value}}` : `${key}="${safeMdxAttribute2(value)}"`
42403
+ ).join(" ");
42404
+ return `<Slide duration={${duration3}}${attrs ? ` ${attrs}` : ""}>`;
42405
+ }
42406
+ function stringProp3(value) {
42407
+ return typeof value === "string" && value ? value : void 0;
42408
+ }
42409
+ function numberProp3(value) {
42410
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
42411
+ }
42412
+ function safeMdxText2(value) {
42413
+ return value.replace(/</g, "&lt;").replace(/>/g, "&gt;");
42414
+ }
42415
+ function safeMdxAttribute2(value) {
42416
+ return safeMdxText2(value).replace(/"/g, "&quot;");
42417
+ }
42418
+ function runTool(callback) {
42419
+ try {
42420
+ return jsonResult(callback());
42421
+ } catch (error51) {
42422
+ return {
42423
+ content: [
42424
+ {
42425
+ type: "text",
42426
+ text: error51 instanceof Error ? error51.message : String(error51)
42427
+ }
42428
+ ],
42429
+ isError: true
42430
+ };
42431
+ }
42432
+ }
42433
+ function jsonResult(data) {
42434
+ return {
42435
+ content: [
42436
+ {
42437
+ type: "text",
42438
+ text: toJson(data)
42439
+ }
42440
+ ],
42441
+ structuredContent: {
42442
+ result: data
42443
+ }
42444
+ };
42445
+ }
42446
+ function toJson(value) {
42447
+ return JSON.stringify(value, null, 2);
42448
+ }
42449
+ var publicSlideLayouts, layoutSlideOptionsSchema;
42450
+ var init_slideLayoutMcp = __esm({
42451
+ "mcp/slideLayoutMcp.ts"() {
42452
+ "use strict";
42453
+ init_mcp();
42454
+ init_v4();
42455
+ init_motionDocSerialize();
42456
+ init_motionDocAutomation();
42457
+ init_motionDocParser();
42458
+ init_slideLayouts();
42459
+ publicSlideLayouts = slideLayouts.filter((layout) => layout.id !== "blank");
42460
+ layoutSlideOptionsSchema = {
42461
+ accent: external_exports.string().optional(),
42462
+ background: external_exports.string().optional(),
42463
+ duration: external_exports.number().positive().optional(),
42464
+ replacements: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("Optional exact text replacements applied to the layout source."),
42465
+ textColor: external_exports.string().optional(),
42466
+ theme: external_exports.enum(["dark", "light"]).optional()
42467
+ };
42468
+ }
42469
+ });
42470
+
42034
42471
  // mcp/server.ts
42472
+ import fs from "node:fs";
42473
+ import path from "node:path";
42035
42474
  var require_server = __commonJS({
42036
42475
  "mcp/server.ts"() {
42037
42476
  init_mcp();
@@ -42041,6 +42480,7 @@ var require_server = __commonJS({
42041
42480
  init_motionDocExport();
42042
42481
  init_defaultMdx();
42043
42482
  init_templates();
42483
+ init_slideLayoutMcp();
42044
42484
  var server = new McpServer({
42045
42485
  name: "slidex-motion-doc",
42046
42486
  version: "2.0.4"
@@ -42059,6 +42499,8 @@ var require_server = __commonJS({
42059
42499
  registerResources();
42060
42500
  registerPrompts();
42061
42501
  registerTools();
42502
+ registerSlideLayoutMcp(server);
42503
+ registerWorkspaceSkills();
42062
42504
  main().catch((error51) => {
42063
42505
  console.error(error51 instanceof Error ? error51.stack ?? error51.message : String(error51));
42064
42506
  process.exitCode = 1;
@@ -42077,7 +42519,7 @@ var require_server = __commonJS({
42077
42519
  source: external_exports.string().describe("SlideX MotionDoc MDX source.")
42078
42520
  }
42079
42521
  },
42080
- ({ source }) => runTool(() => summarizeMotionDoc(source))
42522
+ ({ source }) => runTool2(() => summarizeMotionDoc(source))
42081
42523
  );
42082
42524
  server.registerTool(
42083
42525
  "slidex_validate_motion_doc",
@@ -42088,7 +42530,7 @@ var require_server = __commonJS({
42088
42530
  source: external_exports.string().describe("SlideX MotionDoc MDX source.")
42089
42531
  }
42090
42532
  },
42091
- ({ source }) => runTool(() => summarizeMotionDoc(source).validation)
42533
+ ({ source }) => runTool2(() => summarizeMotionDoc(source).validation)
42092
42534
  );
42093
42535
  server.registerTool(
42094
42536
  "slidex_list_templates",
@@ -42099,7 +42541,7 @@ var require_server = __commonJS({
42099
42541
  category: external_exports.string().optional().describe("Optional category filter.")
42100
42542
  }
42101
42543
  },
42102
- ({ category }) => runTool(() => listTemplateSummaries(category))
42544
+ ({ category }) => runTool2(() => listTemplateSummaries(category))
42103
42545
  );
42104
42546
  server.registerTool(
42105
42547
  "slidex_get_template",
@@ -42111,7 +42553,7 @@ var require_server = __commonJS({
42111
42553
  templateId: external_exports.string().optional().describe("Template id. Defaults to the first template.")
42112
42554
  }
42113
42555
  },
42114
- ({ includeSource, templateId }) => runTool(() => formatTemplate(getTemplateOrDefault(templateId), includeSource))
42556
+ ({ includeSource, templateId }) => runTool2(() => formatTemplate(getTemplateOrDefault(templateId), includeSource))
42115
42557
  );
42116
42558
  server.registerTool(
42117
42559
  "slidex_create_deck",
@@ -42133,7 +42575,7 @@ var require_server = __commonJS({
42133
42575
  title: external_exports.string()
42134
42576
  }
42135
42577
  },
42136
- (args) => runTool(() => createMotionDocFromOutline(args))
42578
+ (args) => runTool2(() => createMotionDocFromOutline(args))
42137
42579
  );
42138
42580
  server.registerTool(
42139
42581
  "slidex_create_from_template",
@@ -42146,7 +42588,7 @@ var require_server = __commonJS({
42146
42588
  title: external_exports.string().optional()
42147
42589
  }
42148
42590
  },
42149
- ({ replacements, templateId, title }) => runTool(() => {
42591
+ ({ replacements, templateId, title }) => runTool2(() => {
42150
42592
  const template = getTemplateOrDefault(templateId);
42151
42593
  const titled = title ? applyMotionDocTitle(template.source, title) : { source: template.source };
42152
42594
  const replaced = replacements ? applyMotionDocTextReplacements(titled.source, replacements) : { source: titled.source, summary: summarizeMotionDoc(titled.source) };
@@ -42168,7 +42610,7 @@ var require_server = __commonJS({
42168
42610
  source: external_exports.string()
42169
42611
  }
42170
42612
  },
42171
- ({ props, slideIndex, source }) => runTool(() => updateMotionDocSlideProps(source, slideIndex, props))
42613
+ ({ props, slideIndex, source }) => runTool2(() => updateMotionDocSlideProps(source, slideIndex, props))
42172
42614
  );
42173
42615
  server.registerTool(
42174
42616
  "slidex_replace_slide",
@@ -42181,7 +42623,7 @@ var require_server = __commonJS({
42181
42623
  source: external_exports.string()
42182
42624
  }
42183
42625
  },
42184
- ({ slideIndex, slideSource, source }) => runTool(() => replaceMotionDocSlide(source, slideIndex, slideSource))
42626
+ ({ slideIndex, slideSource, source }) => runTool2(() => replaceMotionDocSlide(source, slideIndex, slideSource))
42185
42627
  );
42186
42628
  server.registerTool(
42187
42629
  "slidex_add_block",
@@ -42197,7 +42639,7 @@ var require_server = __commonJS({
42197
42639
  type: external_exports.enum(motionDocAddBlockTypes)
42198
42640
  }
42199
42641
  },
42200
- ({ position, props, slideIndex, source, text: text2, type }) => runTool(() => addMotionDocBlock(source, slideIndex, type, { position, props, text: text2 }))
42642
+ ({ position, props, slideIndex, source, text: text2, type }) => runTool2(() => addMotionDocBlock(source, slideIndex, type, { position, props, text: text2 }))
42201
42643
  );
42202
42644
  server.registerTool(
42203
42645
  "slidex_delete_slide",
@@ -42209,7 +42651,7 @@ var require_server = __commonJS({
42209
42651
  source: external_exports.string()
42210
42652
  }
42211
42653
  },
42212
- ({ slideIndex, source }) => runTool(() => deleteMotionDocSlide(source, slideIndex))
42654
+ ({ slideIndex, source }) => runTool2(() => deleteMotionDocSlide(source, slideIndex))
42213
42655
  );
42214
42656
  server.registerTool(
42215
42657
  "slidex_reorder_slide",
@@ -42222,7 +42664,7 @@ var require_server = __commonJS({
42222
42664
  toIndex: external_exports.number().int().min(0)
42223
42665
  }
42224
42666
  },
42225
- ({ fromIndex, source, toIndex }) => runTool(() => reorderMotionDocSlide(source, fromIndex, toIndex))
42667
+ ({ fromIndex, source, toIndex }) => runTool2(() => reorderMotionDocSlide(source, fromIndex, toIndex))
42226
42668
  );
42227
42669
  server.registerTool(
42228
42670
  "slidex_export_html",
@@ -42234,7 +42676,7 @@ var require_server = __commonJS({
42234
42676
  title: external_exports.string().optional()
42235
42677
  }
42236
42678
  },
42237
- ({ source, title }) => runTool(() => ({
42679
+ ({ source, title }) => runTool2(() => ({
42238
42680
  html: buildMotionDocHtml(source, title),
42239
42681
  summary: summarizeMotionDoc(source)
42240
42682
  }))
@@ -42245,7 +42687,7 @@ var require_server = __commonJS({
42245
42687
  title: "List Block Types",
42246
42688
  description: "List block type names accepted by slidex_add_block."
42247
42689
  },
42248
- () => runTool(() => ({ blockTypes: motionDocAddBlockTypes }))
42690
+ () => runTool2(() => ({ blockTypes: motionDocAddBlockTypes }))
42249
42691
  );
42250
42692
  }
42251
42693
  function registerResources() {
@@ -42261,7 +42703,7 @@ var require_server = __commonJS({
42261
42703
  contents: [
42262
42704
  {
42263
42705
  mimeType: "application/json",
42264
- text: toJson(listTemplateSummaries()),
42706
+ text: toJson2(listTemplateSummaries()),
42265
42707
  uri: uri.href
42266
42708
  }
42267
42709
  ]
@@ -42353,9 +42795,81 @@ var require_server = __commonJS({
42353
42795
  })
42354
42796
  );
42355
42797
  }
42356
- function runTool(callback) {
42798
+ function registerWorkspaceSkills() {
42799
+ const skillsDir = path.join(process.cwd(), ".agents", "skills");
42800
+ if (!fs.existsSync(skillsDir)) return;
42801
+ let skillDirs = [];
42802
+ try {
42803
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
42804
+ skillDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
42805
+ } catch {
42806
+ return;
42807
+ }
42808
+ for (const dirName of skillDirs) {
42809
+ const skillFilePath = path.join(skillsDir, dirName, "SKILL.md");
42810
+ if (!fs.existsSync(skillFilePath)) continue;
42811
+ try {
42812
+ const content = fs.readFileSync(skillFilePath, "utf8");
42813
+ let name = dirName;
42814
+ let description = `Skill: ${dirName}`;
42815
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
42816
+ if (frontmatterMatch) {
42817
+ const yaml = frontmatterMatch[1];
42818
+ const nameMatch = yaml.match(/^name:\s*(.+)$/m);
42819
+ const descMatch = yaml.match(/^description:\s*(.+)$/m);
42820
+ if (nameMatch) name = nameMatch[1].trim();
42821
+ if (descMatch) description = descMatch[1].trim();
42822
+ }
42823
+ server.registerResource(
42824
+ `skill-${dirName}`,
42825
+ `workspace://skills/${dirName}`,
42826
+ {
42827
+ description,
42828
+ mimeType: "text/markdown",
42829
+ title: name
42830
+ },
42831
+ (uri) => ({
42832
+ contents: [
42833
+ {
42834
+ mimeType: "text/markdown",
42835
+ text: content,
42836
+ uri: uri.href
42837
+ }
42838
+ ]
42839
+ })
42840
+ );
42841
+ server.registerPrompt(
42842
+ `skill_${dirName.replace(/[^a-zA-Z0-9_]/g, "_")}`,
42843
+ {
42844
+ description,
42845
+ title: name,
42846
+ argsSchema: {
42847
+ request: external_exports.string().optional().describe("The user's specific request to apply this skill to.")
42848
+ }
42849
+ },
42850
+ (args) => {
42851
+ const requestText = args?.request ? `\\n\\nUser Request: ${args.request}` : "";
42852
+ return {
42853
+ description: `Apply the ${name} skill.`,
42854
+ messages: [
42855
+ {
42856
+ role: "user",
42857
+ content: {
42858
+ type: "text",
42859
+ text: `Please apply the following skill guidelines/workflows to the task.\\n\\nSkill Definition:\\n${content}${requestText}`
42860
+ }
42861
+ }
42862
+ ]
42863
+ };
42864
+ }
42865
+ );
42866
+ } catch {
42867
+ }
42868
+ }
42869
+ }
42870
+ function runTool2(callback) {
42357
42871
  try {
42358
- return jsonResult(callback());
42872
+ return jsonResult2(callback());
42359
42873
  } catch (error51) {
42360
42874
  return {
42361
42875
  content: [
@@ -42368,12 +42882,12 @@ var require_server = __commonJS({
42368
42882
  };
42369
42883
  }
42370
42884
  }
42371
- function jsonResult(data) {
42885
+ function jsonResult2(data) {
42372
42886
  return {
42373
42887
  content: [
42374
42888
  {
42375
42889
  type: "text",
42376
- text: toJson(data)
42890
+ text: toJson2(data)
42377
42891
  }
42378
42892
  ],
42379
42893
  structuredContent: {
@@ -42417,7 +42931,7 @@ var require_server = __commonJS({
42417
42931
  useCase: template.useCase
42418
42932
  };
42419
42933
  }
42420
- function toJson(value) {
42934
+ function toJson2(value) {
42421
42935
  return JSON.stringify(value, null, 2);
42422
42936
  }
42423
42937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@z7589xxz758/slidex-mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server for creating, validating, editing, and exporting SlideX MotionDoc decks.",
5
5
  "type": "module",
6
6
  "bin": {