@olonjs/mcp 1.0.106 → 1.0.108

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/cli.js CHANGED
@@ -56,6 +56,19 @@ var PlaywrightBridge = class {
56
56
  return window.navigator.modelContextProtocol.executeTool(name, args);
57
57
  }, { name: toolName, args: inputArgsJson });
58
58
  }
59
+ async navigateTo(slug) {
60
+ if (!this.page) throw new Error("Not connected");
61
+ const baseUrl = this.targetUrl.replace(/\/admin.*$/, "");
62
+ const targetUrl = `${baseUrl}/admin/${slug}`;
63
+ console.error(`[PlaywrightBridge] Navigating to ${targetUrl}...`);
64
+ await this.page.goto(targetUrl, { waitUntil: "domcontentloaded" });
65
+ await this.page.waitForFunction(() => {
66
+ return window.navigator.modelContextProtocol !== void 0;
67
+ }, void 0, { timeout: 15e3 }).catch(() => {
68
+ throw new Error(`Timeout waiting for WebMCP on /admin/${slug}.`);
69
+ });
70
+ console.error(`[PlaywrightBridge] Now on slug: ${slug}`);
71
+ }
59
72
  async disconnect() {
60
73
  if (this.browser) {
61
74
  await this.browser.close();
@@ -105,12 +118,28 @@ var OlonJsMcpServer = class {
105
118
  try {
106
119
  const webMcpTools = await this.bridge.listTools();
107
120
  return {
108
- tools: webMcpTools.map((t) => ({
109
- name: t.name,
110
- description: t.description,
111
- inputSchema: t.inputSchema
112
- // MCP SDK expects specific types but general JSON schema is supported
113
- }))
121
+ tools: [
122
+ {
123
+ name: "navigate-to-page",
124
+ description: "Navigate the Studio Playwright browser to a specific page by slug. Call this before update-section whenever you need to edit a page different from the current one.",
125
+ inputSchema: {
126
+ type: "object",
127
+ properties: {
128
+ slug: {
129
+ type: "string",
130
+ description: "The page slug to navigate to (e.g. 'chi-sono', 'home', 'contatti')."
131
+ }
132
+ },
133
+ required: ["slug"],
134
+ additionalProperties: false
135
+ }
136
+ },
137
+ ...webMcpTools.map((t) => ({
138
+ name: t.name,
139
+ description: t.description,
140
+ inputSchema: t.inputSchema
141
+ }))
142
+ ]
114
143
  };
115
144
  } catch (err) {
116
145
  console.error("Failed to list tools:", err);
@@ -120,6 +149,15 @@ var OlonJsMcpServer = class {
120
149
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
121
150
  try {
122
151
  const { name, arguments: args } = request.params;
152
+ if (name === "navigate-to-page") {
153
+ const slug = args?.slug;
154
+ if (!slug) throw new Error("Missing required parameter: slug");
155
+ await this.bridge.navigateTo(slug);
156
+ return {
157
+ content: [{ type: "text", text: `Navigated to /admin/${slug}. Studio is now on slug "${slug}".` }],
158
+ isError: false
159
+ };
160
+ }
123
161
  const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));
124
162
  const parsed = JSON.parse(resultString);
125
163
  return {
@@ -183,23 +221,56 @@ import * as fs from "fs/promises";
183
221
  import { existsSync, mkdirSync } from "fs";
184
222
  import * as path from "path";
185
223
  import * as os from "os";
224
+ import { execSync } from "child_process";
186
225
  async function initMcp() {
187
226
  console.log("\n[OlonJs MCP] Initializing Cursor MCP settings...\n");
188
- const isWin = process2.platform === "win32";
189
- const commandName = isWin ? "npx.cmd" : "npx";
227
+ let isWin = process2.platform === "win32";
228
+ let isWsl = false;
229
+ let wslHostHome = "";
230
+ if (process2.platform === "linux" && process2.env.WSL_DISTRO_NAME) {
231
+ isWsl = true;
232
+ try {
233
+ const winProfile = execSync('cmd.exe /c "echo %USERPROFILE%"').toString().trim();
234
+ if (winProfile && winProfile.includes(":\\")) {
235
+ const drive = winProfile.charAt(0).toLowerCase();
236
+ const rest = winProfile.slice(2).replace(/\\/g, "/");
237
+ wslHostHome = `/mnt/${drive}${rest}`;
238
+ }
239
+ } catch (e) {
240
+ console.log("Warning: Detected WSL but could not resolve Windows host profile path.");
241
+ }
242
+ }
243
+ const commandName = isWin || isWsl ? "wsl" : "npx";
190
244
  const mcpConfig = {
191
245
  command: commandName,
192
246
  args: ["-y", "@olonjs/mcp@latest", "http://localhost:5174"]
193
247
  };
248
+ if (isWsl) {
249
+ const distro = process2.env.WSL_DISTRO_NAME || "Ubuntu";
250
+ mcpConfig.args = [
251
+ "-d",
252
+ distro,
253
+ "--cd",
254
+ "~",
255
+ "bash",
256
+ "-ilc",
257
+ "npx -y @olonjs/mcp@latest http://localhost:5174"
258
+ ];
259
+ } else if (isWin) {
260
+ mcpConfig.command = "npx.cmd";
261
+ }
194
262
  const homeDir = os.homedir();
195
- const pathsToCheck = [
196
- path.join(homeDir, ".cursor", "mcp.json")
197
- ];
263
+ const pathsToCheck = [];
264
+ pathsToCheck.push(path.join(homeDir, ".cursor", "mcp.json"));
265
+ if (isWsl && wslHostHome) {
266
+ pathsToCheck.push(path.join(wslHostHome, ".cursor", "mcp.json"));
267
+ pathsToCheck.push(path.join(wslHostHome, "AppData", "Roaming", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"));
268
+ }
198
269
  if (isWin) {
199
270
  pathsToCheck.push(path.join(homeDir, "AppData", "Roaming", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"));
200
271
  } else if (process2.platform === "darwin") {
201
272
  pathsToCheck.push(path.join(homeDir, "Library", "Application Support", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"));
202
- } else {
273
+ } else if (!isWsl) {
203
274
  pathsToCheck.push(path.join(homeDir, ".config", "Cursor", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"));
204
275
  }
205
276
  let updatedCount = 0;
@@ -209,7 +280,8 @@ async function initMcp() {
209
280
  if (existsSync(configPath)) {
210
281
  const content = await fs.readFile(configPath, "utf-8");
211
282
  try {
212
- configData = JSON.parse(content);
283
+ const sanitizedContent = content.replace(/,\s*([\]}])/g, "$1");
284
+ configData = JSON.parse(sanitizedContent);
213
285
  } catch (e) {
214
286
  console.log(`Warning: Could not parse ${configPath}. Creating new object.`);
215
287
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bridge.ts","../src/server.ts","../src/cli.ts"],"sourcesContent":["import { chromium, Browser, Page } from 'playwright';\n\nexport interface WebMcpToolInfo {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport class PlaywrightBridge {\n private browser: Browser | null = null;\n public page: Page | null = null;\n\n constructor(\n private targetUrl: string,\n private privateKey?: string\n ) {}\n\n async connect() {\n console.error(`[PlaywrightBridge] Launching browser...`);\n this.browser = await chromium.launch({ headless: true });\n \n const context = await this.browser.newContext();\n this.page = await context.newPage();\n\n console.error(`[PlaywrightBridge] Navigating to ${this.targetUrl}...`);\n await this.page.goto(this.targetUrl, { waitUntil: 'domcontentloaded' });\n\n // Try to handle Crypto Auth Wall if it exists\n if (this.privateKey) {\n try {\n // We wait a bit to see if an auth input appears.\n // In the future, this selector should match the OlonJS AdminGuard.\n const authInputSelector = 'input[type=\"password\"]'; \n const authButtonSelector = 'button:has-text(\"Authenticate\")';\n\n const isAuthWall = await this.page.locator(authInputSelector).isVisible({ timeout: 2000 }).catch(() => false);\n \n if (isAuthWall) {\n console.error(`[PlaywrightBridge] Auth wall detected. Injecting private key...`);\n await this.page.fill(authInputSelector, this.privateKey);\n await this.page.click(authButtonSelector);\n }\n } catch (err) {\n console.error(`[PlaywrightBridge] Error during auth bypass:`, err);\n }\n }\n\n console.error(`[PlaywrightBridge] Waiting for WebMCP to initialize...`);\n \n // Wait for the navigator.modelContextProtocol to become available\n await this.page.waitForFunction(() => {\n return (window.navigator as any).modelContextProtocol !== undefined;\n }, undefined, { timeout: 15000 }).catch(() => {\n throw new Error(\"Timeout waiting for navigator.modelContextProtocol. Ensure you are navigating to an OlonJS Studio route (e.g. /admin) and WebMCP is enabled.\");\n });\n\n console.error(`[PlaywrightBridge] Connected to WebMCP successfully.`);\n }\n\n async listTools(): Promise<WebMcpToolInfo[]> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(() => {\n return (window.navigator as any).modelContextProtocol.listTools();\n });\n }\n\n async readResource(uri: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async (targetUri) => {\n return (window.navigator as any).modelContextProtocol.readResource(targetUri);\n }, uri);\n }\n\n async executeTool(toolName: string, inputArgsJson: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async ({ name, args }) => {\n return (window.navigator as any).modelContextProtocol.executeTool(name, args);\n }, { name: toolName, args: inputArgsJson });\n }\n\n async disconnect() {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n this.page = null;\n console.error(`[PlaywrightBridge] Disconnected.`);\n }\n }\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ReadResourceRequestSchema\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { PlaywrightBridge } from \"./bridge.js\";\n\nexport class OlonJsMcpServer {\n private server: Server;\n\n constructor(private bridge: PlaywrightBridge) {\n this.server = new Server(\n {\n name: \"@olonjs/mcp\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n resources: {}, // Enabling resources so Agent can read olon:// URIs\n },\n }\n );\n\n this.setupHandlers();\n \n // Error handling\n this.server.onerror = (error) => console.error(\"[MCP Error]\", error);\n process.on(\"SIGINT\", async () => {\n await this.bridge.disconnect();\n await this.server.close();\n process.exit(0);\n });\n }\n\n private setupHandlers() {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n try {\n const webMcpTools = await this.bridge.listTools();\n return {\n tools: webMcpTools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as any, // MCP SDK expects specific types but general JSON schema is supported\n })),\n };\n } catch (err: any) {\n console.error(\"Failed to list tools:\", err);\n return { tools: [] };\n }\n });\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n try {\n const { name, arguments: args } = request.params;\n const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));\n const parsed = JSON.parse(resultString);\n \n return {\n content: parsed.content || [{ type: \"text\", text: resultString }],\n isError: parsed.isError || false,\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\", text: `Error calling tool: ${err.message}` }],\n isError: true,\n };\n }\n });\n\n this.server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: [\n {\n uri: \"olon://pages/home\",\n name: \"Home Page Data\",\n description: \"JSON data of the Home page. To fetch other pages, append the slug, e.g. olon://pages/about\",\n mimeType: \"application/json\",\n }\n ]\n }\n });\n\n this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n try {\n const uri = request.params.uri;\n if (!uri.startsWith(\"olon://\")) {\n throw new Error(`Unsupported URI schema: ${uri}. Only olon:// is supported.`);\n }\n\n const dataString = await this.bridge.readResource(uri);\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: dataString,\n },\n ],\n };\n } catch (err: any) {\n console.error(\"Failed to read resource:\", err);\n throw err;\n }\n });\n }\n\n async run() {\n console.error(\"[OlonJsMcpServer] Connecting to WebMCP via Playwright...\");\n await this.bridge.connect();\n \n console.error(\"[OlonJsMcpServer] WebMCP connected. Starting STDIO transport...\");\n const transport = new StdioServerTransport();\n await this.server.connect(transport);\n console.error(\"[OlonJsMcpServer] MCP Server is running and listening on STDIO.\");\n }\n}\n","#!/usr/bin/env node\r\nimport { PlaywrightBridge } from './bridge.js';\r\nimport { OlonJsMcpServer } from './server.js';\r\nimport * as process from 'node:process';\r\nimport * as fs from 'node:fs/promises';\r\nimport { existsSync, mkdirSync } from 'node:fs';\r\nimport * as path from 'node:path';\r\nimport * as os from 'node:os';\r\n\r\nasync function initMcp() {\r\n console.log('\\n[OlonJs MCP] Initializing Cursor MCP settings...\\n');\r\n \r\n const isWin = process.platform === 'win32';\r\n const commandName = isWin ? 'npx.cmd' : 'npx';\r\n \r\n const mcpConfig = {\r\n command: commandName,\r\n args: ['-y', '@olonjs/mcp@latest', 'http://localhost:5174']\r\n };\r\n\r\n const homeDir = os.homedir();\r\n const pathsToCheck = [\r\n path.join(homeDir, '.cursor', 'mcp.json'),\r\n ];\r\n\r\n if (isWin) {\r\n pathsToCheck.push(path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\r\n } else if (process.platform === 'darwin') {\r\n pathsToCheck.push(path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\r\n } else {\r\n pathsToCheck.push(path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\r\n }\r\n\r\n let updatedCount = 0;\r\n\r\n for (const configPath of pathsToCheck) {\r\n try {\r\n let configData: any = { mcpServers: {} };\r\n \r\n if (existsSync(configPath)) {\r\n const content = await fs.readFile(configPath, 'utf-8');\r\n try {\r\n configData = JSON.parse(content);\r\n } catch (e) {\r\n console.log(`Warning: Could not parse ${configPath}. Creating new object.`);\r\n }\r\n } else {\r\n const dir = path.dirname(configPath);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n }\r\n\r\n if (!configData.mcpServers) {\r\n configData.mcpServers = {};\r\n }\r\n\r\n configData.mcpServers['OlonJS'] = mcpConfig;\r\n\r\n await fs.writeFile(configPath, JSON.stringify(configData, null, 2));\r\n console.log(`✓ Updated MCP configuration at: ${configPath}`);\r\n updatedCount++;\r\n } catch (err) {\r\n console.log(`Skipped ${configPath} (not found or accessible)`);\r\n }\r\n }\r\n\r\n if (updatedCount === 0) {\r\n console.log('\\nCould not find any Cursor MCP configuration files to update.');\r\n console.log('You can manually add the following JSON to your MCP settings:');\r\n console.log(JSON.stringify({ \"OlonJS\": mcpConfig }, null, 2));\r\n } else {\r\n console.log('\\nOlonJS MCP configured successfully!');\r\n console.log('Please restart Cursor (or run \"Developer: Reload Window\") to apply the changes.');\r\n }\r\n}\r\n\r\nasync function main() {\r\n const targetUrl = process.argv[2];\r\n \r\n if (targetUrl === 'init') {\r\n await initMcp();\r\n process.exit(0);\r\n }\r\n\r\n if (!targetUrl) {\r\n console.error('====================================================');\r\n console.error('❌ Missing Target URL');\r\n console.error('Usage: npx @olonjs/mcp <target-url>');\r\n console.error('Or setup: npx @olonjs/mcp init');\r\n console.error('Example: npx @olonjs/mcp http://localhost:5173');\r\n console.error('====================================================');\r\n process.exit(1);\r\n }\r\n\r\n // Normalize URL to always point to /admin to access WebMCP\r\n const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\\/$/, '')}/admin`;\r\n\r\n const privateKey = process.env.OLONJS_PRIVATE_KEY;\r\n\r\n if (privateKey) {\r\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);\r\n } else {\r\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);\r\n }\r\n\r\n try {\r\n const bridge = new PlaywrightBridge(adminUrl, privateKey);\r\n const server = new OlonJsMcpServer(bridge);\r\n \r\n await server.run();\r\n } catch (error) {\r\n console.error('[OlonJs MCP] Fatal error starting server:', error);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nmain();\r\n"],"mappings":"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","names":["process"]}
1
+ {"version":3,"sources":["../src/bridge.ts","../src/server.ts","../src/cli.ts"],"sourcesContent":["import { chromium, Browser, Page } from 'playwright';\n\nexport interface WebMcpToolInfo {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport class PlaywrightBridge {\n private browser: Browser | null = null;\n public page: Page | null = null;\n\n constructor(\n public targetUrl: string,\n private privateKey?: string\n ) {}\n\n async connect() {\n console.error(`[PlaywrightBridge] Launching browser...`);\n this.browser = await chromium.launch({ headless: true });\n \n const context = await this.browser.newContext();\n this.page = await context.newPage();\n\n console.error(`[PlaywrightBridge] Navigating to ${this.targetUrl}...`);\n await this.page.goto(this.targetUrl, { waitUntil: 'domcontentloaded' });\n\n // Try to handle Crypto Auth Wall if it exists\n if (this.privateKey) {\n try {\n // We wait a bit to see if an auth input appears.\n // In the future, this selector should match the OlonJS AdminGuard.\n const authInputSelector = 'input[type=\"password\"]'; \n const authButtonSelector = 'button:has-text(\"Authenticate\")';\n\n const isAuthWall = await this.page.locator(authInputSelector).isVisible({ timeout: 2000 }).catch(() => false);\n \n if (isAuthWall) {\n console.error(`[PlaywrightBridge] Auth wall detected. Injecting private key...`);\n await this.page.fill(authInputSelector, this.privateKey);\n await this.page.click(authButtonSelector);\n }\n } catch (err) {\n console.error(`[PlaywrightBridge] Error during auth bypass:`, err);\n }\n }\n\n console.error(`[PlaywrightBridge] Waiting for WebMCP to initialize...`);\n \n // Wait for the navigator.modelContextProtocol to become available\n await this.page.waitForFunction(() => {\n return (window.navigator as any).modelContextProtocol !== undefined;\n }, undefined, { timeout: 15000 }).catch(() => {\n throw new Error(\"Timeout waiting for navigator.modelContextProtocol. Ensure you are navigating to an OlonJS Studio route (e.g. /admin) and WebMCP is enabled.\");\n });\n\n console.error(`[PlaywrightBridge] Connected to WebMCP successfully.`);\n }\n\n async listTools(): Promise<WebMcpToolInfo[]> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(() => {\n return (window.navigator as any).modelContextProtocol.listTools();\n });\n }\n\n async readResource(uri: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async (targetUri) => {\n return (window.navigator as any).modelContextProtocol.readResource(targetUri);\n }, uri);\n }\n\n async executeTool(toolName: string, inputArgsJson: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async ({ name, args }) => {\n return (window.navigator as any).modelContextProtocol.executeTool(name, args);\n }, { name: toolName, args: inputArgsJson });\n }\n\n async navigateTo(slug: string): Promise<void> {\n if (!this.page) throw new Error(\"Not connected\");\n const baseUrl = this.targetUrl.replace(/\\/admin.*$/, '');\n const targetUrl = `${baseUrl}/admin/${slug}`;\n console.error(`[PlaywrightBridge] Navigating to ${targetUrl}...`);\n await this.page.goto(targetUrl, { waitUntil: 'domcontentloaded' });\n await this.page.waitForFunction(() => {\n return (window.navigator as any).modelContextProtocol !== undefined;\n }, undefined, { timeout: 15000 }).catch(() => {\n throw new Error(`Timeout waiting for WebMCP on /admin/${slug}.`);\n });\n console.error(`[PlaywrightBridge] Now on slug: ${slug}`);\n }\n\n async disconnect() {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n this.page = null;\n console.error(`[PlaywrightBridge] Disconnected.`);\n }\n }\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ReadResourceRequestSchema\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { PlaywrightBridge } from \"./bridge.js\";\n\nexport class OlonJsMcpServer {\n private server: Server;\n\n constructor(private bridge: PlaywrightBridge) {\n this.server = new Server(\n {\n name: \"@olonjs/mcp\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n resources: {}, // Enabling resources so Agent can read olon:// URIs\n },\n }\n );\n\n this.setupHandlers();\n \n // Error handling\n this.server.onerror = (error) => console.error(\"[MCP Error]\", error);\n process.on(\"SIGINT\", async () => {\n await this.bridge.disconnect();\n await this.server.close();\n process.exit(0);\n });\n }\n\n private setupHandlers() {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n try {\n const webMcpTools = await this.bridge.listTools();\n return {\n tools: [\n {\n name: \"navigate-to-page\",\n description: \"Navigate the Studio Playwright browser to a specific page by slug. Call this before update-section whenever you need to edit a page different from the current one.\",\n inputSchema: {\n type: \"object\",\n properties: {\n slug: {\n type: \"string\",\n description: \"The page slug to navigate to (e.g. 'chi-sono', 'home', 'contatti').\",\n },\n },\n required: [\"slug\"],\n additionalProperties: false,\n },\n },\n ...webMcpTools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as any,\n })),\n ],\n };\n } catch (err: any) {\n console.error(\"Failed to list tools:\", err);\n return { tools: [] };\n }\n });\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n try {\n const { name, arguments: args } = request.params;\n\n if (name === \"navigate-to-page\") {\n const slug = (args as any)?.slug;\n if (!slug) throw new Error(\"Missing required parameter: slug\");\n await this.bridge.navigateTo(slug);\n return {\n content: [{ type: \"text\", text: `Navigated to /admin/${slug}. Studio is now on slug \"${slug}\".` }],\n isError: false,\n };\n }\n\n const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));\n const parsed = JSON.parse(resultString);\n \n return {\n content: parsed.content || [{ type: \"text\", text: resultString }],\n isError: parsed.isError || false,\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\", text: `Error calling tool: ${err.message}` }],\n isError: true,\n };\n }\n });\n\n this.server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: [\n {\n uri: \"olon://pages/home\",\n name: \"Home Page Data\",\n description: \"JSON data of the Home page. To fetch other pages, append the slug, e.g. olon://pages/about\",\n mimeType: \"application/json\",\n }\n ]\n }\n });\n\n this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n try {\n const uri = request.params.uri;\n if (!uri.startsWith(\"olon://\")) {\n throw new Error(`Unsupported URI schema: ${uri}. Only olon:// is supported.`);\n }\n\n const dataString = await this.bridge.readResource(uri);\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: dataString,\n },\n ],\n };\n } catch (err: any) {\n console.error(\"Failed to read resource:\", err);\n throw err;\n }\n });\n }\n\n async run() {\n console.error(\"[OlonJsMcpServer] Connecting to WebMCP via Playwright...\");\n await this.bridge.connect();\n \n console.error(\"[OlonJsMcpServer] WebMCP connected. Starting STDIO transport...\");\n const transport = new StdioServerTransport();\n await this.server.connect(transport);\n console.error(\"[OlonJsMcpServer] MCP Server is running and listening on STDIO.\");\n }\n}\n","#!/usr/bin/env node\nimport { PlaywrightBridge } from './bridge.js';\nimport { OlonJsMcpServer } from './server.js';\nimport * as process from 'node:process';\nimport * as fs from 'node:fs/promises';\nimport { existsSync, mkdirSync } from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { execSync } from 'node:child_process';\n\nasync function initMcp() {\n console.log('\\n[OlonJs MCP] Initializing Cursor MCP settings...\\n');\n \n let isWin = process.platform === 'win32';\n let isWsl = false;\n let wslHostHome = '';\n\n // Detect WSL\n if (process.platform === 'linux' && process.env.WSL_DISTRO_NAME) {\n isWsl = true;\n try {\n // Find the Windows user profile path from within WSL\n const winProfile = execSync('cmd.exe /c \"echo %USERPROFILE%\"').toString().trim();\n // Convert C:\\Users\\name to /mnt/c/Users/name\n if (winProfile && winProfile.includes(':\\\\')) {\n const drive = winProfile.charAt(0).toLowerCase();\n const rest = winProfile.slice(2).replace(/\\\\/g, '/');\n wslHostHome = `/mnt/${drive}${rest}`;\n }\n } catch (e) {\n console.log('Warning: Detected WSL but could not resolve Windows host profile path.');\n }\n }\n\n const commandName = isWin || isWsl ? 'wsl' : 'npx';\n const mcpConfig: any = {\n command: commandName,\n args: ['-y', '@olonjs/mcp@latest', 'http://localhost:5174']\n };\n\n // If we are configuring for a Windows host (either natively or from WSL),\n // we must setup the wsl bridge arguments so the host Windows Cursor can reach the Linux npx.\n if (isWsl) {\n const distro = process.env.WSL_DISTRO_NAME || 'Ubuntu';\n mcpConfig.args = [\n '-d', distro,\n '--cd', '~',\n 'bash', '-ilc',\n 'npx -y @olonjs/mcp@latest http://localhost:5174'\n ];\n } else if (isWin) {\n mcpConfig.command = 'npx.cmd';\n }\n\n const homeDir = os.homedir();\n const pathsToCheck: string[] = [];\n\n // 1. Add native path\n pathsToCheck.push(path.join(homeDir, '.cursor', 'mcp.json'));\n\n // 2. Add WSL host path if applicable\n if (isWsl && wslHostHome) {\n pathsToCheck.push(path.join(wslHostHome, '.cursor', 'mcp.json'));\n pathsToCheck.push(path.join(wslHostHome, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\n }\n\n if (isWin) {\n pathsToCheck.push(path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\n } else if (process.platform === 'darwin') {\n pathsToCheck.push(path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\n } else if (!isWsl) {\n pathsToCheck.push(path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));\n }\n\n let updatedCount = 0;\n\n for (const configPath of pathsToCheck) {\n try {\n let configData: any = { mcpServers: {} };\n \n if (existsSync(configPath)) {\n const content = await fs.readFile(configPath, 'utf-8');\n try {\n // Strip trailing commas before parsing just in case\n const sanitizedContent = content.replace(/,\\s*([\\]}])/g, '$1');\n configData = JSON.parse(sanitizedContent);\n } catch (e) {\n console.log(`Warning: Could not parse ${configPath}. Creating new object.`);\n }\n } else {\n const dir = path.dirname(configPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n if (!configData.mcpServers) {\n configData.mcpServers = {};\n }\n\n configData.mcpServers['OlonJS'] = mcpConfig;\n\n await fs.writeFile(configPath, JSON.stringify(configData, null, 2));\n console.log(`✓ Updated MCP configuration at: ${configPath}`);\n updatedCount++;\n } catch (err) {\n console.log(`Skipped ${configPath} (not found or accessible)`);\n }\n }\n\n if (updatedCount === 0) {\n console.log('\\nCould not find any Cursor MCP configuration files to update.');\n console.log('You can manually add the following JSON to your MCP settings:');\n console.log(JSON.stringify({ \"OlonJS\": mcpConfig }, null, 2));\n } else {\n console.log('\\nOlonJS MCP configured successfully!');\n console.log('Please restart Cursor (or run \"Developer: Reload Window\") to apply the changes.');\n }\n}\n\nasync function main() {\n const targetUrl = process.argv[2];\n \n if (targetUrl === 'init') {\n await initMcp();\n process.exit(0);\n }\n\n if (!targetUrl) {\n console.error('====================================================');\n console.error('❌ Missing Target URL');\n console.error('Usage: npx @olonjs/mcp <target-url>');\n console.error('Or setup: npx @olonjs/mcp init');\n console.error('Example: npx @olonjs/mcp http://localhost:5173');\n console.error('====================================================');\n process.exit(1);\n }\n\n // Normalize URL to always point to /admin to access WebMCP\n const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\\/$/, '')}/admin`;\n\n const privateKey = process.env.OLONJS_PRIVATE_KEY;\n\n if (privateKey) {\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);\n } else {\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);\n }\n\n try {\n const bridge = new PlaywrightBridge(adminUrl, privateKey);\n const server = new OlonJsMcpServer(bridge);\n \n await server.run();\n } catch (error) {\n console.error('[OlonJs MCP] Fatal error starting server:', error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["process"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olonjs/mcp",
3
- "version": "1.0.106",
3
+ "version": "1.0.108",
4
4
  "description": "Local MCP server bridge for OlonJS Tenant Zero-UI automation via Playwright",
5
5
  "private": false,
6
6
  "type": "module",
package/src/bridge.ts CHANGED
@@ -11,7 +11,7 @@ export class PlaywrightBridge {
11
11
  public page: Page | null = null;
12
12
 
13
13
  constructor(
14
- private targetUrl: string,
14
+ public targetUrl: string,
15
15
  private privateKey?: string
16
16
  ) {}
17
17
 
@@ -78,6 +78,20 @@ export class PlaywrightBridge {
78
78
  }, { name: toolName, args: inputArgsJson });
79
79
  }
80
80
 
81
+ async navigateTo(slug: string): Promise<void> {
82
+ if (!this.page) throw new Error("Not connected");
83
+ const baseUrl = this.targetUrl.replace(/\/admin.*$/, '');
84
+ const targetUrl = `${baseUrl}/admin/${slug}`;
85
+ console.error(`[PlaywrightBridge] Navigating to ${targetUrl}...`);
86
+ await this.page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
87
+ await this.page.waitForFunction(() => {
88
+ return (window.navigator as any).modelContextProtocol !== undefined;
89
+ }, undefined, { timeout: 15000 }).catch(() => {
90
+ throw new Error(`Timeout waiting for WebMCP on /admin/${slug}.`);
91
+ });
92
+ console.error(`[PlaywrightBridge] Now on slug: ${slug}`);
93
+ }
94
+
81
95
  async disconnect() {
82
96
  if (this.browser) {
83
97
  await this.browser.close();
package/src/cli.ts CHANGED
@@ -1,118 +1,161 @@
1
- #!/usr/bin/env node
2
- import { PlaywrightBridge } from './bridge.js';
3
- import { OlonJsMcpServer } from './server.js';
4
- import * as process from 'node:process';
5
- import * as fs from 'node:fs/promises';
6
- import { existsSync, mkdirSync } from 'node:fs';
7
- import * as path from 'node:path';
8
- import * as os from 'node:os';
9
-
10
- async function initMcp() {
11
- console.log('\n[OlonJs MCP] Initializing Cursor MCP settings...\n');
12
-
13
- const isWin = process.platform === 'win32';
14
- const commandName = isWin ? 'npx.cmd' : 'npx';
15
-
16
- const mcpConfig = {
17
- command: commandName,
18
- args: ['-y', '@olonjs/mcp@latest', 'http://localhost:5174']
19
- };
20
-
21
- const homeDir = os.homedir();
22
- const pathsToCheck = [
23
- path.join(homeDir, '.cursor', 'mcp.json'),
24
- ];
25
-
26
- if (isWin) {
27
- pathsToCheck.push(path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
28
- } else if (process.platform === 'darwin') {
29
- pathsToCheck.push(path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
30
- } else {
31
- pathsToCheck.push(path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
32
- }
33
-
34
- let updatedCount = 0;
35
-
36
- for (const configPath of pathsToCheck) {
37
- try {
38
- let configData: any = { mcpServers: {} };
39
-
40
- if (existsSync(configPath)) {
41
- const content = await fs.readFile(configPath, 'utf-8');
42
- try {
43
- configData = JSON.parse(content);
44
- } catch (e) {
45
- console.log(`Warning: Could not parse ${configPath}. Creating new object.`);
46
- }
47
- } else {
48
- const dir = path.dirname(configPath);
49
- if (!existsSync(dir)) {
50
- mkdirSync(dir, { recursive: true });
51
- }
52
- }
53
-
54
- if (!configData.mcpServers) {
55
- configData.mcpServers = {};
56
- }
57
-
58
- configData.mcpServers['OlonJS'] = mcpConfig;
59
-
60
- await fs.writeFile(configPath, JSON.stringify(configData, null, 2));
61
- console.log(`✓ Updated MCP configuration at: ${configPath}`);
62
- updatedCount++;
63
- } catch (err) {
64
- console.log(`Skipped ${configPath} (not found or accessible)`);
65
- }
66
- }
67
-
68
- if (updatedCount === 0) {
69
- console.log('\nCould not find any Cursor MCP configuration files to update.');
70
- console.log('You can manually add the following JSON to your MCP settings:');
71
- console.log(JSON.stringify({ "OlonJS": mcpConfig }, null, 2));
72
- } else {
73
- console.log('\nOlonJS MCP configured successfully!');
74
- console.log('Please restart Cursor (or run "Developer: Reload Window") to apply the changes.');
75
- }
76
- }
77
-
78
- async function main() {
79
- const targetUrl = process.argv[2];
80
-
81
- if (targetUrl === 'init') {
82
- await initMcp();
83
- process.exit(0);
84
- }
85
-
86
- if (!targetUrl) {
87
- console.error('====================================================');
88
- console.error('❌ Missing Target URL');
89
- console.error('Usage: npx @olonjs/mcp <target-url>');
90
- console.error('Or setup: npx @olonjs/mcp init');
91
- console.error('Example: npx @olonjs/mcp http://localhost:5173');
92
- console.error('====================================================');
93
- process.exit(1);
94
- }
95
-
96
- // Normalize URL to always point to /admin to access WebMCP
97
- const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\/$/, '')}/admin`;
98
-
99
- const privateKey = process.env.OLONJS_PRIVATE_KEY;
100
-
101
- if (privateKey) {
102
- console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);
103
- } else {
104
- console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);
105
- }
106
-
107
- try {
108
- const bridge = new PlaywrightBridge(adminUrl, privateKey);
109
- const server = new OlonJsMcpServer(bridge);
110
-
111
- await server.run();
112
- } catch (error) {
113
- console.error('[OlonJs MCP] Fatal error starting server:', error);
114
- process.exit(1);
115
- }
116
- }
117
-
118
- main();
1
+ #!/usr/bin/env node
2
+ import { PlaywrightBridge } from './bridge.js';
3
+ import { OlonJsMcpServer } from './server.js';
4
+ import * as process from 'node:process';
5
+ import * as fs from 'node:fs/promises';
6
+ import { existsSync, mkdirSync } from 'node:fs';
7
+ import * as path from 'node:path';
8
+ import * as os from 'node:os';
9
+ import { execSync } from 'node:child_process';
10
+
11
+ async function initMcp() {
12
+ console.log('\n[OlonJs MCP] Initializing Cursor MCP settings...\n');
13
+
14
+ let isWin = process.platform === 'win32';
15
+ let isWsl = false;
16
+ let wslHostHome = '';
17
+
18
+ // Detect WSL
19
+ if (process.platform === 'linux' && process.env.WSL_DISTRO_NAME) {
20
+ isWsl = true;
21
+ try {
22
+ // Find the Windows user profile path from within WSL
23
+ const winProfile = execSync('cmd.exe /c "echo %USERPROFILE%"').toString().trim();
24
+ // Convert C:\Users\name to /mnt/c/Users/name
25
+ if (winProfile && winProfile.includes(':\\')) {
26
+ const drive = winProfile.charAt(0).toLowerCase();
27
+ const rest = winProfile.slice(2).replace(/\\/g, '/');
28
+ wslHostHome = `/mnt/${drive}${rest}`;
29
+ }
30
+ } catch (e) {
31
+ console.log('Warning: Detected WSL but could not resolve Windows host profile path.');
32
+ }
33
+ }
34
+
35
+ const commandName = isWin || isWsl ? 'wsl' : 'npx';
36
+ const mcpConfig: any = {
37
+ command: commandName,
38
+ args: ['-y', '@olonjs/mcp@latest', 'http://localhost:5174']
39
+ };
40
+
41
+ // If we are configuring for a Windows host (either natively or from WSL),
42
+ // we must setup the wsl bridge arguments so the host Windows Cursor can reach the Linux npx.
43
+ if (isWsl) {
44
+ const distro = process.env.WSL_DISTRO_NAME || 'Ubuntu';
45
+ mcpConfig.args = [
46
+ '-d', distro,
47
+ '--cd', '~',
48
+ 'bash', '-ilc',
49
+ 'npx -y @olonjs/mcp@latest http://localhost:5174'
50
+ ];
51
+ } else if (isWin) {
52
+ mcpConfig.command = 'npx.cmd';
53
+ }
54
+
55
+ const homeDir = os.homedir();
56
+ const pathsToCheck: string[] = [];
57
+
58
+ // 1. Add native path
59
+ pathsToCheck.push(path.join(homeDir, '.cursor', 'mcp.json'));
60
+
61
+ // 2. Add WSL host path if applicable
62
+ if (isWsl && wslHostHome) {
63
+ pathsToCheck.push(path.join(wslHostHome, '.cursor', 'mcp.json'));
64
+ pathsToCheck.push(path.join(wslHostHome, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
65
+ }
66
+
67
+ if (isWin) {
68
+ pathsToCheck.push(path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
69
+ } else if (process.platform === 'darwin') {
70
+ pathsToCheck.push(path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
71
+ } else if (!isWsl) {
72
+ pathsToCheck.push(path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'));
73
+ }
74
+
75
+ let updatedCount = 0;
76
+
77
+ for (const configPath of pathsToCheck) {
78
+ try {
79
+ let configData: any = { mcpServers: {} };
80
+
81
+ if (existsSync(configPath)) {
82
+ const content = await fs.readFile(configPath, 'utf-8');
83
+ try {
84
+ // Strip trailing commas before parsing just in case
85
+ const sanitizedContent = content.replace(/,\s*([\]}])/g, '$1');
86
+ configData = JSON.parse(sanitizedContent);
87
+ } catch (e) {
88
+ console.log(`Warning: Could not parse ${configPath}. Creating new object.`);
89
+ }
90
+ } else {
91
+ const dir = path.dirname(configPath);
92
+ if (!existsSync(dir)) {
93
+ mkdirSync(dir, { recursive: true });
94
+ }
95
+ }
96
+
97
+ if (!configData.mcpServers) {
98
+ configData.mcpServers = {};
99
+ }
100
+
101
+ configData.mcpServers['OlonJS'] = mcpConfig;
102
+
103
+ await fs.writeFile(configPath, JSON.stringify(configData, null, 2));
104
+ console.log(`✓ Updated MCP configuration at: ${configPath}`);
105
+ updatedCount++;
106
+ } catch (err) {
107
+ console.log(`Skipped ${configPath} (not found or accessible)`);
108
+ }
109
+ }
110
+
111
+ if (updatedCount === 0) {
112
+ console.log('\nCould not find any Cursor MCP configuration files to update.');
113
+ console.log('You can manually add the following JSON to your MCP settings:');
114
+ console.log(JSON.stringify({ "OlonJS": mcpConfig }, null, 2));
115
+ } else {
116
+ console.log('\nOlonJS MCP configured successfully!');
117
+ console.log('Please restart Cursor (or run "Developer: Reload Window") to apply the changes.');
118
+ }
119
+ }
120
+
121
+ async function main() {
122
+ const targetUrl = process.argv[2];
123
+
124
+ if (targetUrl === 'init') {
125
+ await initMcp();
126
+ process.exit(0);
127
+ }
128
+
129
+ if (!targetUrl) {
130
+ console.error('====================================================');
131
+ console.error('❌ Missing Target URL');
132
+ console.error('Usage: npx @olonjs/mcp <target-url>');
133
+ console.error('Or setup: npx @olonjs/mcp init');
134
+ console.error('Example: npx @olonjs/mcp http://localhost:5173');
135
+ console.error('====================================================');
136
+ process.exit(1);
137
+ }
138
+
139
+ // Normalize URL to always point to /admin to access WebMCP
140
+ const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\/$/, '')}/admin`;
141
+
142
+ const privateKey = process.env.OLONJS_PRIVATE_KEY;
143
+
144
+ if (privateKey) {
145
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);
146
+ } else {
147
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);
148
+ }
149
+
150
+ try {
151
+ const bridge = new PlaywrightBridge(adminUrl, privateKey);
152
+ const server = new OlonJsMcpServer(bridge);
153
+
154
+ await server.run();
155
+ } catch (error) {
156
+ console.error('[OlonJs MCP] Fatal error starting server:', error);
157
+ process.exit(1);
158
+ }
159
+ }
160
+
161
+ main();
package/src/server.ts CHANGED
@@ -41,11 +41,28 @@ export class OlonJsMcpServer {
41
41
  try {
42
42
  const webMcpTools = await this.bridge.listTools();
43
43
  return {
44
- tools: webMcpTools.map((t) => ({
45
- name: t.name,
46
- description: t.description,
47
- inputSchema: t.inputSchema as any, // MCP SDK expects specific types but general JSON schema is supported
48
- })),
44
+ tools: [
45
+ {
46
+ name: "navigate-to-page",
47
+ description: "Navigate the Studio Playwright browser to a specific page by slug. Call this before update-section whenever you need to edit a page different from the current one.",
48
+ inputSchema: {
49
+ type: "object",
50
+ properties: {
51
+ slug: {
52
+ type: "string",
53
+ description: "The page slug to navigate to (e.g. 'chi-sono', 'home', 'contatti').",
54
+ },
55
+ },
56
+ required: ["slug"],
57
+ additionalProperties: false,
58
+ },
59
+ },
60
+ ...webMcpTools.map((t) => ({
61
+ name: t.name,
62
+ description: t.description,
63
+ inputSchema: t.inputSchema as any,
64
+ })),
65
+ ],
49
66
  };
50
67
  } catch (err: any) {
51
68
  console.error("Failed to list tools:", err);
@@ -56,6 +73,17 @@ export class OlonJsMcpServer {
56
73
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
57
74
  try {
58
75
  const { name, arguments: args } = request.params;
76
+
77
+ if (name === "navigate-to-page") {
78
+ const slug = (args as any)?.slug;
79
+ if (!slug) throw new Error("Missing required parameter: slug");
80
+ await this.bridge.navigateTo(slug);
81
+ return {
82
+ content: [{ type: "text", text: `Navigated to /admin/${slug}. Studio is now on slug "${slug}".` }],
83
+ isError: false,
84
+ };
85
+ }
86
+
59
87
  const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));
60
88
  const parsed = JSON.parse(resultString);
61
89