From db752da7a797aaea6a5935192bc4c2880efc3e13 Mon Sep 17 00:00:00 2001 From: Nicholas Tindle Date: Thu, 14 Aug 2025 15:00:47 -0500 Subject: [PATCH 1/2] feat: add Microsoft Edge AppleScript example for macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new Desktop Extension example that provides MCP tools for controlling Microsoft Edge browser on macOS through AppleScript API. Features: - Open URLs in new or current tabs - Get current tab information - List all open tabs - Close, switch, and reload tabs - Navigate browser history (back/forward) - Execute JavaScript in tabs with result capture - Retrieve page text content This extension demonstrates cross-browser MCP capabilities, extending the existing Chrome example to support Microsoft Edge. All functions have been tested and work correctly on macOS with Edge. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- examples/edge-applescript/manifest.json | 70 ++ examples/edge-applescript/package.json | 15 + examples/edge-applescript/server/index.js | 775 ++++++++++++++++++++++ examples/edge-applescript/yarn.lock | 90 +++ 4 files changed, 950 insertions(+) create mode 100644 examples/edge-applescript/manifest.json create mode 100644 examples/edge-applescript/package.json create mode 100644 examples/edge-applescript/server/index.js create mode 100644 examples/edge-applescript/yarn.lock diff --git a/examples/edge-applescript/manifest.json b/examples/edge-applescript/manifest.json new file mode 100644 index 0000000..cec3edf --- /dev/null +++ b/examples/edge-applescript/manifest.json @@ -0,0 +1,70 @@ +{ + "$schema": "../../dist/dxt-manifest.schema.json", + "dxt_version": "0.1", + "name": "edge-applescript", + "display_name": "Control Edge with AppleScript", + "version": "0.1.0", + "description": "Control Microsoft Edge browser tabs, windows, and navigation", + "long_description": "This extension allows you to control Microsoft Edge browser tabs, windows, and navigation through Edge's AppleScript API on macOS. You can open URLs, manage tabs, execute JavaScript, and retrieve page content.\n\nThis extension is meant to be a demonstration of Desktop Extensions.\n\nThis extension is not affiliated with Microsoft or Edge.", + "author": { + "name": "Anthropic", + "email": "support@anthropic.com", + "url": "https://github.com/anthropics" + }, + "server": { + "type": "node", + "entry_point": "server/index.js", + "mcp_config": { + "command": "node", + "args": ["${__dirname}/server/index.js"], + "env": {} + } + }, + "tools": [ + { + "name": "open_url", + "description": "Open a URL in Edge, either in a new tab or current tab." + }, + { + "name": "get_current_tab", + "description": "Get information about the current active tab." + }, + { + "name": "list_tabs", + "description": "List all open tabs in Edge." + }, + { + "name": "close_tab", + "description": "Close a specific tab." + }, + { + "name": "switch_to_tab", + "description": "Switch to a specific tab." + }, + { + "name": "reload_tab", + "description": "Reload a tab." + }, + { + "name": "go_back", + "description": "Navigate back in browser history." + }, + { + "name": "go_forward", + "description": "Navigate forward in browser history." + }, + { + "name": "execute_javascript", + "description": "Execute JavaScript in the current tab." + }, + { + "name": "get_page_content", + "description": "Get the text content of the current page." + } + ], + "compatibility": { + "platforms": ["darwin"] + }, + "keywords": ["edge", "browser", "remote", "microsoft"], + "license": "MIT" +} \ No newline at end of file diff --git a/examples/edge-applescript/package.json b/examples/edge-applescript/package.json new file mode 100644 index 0000000..13346b1 --- /dev/null +++ b/examples/edge-applescript/package.json @@ -0,0 +1,15 @@ +{ + "name": "edge-applescript", + "version": "0.1.0", + "description": "Control Microsoft Edge browser tabs, windows, and navigation", + "type": "module", + "main": "server/index.js", + "scripts": { + "start": "node server/index.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^0.7.0" + }, + "author": "Anthropic", + "license": "MIT" +} \ No newline at end of file diff --git a/examples/edge-applescript/server/index.js b/examples/edge-applescript/server/index.js new file mode 100644 index 0000000..2a8f862 --- /dev/null +++ b/examples/edge-applescript/server/index.js @@ -0,0 +1,775 @@ +#!/usr/bin/env node + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { execFile } from "child_process"; +import { promisify } from "util"; + +const execFileAsync = promisify(execFile); + +// Constants +const APPLESCRIPT_TIMEOUT = 10000; // 10 seconds +const MAX_RETRIES = 3; +const RETRY_DELAY = 1000; // 1 second + +class EdgeControlServer { + constructor() { + this.server = new Server( + { + name: "edge-applescript", + version: "0.1.0", + }, + { + capabilities: { + tools: {}, + }, + }, + ); + + this.setupHandlers(); + } + + // Helper methods + escapeForAppleScript(str) { + if (typeof str !== "string") return str; + // Basic AppleScript string escaping + return str + .replace(/\\/g, "\\\\") // Escape backslashes first + .replace(/"/g, '\\"') // Then escape double quotes + .replace(/\n/g, "\\n") // Escape newlines + .replace(/\r/g, "\\r"); // Escape carriage returns + } + + async checkEdgeAvailable() { + try { + const script = 'tell application "Microsoft Edge" to return "available"'; + const result = await this.executeAppleScript(script); + return result === "available"; + } catch (error) { + return false; + } + } + + async executeAppleScript(script, retries = MAX_RETRIES) { + for (let attempt = 0; attempt <= retries; attempt++) { + try { + const { stdout, stderr } = await execFileAsync( + "osascript", + ["-e", script], + { + timeout: APPLESCRIPT_TIMEOUT, + maxBuffer: 1024 * 1024, // 1MB buffer + }, + ); + if (stderr) { + console.error("AppleScript stderr:", stderr); + } + return stdout.trim(); + } catch (error) { + if (attempt === retries) { + console.error("AppleScript execution error after retries:", error); + throw new Error(`AppleScript error: ${error.message}`); + } + // Retry with exponential backoff + await new Promise((resolve) => + setTimeout(resolve, RETRY_DELAY * Math.pow(2, attempt)), + ); + } + } + } + + async findTabById(tabId) { + const script = ` + tell application "Microsoft Edge" + repeat with w in windows + repeat with t in tabs of w + if (id of t as string) is "${tabId}" then + return {w, t, true} + end if + end repeat + end repeat + return {null, null, false} + end tell + `; + return this.executeAppleScript(script); + } + + async executeTabOperation( + tabId, + operation, + successMessage = "Operation completed", + ) { + if (!tabId) { + // No tab ID, execute on active tab + return this.executeAppleScript(operation.activeScript); + } + + const script = ` + tell application "Microsoft Edge" + repeat with w in windows + repeat with t in tabs of w + if (id of t as string) is "${tabId}" then + ${operation.tabScript} + return "${successMessage}" + end if + end repeat + end repeat + return "Tab not found" + end tell + `; + return this.executeAppleScript(script); + } + + setupHandlers() { + this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "open_url", + description: "Open a URL in Edge", + inputSchema: { + type: "object", + properties: { + url: { type: "string", description: "URL to open" }, + new_tab: { + type: "boolean", + description: "Open in a new tab", + default: true, + }, + }, + required: ["url"], + }, + }, + { + name: "get_current_tab", + description: "Get information about the current active tab", + inputSchema: { type: "object", properties: {} }, + }, + { + name: "list_tabs", + description: "List all open tabs in Edge", + inputSchema: { + type: "object", + properties: { + window_id: { + type: "number", + description: "Specific window ID to list tabs from", + }, + }, + }, + }, + { + name: "close_tab", + description: "Close a specific tab", + inputSchema: { + type: "object", + properties: { + tab_id: { type: "number", description: "ID of the tab to close" }, + }, + required: ["tab_id"], + }, + }, + { + name: "switch_to_tab", + description: "Switch to a specific tab", + inputSchema: { + type: "object", + properties: { + tab_id: { + type: "number", + description: "ID of the tab to switch to", + }, + }, + required: ["tab_id"], + }, + }, + { + name: "reload_tab", + description: "Reload a tab", + inputSchema: { + type: "object", + properties: { + tab_id: { + type: "number", + description: "ID of the tab to reload", + }, + }, + }, + }, + { + name: "go_back", + description: "Navigate back in browser history", + inputSchema: { + type: "object", + properties: { + tab_id: { type: "number", description: "ID of the tab" }, + }, + }, + }, + { + name: "go_forward", + description: "Navigate forward in browser history", + inputSchema: { + type: "object", + properties: { + tab_id: { type: "number", description: "ID of the tab" }, + }, + }, + }, + { + name: "execute_javascript", + description: "Execute JavaScript in the current tab", + inputSchema: { + type: "object", + properties: { + code: { + type: "string", + description: "JavaScript code to execute", + }, + tab_id: { type: "number", description: "ID of the tab" }, + }, + required: ["code"], + }, + }, + { + name: "get_page_content", + description: "Get the text content of the current page", + inputSchema: { + type: "object", + properties: { + tab_id: { type: "number", description: "ID of the tab" }, + }, + }, + }, + ], + })); + + this.server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + // Check Edge availability first + const isEdgeAvailable = await this.checkEdgeAvailable(); + if (!isEdgeAvailable) { + return { + content: [ + { + type: "text", + text: "Error: Microsoft Edge is not available or not running", + }, + ], + isError: true, + }; + } + + switch (name) { + case "open_url": { + const { url, new_tab = true } = args; + if (!url || typeof url !== "string") { + throw new Error("URL is required and must be a string"); + } + + const escapedUrl = this.escapeForAppleScript(url); + const script = new_tab + ? `tell application "Microsoft Edge" to open location "${escapedUrl}"` + : `tell application "Microsoft Edge" to set URL of active tab of front window to "${escapedUrl}"`; + + await this.executeAppleScript(script); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + action: new_tab + ? "opened_new_tab" + : "navigated_current_tab", + url: url, + }, + null, + 2, + ), + }, + ], + }; + } + + case "get_current_tab": { + const script = ` + tell application "Microsoft Edge" + set currentTab to active tab of front window + set tabInfo to {URL of currentTab, title of currentTab, id of currentTab} + return tabInfo + end tell + `; + const result = await this.executeAppleScript(script); + const [url, title, id] = result.split(", "); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + url, + title, + id: parseInt(id), + }, + null, + 2, + ), + }, + ], + }; + } + + case "list_tabs": { + const script = ` + tell application "Microsoft Edge" + set tabsList to {} + repeat with w in windows + repeat with t in tabs of w + set end of tabsList to {id of t as string, URL of t, title of t} + end repeat + end repeat + set AppleScript's text item delimiters to "|" + set output to "" + repeat with tabInfo in tabsList + set output to output & (item 1 of tabInfo) & "," & (item 2 of tabInfo) & "," & (item 3 of tabInfo) & "|" + end repeat + return output + end tell + `; + const result = await this.executeAppleScript(script); + const tabs = result + .split("|") + .filter((tab) => tab) + .map((tab) => { + const [id, url, title] = tab.split(","); + return { id: parseInt(id), url, title }; + }); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + tabs: tabs, + count: tabs.length, + }, + null, + 2, + ), + }, + ], + }; + } + + case "close_tab": { + const { tab_id } = args; + + const result = await this.executeTabOperation( + tab_id, + { + tabScript: "close t", + activeScript: ` + tell application "Microsoft Edge" + close active tab of front window + return "Tab closed" + end tell + `, + }, + "Tab closed", + ); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result === "Tab closed", + message: result, + }, + null, + 2, + ), + }, + ], + }; + } + + case "switch_to_tab": { + const { tab_id } = args; + + if (!tab_id) { + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: false, + error: "tab_id is required", + }, + null, + 2, + ), + }, + ], + }; + } + + // Use a direct AppleScript approach instead of executeTabOperation + const script = ` + tell application "Microsoft Edge" + set tabFound to false + repeat with w in windows + set tabIndex to 0 + repeat with t in tabs of w + set tabIndex to tabIndex + 1 + if (id of t as string) is "${tab_id}" then + set active tab index of w to tabIndex + set index of w to 1 + activate + set tabFound to true + exit repeat + end if + end repeat + if tabFound then exit repeat + end repeat + if tabFound then + return "Switched to tab" + else + return "Tab not found" + end if + end tell + `; + + const result = await this.executeAppleScript(script); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result === "Switched to tab", + message: result, + }, + null, + 2, + ), + }, + ], + }; + } + + case "reload_tab": { + const { tab_id } = args; + + const result = await this.executeTabOperation( + tab_id, + { + tabScript: "reload t", + activeScript: ` + tell application "Microsoft Edge" + reload active tab of front window + return "Tab reloaded" + end tell + `, + }, + "Tab reloaded", + ); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result !== "Tab not found", + message: result, + }, + null, + 2, + ), + }, + ], + }; + } + + case "go_back": { + const { tab_id } = args; + + const result = await this.executeTabOperation( + tab_id, + { + tabScript: "go back t", + activeScript: ` + tell application "Microsoft Edge" + go back active tab of front window + return "Navigated back" + end tell + `, + }, + "Navigated back", + ); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result !== "Tab not found", + message: result, + }, + null, + 2, + ), + }, + ], + }; + } + + case "go_forward": { + const { tab_id } = args; + + const result = await this.executeTabOperation( + tab_id, + { + tabScript: "go forward t", + activeScript: ` + tell application "Microsoft Edge" + go forward active tab of front window + return "Navigated forward" + end tell + `, + }, + "Navigated forward", + ); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result !== "Tab not found", + message: result, + }, + null, + 2, + ), + }, + ], + }; + } + + case "execute_javascript": { + const { code, tab_id } = args; + if (!code || typeof code !== "string") { + throw new Error( + "JavaScript code is required and must be a string", + ); + } + + // Edge's AppleScript may have different behavior with return values + // Let's use a more direct approach without wrapper functions + // and ensure the code explicitly returns a value + const wrappedCode = ` + (() => { + try { + let __result = ${code.includes('return') ? code : `(() => { ${code} })()`}; + if (__result === undefined) return 'undefined'; + if (__result === null) return 'null'; + if (typeof __result === 'object') { + try { return JSON.stringify(__result); } + catch(e) { return String(__result); } + } + return String(__result); + } catch(e) { + return 'Error: ' + e.toString(); + } + })() + `; + + const escapedCode = this.escapeForAppleScript(wrappedCode); + + // Use a different AppleScript structure that explicitly captures the result + const script = tab_id + ? ` + tell application "Microsoft Edge" + repeat with w in windows + repeat with t in tabs of w + if (id of t as string) is "${tab_id}" then + set jsResult to execute t javascript "${escapedCode}" + if jsResult is missing value then + return "undefined" + else + return jsResult as string + end if + end if + end repeat + end repeat + return "Tab not found" + end tell + ` + : ` + tell application "Microsoft Edge" + set jsResult to execute active tab of front window javascript "${escapedCode}" + if jsResult is missing value then + return "undefined" + else + return jsResult as string + end if + end tell + `; + + const result = await this.executeAppleScript(script); + + // Check if it's an error or tab not found + if (result === "Tab not found") { + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: false, + error: "Tab not found", + }, + null, + 2, + ), + }, + ], + }; + } + + // Check if the result starts with "Error: " + const isError = result.startsWith("Error: "); + + // Try to parse as JSON if it looks like JSON + let parsedResult = result; + if (!isError && (result.startsWith("{") || result.startsWith("["))) { + try { + parsedResult = JSON.parse(result); + } catch (e) { + // Keep as string if parsing fails + } + } + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: !isError, + result: isError ? undefined : parsedResult, + error: isError ? result.substring(7) : undefined, + }, + null, + 2, + ), + }, + ], + }; + } + + case "get_page_content": { + const { tab_id } = args; + + const jsCode = "document.body.innerText"; + const script = tab_id + ? ` + tell application "Microsoft Edge" + repeat with w in windows + repeat with t in tabs of w + if (id of t as string) is "${tab_id}" then + set pageContent to execute t javascript "${jsCode}" + return pageContent + end if + end repeat + end repeat + return "Tab not found" + end tell + ` + : ` + tell application "Microsoft Edge" + execute active tab of front window javascript "${jsCode}" + end tell + `; + + const result = await this.executeAppleScript(script); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: result !== "Tab not found", + content: result, + contentLength: result.length, + }, + null, + 2, + ), + }, + ], + }; + } + + default: + throw new Error(`Unknown tool: ${name}`); + } + } catch (error) { + console.error(`Error in tool '${name}':`, error); + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: false, + error: error.message, + tool: name, + args: args, + }, + null, + 2, + ), + }, + ], + isError: true, + }; + } + }); + } + + async run() { + try { + const isEdgeAvailable = await this.checkEdgeAvailable(); + if (!isEdgeAvailable) { + console.error("Warning: Microsoft Edge is not available or not running"); + } + + const transport = new StdioServerTransport(); + await this.server.connect(transport); + console.error("Edge AppleScript MCP server running on stdio"); + } catch (error) { + console.error("Failed to start Edge AppleScript MCP server:", error); + throw error; + } + } +} + +const server = new EdgeControlServer(); +server.run().catch(console.error); \ No newline at end of file diff --git a/examples/edge-applescript/yarn.lock b/examples/edge-applescript/yarn.lock new file mode 100644 index 0000000..4770837 --- /dev/null +++ b/examples/edge-applescript/yarn.lock @@ -0,0 +1,90 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@modelcontextprotocol/sdk@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-0.7.0.tgz#c9e21005429c2ff24348ea6c68a78aa5c0e3d20e" + integrity sha512-YlnQf8//eDHClUM607vb/6+GHmCdMnIfOkN2pcpexN4go9sYHm2JfNnqc5ILS7M8enUlwe9dQO9886l3NO3rUw== + dependencies: + content-type "^1.0.5" + raw-body "^3.0.0" + zod "^3.23.8" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +raw-body@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" + integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.6.3" + unpipe "1.0.0" + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +zod@^3.23.8: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From 33a2a6ffe85057c19877c4f2430202cfeb7c4802 Mon Sep 17 00:00:00 2001 From: Nicholas Tindle Date: Thu, 14 Aug 2025 15:09:08 -0500 Subject: [PATCH 2/2] docs: add edge-applescript to examples README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the examples documentation to include the new Microsoft Edge AppleScript example in the table of available examples. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- examples/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index ca2ce4a..99d44b7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,8 @@ But, the MCP servers themselves are not robust secure production ready servers a | Example | Type | Demonstrates | | --------------------- | ------- | ---------------------------------------- | | `hello-world-node` | Node.js | Basic MCP server with simple time tool | -| `chrome-applescript` | Node.js | Browser automation via AppleScript | +| `chrome-applescript` | Node.js | Chrome browser automation via AppleScript | +| `edge-applescript` | Node.js | Edge browser automation via AppleScript | | `file-manager-python` | Python | File system operations and path handling | ## Usage