@webiny/mcp 6.3.0-beta.4 → 6.4.0-beta.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.
@@ -1,44 +1,37 @@
1
1
  import { ConsoleUi } from "../ui.js";
2
2
  import { discoverAgents } from "../agents/discover.js";
3
- export async function configureMcp(params = {}) {
4
- const ui = params.ui ?? new ConsoleUi();
5
- const cwd = params.cwd ?? process.cwd();
6
- if (params.instructions) {
7
- const {
8
- printInstructions
9
- } = await import("../agents/instructions.js");
10
- await printInstructions();
11
- return;
12
- }
13
- const agents = await discoverAgents();
14
- const supported = Array.from(agents.keys());
15
- const target = params.agent;
16
- if (!target) {
17
- ui.text("Available agents:");
18
- ui.emptyLine();
19
- for (const [slug, {
20
- preset
21
- }] of agents) {
22
- ui.text(` ${slug.padEnd(12)} ${preset.displayName}`);
3
+ async function configureMcp(params = {}) {
4
+ const ui = params.ui ?? new ConsoleUi();
5
+ const cwd = params.cwd ?? process.cwd();
6
+ if (params.instructions) {
7
+ const { printInstructions } = await import("../agents/instructions.js");
8
+ await printInstructions();
9
+ return;
23
10
  }
24
- ui.emptyLine();
25
- ui.text("Usage: npx webiny-mcp configure <agent>");
26
- ui.text("For other agents, run: npx webiny-mcp configure --instructions");
27
- return;
28
- }
29
- if (!agents.has(target)) {
30
- ui.error(`Unknown agent "${target}".`);
31
- ui.text(`Supported: ${supported.join(", ")}`);
32
- ui.text("For other agents, run: npx webiny-mcp configure --instructions");
33
- process.exit(1);
34
- }
35
- const {
36
- init
37
- } = agents.get(target);
38
- await init({
39
- ui,
40
- cwd
41
- });
11
+ const agents = await discoverAgents();
12
+ const supported = Array.from(agents.keys());
13
+ const target = params.agent;
14
+ if (!target) {
15
+ ui.text("Available agents:");
16
+ ui.emptyLine();
17
+ for (const [slug, { preset }] of agents)ui.text(` ${slug.padEnd(12)} ${preset.displayName}`);
18
+ ui.emptyLine();
19
+ ui.text("Usage: npx webiny-mcp configure <agent>");
20
+ ui.text("For other agents, run: npx webiny-mcp configure --instructions");
21
+ return;
22
+ }
23
+ if (!agents.has(target)) {
24
+ ui.error(`Unknown agent "${target}".`);
25
+ ui.text(`Supported: ${supported.join(", ")}`);
26
+ ui.text("For other agents, run: npx webiny-mcp configure --instructions");
27
+ process.exit(1);
28
+ }
29
+ const { init } = agents.get(target);
30
+ await init({
31
+ ui,
32
+ cwd
33
+ });
42
34
  }
35
+ export { configureMcp };
43
36
 
44
37
  //# sourceMappingURL=ConfigureMcp.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ConsoleUi","discoverAgents","configureMcp","params","ui","cwd","process","instructions","printInstructions","agents","supported","Array","from","keys","target","agent","text","emptyLine","slug","preset","padEnd","displayName","has","error","join","exit","init","get"],"sources":["ConfigureMcp.ts"],"sourcesContent":["import type { IUi } from \"../ui.js\";\nimport { ConsoleUi } from \"../ui.js\";\nimport { discoverAgents } from \"../agents/discover.js\";\n\nexport interface IConfigureMcpParams {\n agent?: string;\n instructions?: boolean;\n ui?: IUi;\n cwd?: string;\n}\n\nexport async function configureMcp(params: IConfigureMcpParams = {}): Promise<void> {\n const ui = params.ui ?? new ConsoleUi();\n const cwd = params.cwd ?? process.cwd();\n\n if (params.instructions) {\n const { printInstructions } = await import(\"../agents/instructions.js\");\n await printInstructions();\n return;\n }\n\n const agents = await discoverAgents();\n const supported = Array.from(agents.keys());\n const target = params.agent;\n\n if (!target) {\n ui.text(\"Available agents:\");\n ui.emptyLine();\n for (const [slug, { preset }] of agents) {\n ui.text(` ${slug.padEnd(12)} ${preset.displayName}`);\n }\n ui.emptyLine();\n ui.text(\"Usage: npx webiny-mcp configure <agent>\");\n ui.text(\"For other agents, run: npx webiny-mcp configure --instructions\");\n return;\n }\n\n if (!agents.has(target)) {\n ui.error(`Unknown agent \"${target}\".`);\n ui.text(`Supported: ${supported.join(\", \")}`);\n ui.text(\"For other agents, run: npx webiny-mcp configure --instructions\");\n process.exit(1);\n }\n\n const { init } = agents.get(target)!;\n await init({ ui, cwd });\n}\n"],"mappings":"AACA,SAASA,SAAS;AAClB,SAASC,cAAc;AASvB,OAAO,eAAeC,YAAYA,CAACC,MAA2B,GAAG,CAAC,CAAC,EAAiB;EAChF,MAAMC,EAAE,GAAGD,MAAM,CAACC,EAAE,IAAI,IAAIJ,SAAS,CAAC,CAAC;EACvC,MAAMK,GAAG,GAAGF,MAAM,CAACE,GAAG,IAAIC,OAAO,CAACD,GAAG,CAAC,CAAC;EAEvC,IAAIF,MAAM,CAACI,YAAY,EAAE;IACrB,MAAM;MAAEC;IAAkB,CAAC,GAAG,MAAM,MAAM,4BAA4B,CAAC;IACvE,MAAMA,iBAAiB,CAAC,CAAC;IACzB;EACJ;EAEA,MAAMC,MAAM,GAAG,MAAMR,cAAc,CAAC,CAAC;EACrC,MAAMS,SAAS,GAAGC,KAAK,CAACC,IAAI,CAACH,MAAM,CAACI,IAAI,CAAC,CAAC,CAAC;EAC3C,MAAMC,MAAM,GAAGX,MAAM,CAACY,KAAK;EAE3B,IAAI,CAACD,MAAM,EAAE;IACTV,EAAE,CAACY,IAAI,CAAC,mBAAmB,CAAC;IAC5BZ,EAAE,CAACa,SAAS,CAAC,CAAC;IACd,KAAK,MAAM,CAACC,IAAI,EAAE;MAAEC;IAAO,CAAC,CAAC,IAAIV,MAAM,EAAE;MACrCL,EAAE,CAACY,IAAI,CAAC,KAAKE,IAAI,CAACE,MAAM,CAAC,EAAE,CAAC,IAAID,MAAM,CAACE,WAAW,EAAE,CAAC;IACzD;IACAjB,EAAE,CAACa,SAAS,CAAC,CAAC;IACdb,EAAE,CAACY,IAAI,CAAC,yCAAyC,CAAC;IAClDZ,EAAE,CAACY,IAAI,CAAC,gEAAgE,CAAC;IACzE;EACJ;EAEA,IAAI,CAACP,MAAM,CAACa,GAAG,CAACR,MAAM,CAAC,EAAE;IACrBV,EAAE,CAACmB,KAAK,CAAC,kBAAkBT,MAAM,IAAI,CAAC;IACtCV,EAAE,CAACY,IAAI,CAAC,cAAcN,SAAS,CAACc,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC7CpB,EAAE,CAACY,IAAI,CAAC,gEAAgE,CAAC;IACzEV,OAAO,CAACmB,IAAI,CAAC,CAAC,CAAC;EACnB;EAEA,MAAM;IAAEC;EAAK,CAAC,GAAGjB,MAAM,CAACkB,GAAG,CAACb,MAAM,CAAE;EACpC,MAAMY,IAAI,CAAC;IAAEtB,EAAE;IAAEC;EAAI,CAAC,CAAC;AAC3B","ignoreList":[]}
1
+ {"version":3,"file":"cli/ConfigureMcp.js","sources":["../../src/cli/ConfigureMcp.ts"],"sourcesContent":["import type { IUi } from \"../ui.js\";\nimport { ConsoleUi } from \"../ui.js\";\nimport { discoverAgents } from \"../agents/discover.js\";\n\nexport interface IConfigureMcpParams {\n agent?: string;\n instructions?: boolean;\n ui?: IUi;\n cwd?: string;\n}\n\nexport async function configureMcp(params: IConfigureMcpParams = {}): Promise<void> {\n const ui = params.ui ?? new ConsoleUi();\n const cwd = params.cwd ?? process.cwd();\n\n if (params.instructions) {\n const { printInstructions } = await import(\"../agents/instructions.js\");\n await printInstructions();\n return;\n }\n\n const agents = await discoverAgents();\n const supported = Array.from(agents.keys());\n const target = params.agent;\n\n if (!target) {\n ui.text(\"Available agents:\");\n ui.emptyLine();\n for (const [slug, { preset }] of agents) {\n ui.text(` ${slug.padEnd(12)} ${preset.displayName}`);\n }\n ui.emptyLine();\n ui.text(\"Usage: npx webiny-mcp configure <agent>\");\n ui.text(\"For other agents, run: npx webiny-mcp configure --instructions\");\n return;\n }\n\n if (!agents.has(target)) {\n ui.error(`Unknown agent \"${target}\".`);\n ui.text(`Supported: ${supported.join(\", \")}`);\n ui.text(\"For other agents, run: npx webiny-mcp configure --instructions\");\n process.exit(1);\n }\n\n const { init } = agents.get(target)!;\n await init({ ui, cwd });\n}\n"],"names":["configureMcp","params","ui","ConsoleUi","cwd","process","printInstructions","agents","discoverAgents","supported","Array","target","slug","preset","init"],"mappings":";;AAWO,eAAeA,aAAaC,SAA8B,CAAC,CAAC;IAC/D,MAAMC,KAAKD,OAAO,EAAE,IAAI,IAAIE;IAC5B,MAAMC,MAAMH,OAAO,GAAG,IAAII,QAAQ,GAAG;IAErC,IAAIJ,OAAO,YAAY,EAAE;QACrB,MAAM,EAAEK,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC;QAC3C,MAAMA;QACN;IACJ;IAEA,MAAMC,SAAS,MAAMC;IACrB,MAAMC,YAAYC,MAAM,IAAI,CAACH,OAAO,IAAI;IACxC,MAAMI,SAASV,OAAO,KAAK;IAE3B,IAAI,CAACU,QAAQ;QACTT,GAAG,IAAI,CAAC;QACRA,GAAG,SAAS;QACZ,KAAK,MAAM,CAACU,MAAM,EAAEC,MAAM,EAAE,CAAC,IAAIN,OAC7BL,GAAG,IAAI,CAAC,CAAC,EAAE,EAAEU,KAAK,MAAM,CAAC,IAAI,CAAC,EAAEC,OAAO,WAAW,EAAE;QAExDX,GAAG,SAAS;QACZA,GAAG,IAAI,CAAC;QACRA,GAAG,IAAI,CAAC;QACR;IACJ;IAEA,IAAI,CAACK,OAAO,GAAG,CAACI,SAAS;QACrBT,GAAG,KAAK,CAAC,CAAC,eAAe,EAAES,OAAO,EAAE,CAAC;QACrCT,GAAG,IAAI,CAAC,CAAC,WAAW,EAAEO,UAAU,IAAI,CAAC,OAAO;QAC5CP,GAAG,IAAI,CAAC;QACRG,QAAQ,IAAI,CAAC;IACjB;IAEA,MAAM,EAAES,IAAI,EAAE,GAAGP,OAAO,GAAG,CAACI;IAC5B,MAAMG,KAAK;QAAEZ;QAAIE;IAAI;AACzB"}
package/cli/McpServer.js CHANGED
@@ -1,213 +1,174 @@
1
- import { readFileSync, readdirSync, existsSync } from "fs";
2
- import { join, resolve, dirname } from "path";
1
+ import { existsSync, readFileSync, readdirSync } from "fs";
2
+ import { dirname, join, resolve } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { createRequire } from "module";
5
- import fm from "front-matter";
5
+ import front_matter from "front-matter";
6
6
  import { z } from "zod";
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
-
11
- // ---------------------------------------------------------------------------
12
- // Skill discovery
13
- // ---------------------------------------------------------------------------
14
-
15
- /**
16
- * Recursively find all SKILL.md files under `dir`.
17
- */
9
+ const McpServer_dirname = dirname(fileURLToPath(import.meta.url));
18
10
  function findSkillFiles(dir) {
19
- if (!existsSync(dir)) {
20
- return [];
21
- }
22
- const results = [];
23
- for (const entry of readdirSync(dir, {
24
- withFileTypes: true
25
- })) {
26
- const fullPath = join(dir, entry.name);
27
- if (entry.isDirectory()) {
28
- results.push(...findSkillFiles(fullPath));
29
- } else if (entry.isFile() && entry.name === "SKILL.md") {
30
- results.push(fullPath);
11
+ if (!existsSync(dir)) return [];
12
+ const results = [];
13
+ for (const entry of readdirSync(dir, {
14
+ withFileTypes: true
15
+ })){
16
+ const fullPath = join(dir, entry.name);
17
+ if (entry.isDirectory()) results.push(...findSkillFiles(fullPath));
18
+ else if (entry.isFile() && "SKILL.md" === entry.name) results.push(fullPath);
31
19
  }
32
- }
33
- return results;
20
+ return results;
34
21
  }
35
-
36
- /**
37
- * Discover skills from multiple directories. First match wins (higher-priority dirs first).
38
- */
39
22
  function discoverSkills(skillsDirs) {
40
- const skills = new Map();
41
- for (const dir of skillsDirs) {
42
- for (const filePath of findSkillFiles(dir)) {
43
- try {
23
+ const skills = new Map();
24
+ for (const dir of skillsDirs)for (const filePath of findSkillFiles(dir))try {
44
25
  const raw = readFileSync(filePath, "utf8");
45
- const parsed = fm(raw);
46
- const {
47
- name,
48
- description
49
- } = parsed.attributes;
26
+ const parsed = front_matter(raw);
27
+ const { name, description } = parsed.attributes;
50
28
  if (!name || !description) {
51
- console.error(`[webiny-mcp] skipping ${filePath}: missing name or description`);
52
- continue;
29
+ console.error(`[webiny-mcp] skipping ${filePath}: missing name or description`);
30
+ continue;
53
31
  }
54
32
  if (!skills.has(name)) {
55
- const context = parsed.attributes.context || "webiny-extensions";
56
- skills.set(name, {
57
- name,
58
- description,
59
- context,
60
- filePath
61
- });
33
+ const context = parsed.attributes.context || "webiny-extensions";
34
+ skills.set(name, {
35
+ name,
36
+ description,
37
+ context,
38
+ filePath
39
+ });
62
40
  }
63
- } catch (err) {
41
+ } catch (err) {
64
42
  console.error(`[webiny-mcp] error reading ${filePath}:`, err);
65
- }
66
43
  }
67
- }
68
- return skills;
44
+ return skills;
69
45
  }
70
-
71
- // ---------------------------------------------------------------------------
72
- // Helpers
73
- // ---------------------------------------------------------------------------
74
-
75
46
  function getVersion() {
76
- try {
77
- return createRequire(import.meta.url)("../../package.json").version;
78
- } catch {
79
- return "0.0.0";
80
- }
47
+ try {
48
+ return createRequire(import.meta.url)("../../package.json").version;
49
+ } catch {
50
+ return "0.0.0";
51
+ }
81
52
  }
82
53
  function buildCatalog(skills) {
83
- const lines = [`# Webiny Skills (v${getVersion()})`, ""];
84
- if (skills.size === 0) {
85
- lines.push("_(No skills found. Add SKILL.md files with front-matter to a skills directory.)_");
86
- return lines.join("\n");
87
- }
88
-
89
- // Group skills by context.
90
- const contextDescriptions = {
91
- "webiny-extensions": "Use these skills when writing Webiny extensions (usually in `extensions/`) or making changes to `webiny.config.tsx` (user project development).",
92
- "webiny-packages": "Use these skills when writing code in `packages/` (core Webiny framework development)."
93
- };
94
- const groups = new Map();
95
- for (const skill of skills.values()) {
96
- const list = groups.get(skill.context) || [];
97
- list.push(skill);
98
- groups.set(skill.context, list);
99
- }
100
- for (const [context, contextSkills] of [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
101
- lines.push(`## ${context}`, "");
102
- const desc = contextDescriptions[context];
103
- if (desc) {
104
- lines.push(desc, "");
54
+ const lines = [
55
+ `# Webiny Skills (v${getVersion()})`,
56
+ ""
57
+ ];
58
+ if (0 === skills.size) {
59
+ lines.push("_(No skills found. Add SKILL.md files with front-matter to a skills directory.)_");
60
+ return lines.join("\n");
61
+ }
62
+ const contextDescriptions = {
63
+ "webiny-extensions": "Use these skills when writing Webiny extensions (usually in `extensions/`) or making changes to `webiny.config.tsx` (user project development).",
64
+ "webiny-packages": "Use these skills when writing code in `packages/` (core Webiny framework development)."
65
+ };
66
+ const groups = new Map();
67
+ for (const skill of skills.values()){
68
+ const list = groups.get(skill.context) || [];
69
+ list.push(skill);
70
+ groups.set(skill.context, list);
105
71
  }
106
- lines.push("| Skill | Description |");
107
- lines.push("|---|---|");
108
- for (const skill of contextSkills.sort((a, b) => a.name.localeCompare(b.name))) {
109
- const skillDesc = skill.description.replace(/\n/g, " ").trim();
110
- lines.push(`| \`${skill.name}\` | ${skillDesc} |`);
72
+ for (const [context, contextSkills] of [
73
+ ...groups.entries()
74
+ ].sort((a, b)=>a[0].localeCompare(b[0]))){
75
+ lines.push(`## ${context}`, "");
76
+ const desc = contextDescriptions[context];
77
+ if (desc) lines.push(desc, "");
78
+ lines.push("| Skill | Description |");
79
+ lines.push("|---|---|");
80
+ for (const skill of contextSkills.sort((a, b)=>a.name.localeCompare(b.name))){
81
+ const skillDesc = skill.description.replace(/\n/g, " ").trim();
82
+ lines.push(`| \`${skill.name}\` | ${skillDesc} |`);
83
+ }
84
+ lines.push("");
111
85
  }
112
- lines.push("");
113
- }
114
- return lines.join("\n");
86
+ return lines.join("\n");
115
87
  }
116
88
  function readSkillContent(skill) {
117
- const raw = readFileSync(skill.filePath, "utf8");
118
- return fm(raw).body;
89
+ const raw = readFileSync(skill.filePath, "utf8");
90
+ return front_matter(raw).body;
119
91
  }
120
-
121
- // ---------------------------------------------------------------------------
122
- // Standalone entry point
123
- // ---------------------------------------------------------------------------
124
-
125
- export async function startMcpServer(params = {}) {
126
- const cwd = process.cwd();
127
- const builtInSkillsDir = join(__dirname, "..", "skills");
128
- const skillsOverride = params.skills;
129
- const additionalSkillsDirs = params.additionalSkills || [];
130
- const baseDir = skillsOverride ? resolve(cwd, skillsOverride) : builtInSkillsDir;
131
-
132
- // skillsDirs[0] = highest priority, skillsDirs[last] = lowest priority
133
- const skillsDirs = [...[...additionalSkillsDirs].map(p => resolve(cwd, p)).reverse(), baseDir];
134
- if (skillsOverride) {
135
- console.error(`[webiny-mcp] skills override: ${baseDir}`);
136
- }
137
- for (const d of additionalSkillsDirs) {
138
- console.error(`[webiny-mcp] additional skills: ${resolve(cwd, d)}`);
139
- }
140
-
141
- // In-memory cache: populated on first list, reused by get.
142
- let skillsCache = null;
143
- function getSkills() {
144
- if (!skillsCache) {
145
- skillsCache = discoverSkills(skillsDirs);
146
- console.error(`[webiny-mcp] discovered ${skillsCache.size} skill(s)`);
147
- }
148
- return skillsCache;
149
- }
150
-
151
- // ---------------------------------------------------------------
152
- // MCP server
153
- // ---------------------------------------------------------------
154
-
155
- const server = new McpServer({
156
- name: "webiny",
157
- version: getVersion()
158
- });
159
- server.registerTool("list_webiny_skills", {
160
- title: "List Webiny Skills",
161
- description: "Returns a catalog of all available Webiny skills with names and descriptions. " + "Always call this first when working on anything Webiny-related, then call " + "get_webiny_skill to load the specific skill you need.",
162
- inputSchema: {},
163
- annotations: {
164
- readOnlyHint: true
165
- }
166
- }, async () => ({
167
- content: [{
168
- type: "text",
169
- text: buildCatalog(getSkills())
170
- }]
171
- }));
172
- server.registerTool("get_webiny_skill", {
173
- title: "Get Webiny Skill",
174
- description: "Loads the full Webiny documentation for a specific skill. " + "Call list_webiny_skills first to see available skill names.",
175
- inputSchema: {
176
- topic: z.string().describe("Skill name — use exact names from list_webiny_skills")
177
- },
178
- annotations: {
179
- readOnlyHint: true
180
- }
181
- }, async ({
182
- topic
183
- }) => {
184
- const skills = getSkills();
185
- const skill = skills.get(topic);
186
- if (!skill) {
187
- const available = [...skills.keys()].sort();
188
- return {
189
- content: [{
190
- type: "text",
191
- text: `Skill not found: "${topic}".\n\n` + `Available skills: ${available.join(", ") || "(none)"}.`
192
- }],
193
- isError: true
194
- };
92
+ async function startMcpServer(params = {}) {
93
+ const cwd = process.cwd();
94
+ const builtInSkillsDir = join(McpServer_dirname, "..", "skills");
95
+ const skillsOverride = params.skills;
96
+ const additionalSkillsDirs = params.additionalSkills || [];
97
+ const baseDir = skillsOverride ? resolve(cwd, skillsOverride) : builtInSkillsDir;
98
+ const skillsDirs = [
99
+ ...[
100
+ ...additionalSkillsDirs
101
+ ].map((p)=>resolve(cwd, p)).reverse(),
102
+ baseDir
103
+ ];
104
+ if (skillsOverride) console.error(`[webiny-mcp] skills override: ${baseDir}`);
105
+ for (const d of additionalSkillsDirs)console.error(`[webiny-mcp] additional skills: ${resolve(cwd, d)}`);
106
+ let skillsCache = null;
107
+ function getSkills() {
108
+ if (!skillsCache) {
109
+ skillsCache = discoverSkills(skillsDirs);
110
+ console.error(`[webiny-mcp] discovered ${skillsCache.size} skill(s)`);
111
+ }
112
+ return skillsCache;
195
113
  }
196
- return {
197
- content: [{
198
- type: "text",
199
- text: readSkillContent(skill)
200
- }]
201
- };
202
- });
203
-
204
- // ---------------------------------------------------------------
205
- // Start
206
- // ---------------------------------------------------------------
207
-
208
- const transport = new StdioServerTransport();
209
- await server.connect(transport);
210
- console.error("[webiny-mcp] server ready");
114
+ const server = new McpServer({
115
+ name: "webiny",
116
+ version: getVersion()
117
+ });
118
+ server.registerTool("list_webiny_skills", {
119
+ title: "List Webiny Skills",
120
+ description: "Returns a catalog of all available Webiny skills with names and descriptions. Always call this first when working on anything Webiny-related, then call get_webiny_skill to load the specific skill you need.",
121
+ inputSchema: {},
122
+ annotations: {
123
+ readOnlyHint: true
124
+ }
125
+ }, async ()=>({
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: buildCatalog(getSkills())
130
+ }
131
+ ]
132
+ }));
133
+ server.registerTool("get_webiny_skill", {
134
+ title: "Get Webiny Skill",
135
+ description: "Loads the full Webiny documentation for a specific skill. Call list_webiny_skills first to see available skill names.",
136
+ inputSchema: {
137
+ topic: z.string().describe("Skill name — use exact names from list_webiny_skills")
138
+ },
139
+ annotations: {
140
+ readOnlyHint: true
141
+ }
142
+ }, async ({ topic })=>{
143
+ const skills = getSkills();
144
+ const skill = skills.get(topic);
145
+ if (!skill) {
146
+ const available = [
147
+ ...skills.keys()
148
+ ].sort();
149
+ return {
150
+ content: [
151
+ {
152
+ type: "text",
153
+ text: `Skill not found: "${topic}".\n\nAvailable skills: ${available.join(", ") || "(none)"}.`
154
+ }
155
+ ],
156
+ isError: true
157
+ };
158
+ }
159
+ return {
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: readSkillContent(skill)
164
+ }
165
+ ]
166
+ };
167
+ });
168
+ const transport = new StdioServerTransport();
169
+ await server.connect(transport);
170
+ console.error("[webiny-mcp] server ready");
211
171
  }
172
+ export { startMcpServer };
212
173
 
213
174
  //# sourceMappingURL=McpServer.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["readFileSync","readdirSync","existsSync","join","resolve","dirname","fileURLToPath","createRequire","fm","z","McpServer","StdioServerTransport","__dirname","import","meta","url","findSkillFiles","dir","results","entry","withFileTypes","fullPath","name","isDirectory","push","isFile","discoverSkills","skillsDirs","skills","Map","filePath","raw","parsed","description","attributes","console","error","has","context","set","err","getVersion","version","buildCatalog","lines","size","contextDescriptions","groups","skill","values","list","get","contextSkills","entries","sort","a","b","localeCompare","desc","skillDesc","replace","trim","readSkillContent","body","startMcpServer","params","cwd","process","builtInSkillsDir","skillsOverride","additionalSkillsDirs","additionalSkills","baseDir","map","p","reverse","d","skillsCache","getSkills","server","registerTool","title","inputSchema","annotations","readOnlyHint","content","type","text","topic","string","describe","available","keys","isError","transport","connect"],"sources":["McpServer.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from \"fs\";\nimport { join, resolve, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { createRequire } from \"module\";\nimport fm from \"front-matter\";\nimport { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface IMcpServerParams {\n skills?: string;\n additionalSkills?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Skill discovery\n// ---------------------------------------------------------------------------\n\ninterface SkillAttributes {\n name: string;\n description: string;\n context?: string;\n}\n\ninterface Skill {\n name: string;\n description: string;\n context: string;\n filePath: string;\n}\n\n/**\n * Recursively find all SKILL.md files under `dir`.\n */\nfunction findSkillFiles(dir: string): string[] {\n if (!existsSync(dir)) {\n return [];\n }\n const results: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...findSkillFiles(fullPath));\n } else if (entry.isFile() && entry.name === \"SKILL.md\") {\n results.push(fullPath);\n }\n }\n return results;\n}\n\n/**\n * Discover skills from multiple directories. First match wins (higher-priority dirs first).\n */\nfunction discoverSkills(skillsDirs: string[]): Map<string, Skill> {\n const skills = new Map<string, Skill>();\n\n for (const dir of skillsDirs) {\n for (const filePath of findSkillFiles(dir)) {\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const parsed = fm<SkillAttributes>(raw);\n const { name, description } = parsed.attributes;\n\n if (!name || !description) {\n console.error(`[webiny-mcp] skipping ${filePath}: missing name or description`);\n continue;\n }\n\n if (!skills.has(name)) {\n const context = parsed.attributes.context || \"webiny-extensions\";\n skills.set(name, { name, description, context, filePath });\n }\n } catch (err) {\n console.error(`[webiny-mcp] error reading ${filePath}:`, err);\n }\n }\n }\n\n return skills;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n return createRequire(import.meta.url)(\"../../package.json\").version;\n } catch {\n return \"0.0.0\";\n }\n}\n\nfunction buildCatalog(skills: Map<string, Skill>): string {\n const lines: string[] = [`# Webiny Skills (v${getVersion()})`, \"\"];\n\n if (skills.size === 0) {\n lines.push(\n \"_(No skills found. Add SKILL.md files with front-matter to a skills directory.)_\"\n );\n return lines.join(\"\\n\");\n }\n\n // Group skills by context.\n const contextDescriptions: Record<string, string> = {\n \"webiny-extensions\":\n \"Use these skills when writing Webiny extensions (usually in `extensions/`) or making changes to `webiny.config.tsx` (user project development).\",\n \"webiny-packages\":\n \"Use these skills when writing code in `packages/` (core Webiny framework development).\"\n };\n\n const groups = new Map<string, Skill[]>();\n for (const skill of skills.values()) {\n const list = groups.get(skill.context) || [];\n list.push(skill);\n groups.set(skill.context, list);\n }\n\n for (const [context, contextSkills] of [...groups.entries()].sort((a, b) =>\n a[0].localeCompare(b[0])\n )) {\n lines.push(`## ${context}`, \"\");\n const desc = contextDescriptions[context];\n if (desc) {\n lines.push(desc, \"\");\n }\n lines.push(\"| Skill | Description |\");\n lines.push(\"|---|---|\");\n for (const skill of contextSkills.sort((a, b) => a.name.localeCompare(b.name))) {\n const skillDesc = skill.description.replace(/\\n/g, \" \").trim();\n lines.push(`| \\`${skill.name}\\` | ${skillDesc} |`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction readSkillContent(skill: Skill): string {\n const raw = readFileSync(skill.filePath, \"utf8\");\n return fm(raw).body;\n}\n\n// ---------------------------------------------------------------------------\n// Standalone entry point\n// ---------------------------------------------------------------------------\n\nexport async function startMcpServer(params: IMcpServerParams = {}): Promise<void> {\n const cwd = process.cwd();\n const builtInSkillsDir = join(__dirname, \"..\", \"skills\");\n const skillsOverride = params.skills;\n const additionalSkillsDirs = params.additionalSkills || [];\n\n const baseDir = skillsOverride ? resolve(cwd, skillsOverride) : builtInSkillsDir;\n\n // skillsDirs[0] = highest priority, skillsDirs[last] = lowest priority\n const skillsDirs = [...[...additionalSkillsDirs].map(p => resolve(cwd, p)).reverse(), baseDir];\n\n if (skillsOverride) {\n console.error(`[webiny-mcp] skills override: ${baseDir}`);\n }\n for (const d of additionalSkillsDirs) {\n console.error(`[webiny-mcp] additional skills: ${resolve(cwd, d)}`);\n }\n\n // In-memory cache: populated on first list, reused by get.\n let skillsCache: Map<string, Skill> | null = null;\n\n function getSkills(): Map<string, Skill> {\n if (!skillsCache) {\n skillsCache = discoverSkills(skillsDirs);\n console.error(`[webiny-mcp] discovered ${skillsCache.size} skill(s)`);\n }\n return skillsCache;\n }\n\n // ---------------------------------------------------------------\n // MCP server\n // ---------------------------------------------------------------\n\n const server = new McpServer({ name: \"webiny\", version: getVersion() });\n\n server.registerTool(\n \"list_webiny_skills\",\n {\n title: \"List Webiny Skills\",\n description:\n \"Returns a catalog of all available Webiny skills with names and descriptions. \" +\n \"Always call this first when working on anything Webiny-related, then call \" +\n \"get_webiny_skill to load the specific skill you need.\",\n inputSchema: {},\n annotations: { readOnlyHint: true }\n },\n async () => ({\n content: [{ type: \"text\", text: buildCatalog(getSkills()) }]\n })\n );\n\n server.registerTool(\n \"get_webiny_skill\",\n {\n title: \"Get Webiny Skill\",\n description:\n \"Loads the full Webiny documentation for a specific skill. \" +\n \"Call list_webiny_skills first to see available skill names.\",\n inputSchema: {\n topic: z.string().describe(\"Skill name — use exact names from list_webiny_skills\")\n },\n annotations: { readOnlyHint: true }\n },\n async ({ topic }) => {\n const skills = getSkills();\n const skill = skills.get(topic);\n if (!skill) {\n const available = [...skills.keys()].sort();\n return {\n content: [\n {\n type: \"text\",\n text:\n `Skill not found: \"${topic}\".\\n\\n` +\n `Available skills: ${available.join(\", \") || \"(none)\"}.`\n }\n ],\n isError: true\n };\n }\n return {\n content: [{ type: \"text\", text: readSkillContent(skill) }]\n };\n }\n );\n\n // ---------------------------------------------------------------\n // Start\n // ---------------------------------------------------------------\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[webiny-mcp] server ready\");\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"cli/McpServer.js","sources":["../../src/cli/McpServer.ts"],"sourcesContent":["import { readFileSync, readdirSync, existsSync } from \"fs\";\nimport { join, resolve, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { createRequire } from \"module\";\nimport fm from \"front-matter\";\nimport { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport interface IMcpServerParams {\n skills?: string;\n additionalSkills?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Skill discovery\n// ---------------------------------------------------------------------------\n\ninterface SkillAttributes {\n name: string;\n description: string;\n context?: string;\n}\n\ninterface Skill {\n name: string;\n description: string;\n context: string;\n filePath: string;\n}\n\n/**\n * Recursively find all SKILL.md files under `dir`.\n */\nfunction findSkillFiles(dir: string): string[] {\n if (!existsSync(dir)) {\n return [];\n }\n const results: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...findSkillFiles(fullPath));\n } else if (entry.isFile() && entry.name === \"SKILL.md\") {\n results.push(fullPath);\n }\n }\n return results;\n}\n\n/**\n * Discover skills from multiple directories. First match wins (higher-priority dirs first).\n */\nfunction discoverSkills(skillsDirs: string[]): Map<string, Skill> {\n const skills = new Map<string, Skill>();\n\n for (const dir of skillsDirs) {\n for (const filePath of findSkillFiles(dir)) {\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const parsed = fm<SkillAttributes>(raw);\n const { name, description } = parsed.attributes;\n\n if (!name || !description) {\n console.error(`[webiny-mcp] skipping ${filePath}: missing name or description`);\n continue;\n }\n\n if (!skills.has(name)) {\n const context = parsed.attributes.context || \"webiny-extensions\";\n skills.set(name, { name, description, context, filePath });\n }\n } catch (err) {\n console.error(`[webiny-mcp] error reading ${filePath}:`, err);\n }\n }\n }\n\n return skills;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getVersion(): string {\n try {\n return createRequire(import.meta.url)(\"../../package.json\").version;\n } catch {\n return \"0.0.0\";\n }\n}\n\nfunction buildCatalog(skills: Map<string, Skill>): string {\n const lines: string[] = [`# Webiny Skills (v${getVersion()})`, \"\"];\n\n if (skills.size === 0) {\n lines.push(\n \"_(No skills found. Add SKILL.md files with front-matter to a skills directory.)_\"\n );\n return lines.join(\"\\n\");\n }\n\n // Group skills by context.\n const contextDescriptions: Record<string, string> = {\n \"webiny-extensions\":\n \"Use these skills when writing Webiny extensions (usually in `extensions/`) or making changes to `webiny.config.tsx` (user project development).\",\n \"webiny-packages\":\n \"Use these skills when writing code in `packages/` (core Webiny framework development).\"\n };\n\n const groups = new Map<string, Skill[]>();\n for (const skill of skills.values()) {\n const list = groups.get(skill.context) || [];\n list.push(skill);\n groups.set(skill.context, list);\n }\n\n for (const [context, contextSkills] of [...groups.entries()].sort((a, b) =>\n a[0].localeCompare(b[0])\n )) {\n lines.push(`## ${context}`, \"\");\n const desc = contextDescriptions[context];\n if (desc) {\n lines.push(desc, \"\");\n }\n lines.push(\"| Skill | Description |\");\n lines.push(\"|---|---|\");\n for (const skill of contextSkills.sort((a, b) => a.name.localeCompare(b.name))) {\n const skillDesc = skill.description.replace(/\\n/g, \" \").trim();\n lines.push(`| \\`${skill.name}\\` | ${skillDesc} |`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction readSkillContent(skill: Skill): string {\n const raw = readFileSync(skill.filePath, \"utf8\");\n return fm(raw).body;\n}\n\n// ---------------------------------------------------------------------------\n// Standalone entry point\n// ---------------------------------------------------------------------------\n\nexport async function startMcpServer(params: IMcpServerParams = {}): Promise<void> {\n const cwd = process.cwd();\n const builtInSkillsDir = join(__dirname, \"..\", \"skills\");\n const skillsOverride = params.skills;\n const additionalSkillsDirs = params.additionalSkills || [];\n\n const baseDir = skillsOverride ? resolve(cwd, skillsOverride) : builtInSkillsDir;\n\n // skillsDirs[0] = highest priority, skillsDirs[last] = lowest priority\n const skillsDirs = [...[...additionalSkillsDirs].map(p => resolve(cwd, p)).reverse(), baseDir];\n\n if (skillsOverride) {\n console.error(`[webiny-mcp] skills override: ${baseDir}`);\n }\n for (const d of additionalSkillsDirs) {\n console.error(`[webiny-mcp] additional skills: ${resolve(cwd, d)}`);\n }\n\n // In-memory cache: populated on first list, reused by get.\n let skillsCache: Map<string, Skill> | null = null;\n\n function getSkills(): Map<string, Skill> {\n if (!skillsCache) {\n skillsCache = discoverSkills(skillsDirs);\n console.error(`[webiny-mcp] discovered ${skillsCache.size} skill(s)`);\n }\n return skillsCache;\n }\n\n // ---------------------------------------------------------------\n // MCP server\n // ---------------------------------------------------------------\n\n const server = new McpServer({ name: \"webiny\", version: getVersion() });\n\n server.registerTool(\n \"list_webiny_skills\",\n {\n title: \"List Webiny Skills\",\n description:\n \"Returns a catalog of all available Webiny skills with names and descriptions. \" +\n \"Always call this first when working on anything Webiny-related, then call \" +\n \"get_webiny_skill to load the specific skill you need.\",\n inputSchema: {},\n annotations: { readOnlyHint: true }\n },\n async () => ({\n content: [{ type: \"text\", text: buildCatalog(getSkills()) }]\n })\n );\n\n server.registerTool(\n \"get_webiny_skill\",\n {\n title: \"Get Webiny Skill\",\n description:\n \"Loads the full Webiny documentation for a specific skill. \" +\n \"Call list_webiny_skills first to see available skill names.\",\n inputSchema: {\n topic: z.string().describe(\"Skill name — use exact names from list_webiny_skills\")\n },\n annotations: { readOnlyHint: true }\n },\n async ({ topic }) => {\n const skills = getSkills();\n const skill = skills.get(topic);\n if (!skill) {\n const available = [...skills.keys()].sort();\n return {\n content: [\n {\n type: \"text\",\n text:\n `Skill not found: \"${topic}\".\\n\\n` +\n `Available skills: ${available.join(\", \") || \"(none)\"}.`\n }\n ],\n isError: true\n };\n }\n return {\n content: [{ type: \"text\", text: readSkillContent(skill) }]\n };\n }\n );\n\n // ---------------------------------------------------------------\n // Start\n // ---------------------------------------------------------------\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[webiny-mcp] server ready\");\n}\n"],"names":["__dirname","dirname","fileURLToPath","findSkillFiles","dir","existsSync","results","entry","readdirSync","fullPath","join","discoverSkills","skillsDirs","skills","Map","filePath","raw","readFileSync","parsed","fm","name","description","console","context","err","getVersion","createRequire","buildCatalog","lines","contextDescriptions","groups","skill","list","contextSkills","a","b","desc","skillDesc","readSkillContent","startMcpServer","params","cwd","process","builtInSkillsDir","skillsOverride","additionalSkillsDirs","baseDir","resolve","p","d","skillsCache","getSkills","server","McpServer","z","topic","available","transport","StdioServerTransport"],"mappings":"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"}