🚀 Hello World
This commit is contained in:
commit
0172c07623
3 changed files with 11896 additions and 0 deletions
73
examples/chrome-applescript/manifest.json
Normal file
73
examples/chrome-applescript/manifest.json
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"dxt_version": "0.1",
|
||||
"name": "chrome-applescript",
|
||||
"display_name": "Control Chrome with AppleScript",
|
||||
"version": "0.1.0",
|
||||
"description": "Control Google Chrome browser tabs, windows, and navigation",
|
||||
"long_description": "This extension allows you to control Google Chrome browser tabs, windows, and navigation through Chrome's official AppleScript API. 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 Google or Chrome.",
|
||||
"author": {
|
||||
"name": "Anthropic",
|
||||
"email": "support@anthropic.com",
|
||||
"url": "https://github.com/anthropics"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server/index.js",
|
||||
"build": "npx @anthropic-ai/dxt pack"
|
||||
},
|
||||
"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 Chrome, 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 Chrome."
|
||||
},
|
||||
{
|
||||
"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": ["chrome", "browser", "remote"],
|
||||
"license": "MIT"
|
||||
}
|
||||
784
examples/chrome-applescript/server/index.js
Normal file
784
examples/chrome-applescript/server/index.js
Normal file
|
|
@ -0,0 +1,784 @@
|
|||
#!/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 ChromeControlServer {
|
||||
constructor() {
|
||||
this.server = new Server(
|
||||
{
|
||||
name: "chrome-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 checkChromeAvailable() {
|
||||
try {
|
||||
const script = 'tell application "Google Chrome" 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 "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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 Chrome",
|
||||
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 Chrome",
|
||||
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 Chrome availability first
|
||||
const isChromeAvailable = await this.checkChromeAvailable();
|
||||
if (!isChromeAvailable) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Google Chrome 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 "Google Chrome" to open location "${escapedUrl}"`
|
||||
: `tell application "Google Chrome" 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 "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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;
|
||||
|
||||
const result = await this.executeTabOperation(
|
||||
tab_id,
|
||||
{
|
||||
tabScript: `
|
||||
set active tab of w to t
|
||||
set index of w to 1
|
||||
activate
|
||||
`,
|
||||
activeScript: `
|
||||
tell application "Google Chrome"
|
||||
activate
|
||||
return "Chrome activated"
|
||||
end tell
|
||||
`,
|
||||
},
|
||||
"Switched to tab",
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
success: result !== "Tab not found",
|
||||
message: result,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case "reload_tab": {
|
||||
const { tab_id } = args;
|
||||
|
||||
const result = await this.executeTabOperation(
|
||||
tab_id,
|
||||
{
|
||||
tabScript: "reload t",
|
||||
activeScript: `
|
||||
tell application "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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",
|
||||
);
|
||||
}
|
||||
|
||||
// Wrap the code to capture console.log outputs
|
||||
const wrappedCode = `
|
||||
(function() {
|
||||
const logs = [];
|
||||
const originalLog = console.log;
|
||||
const originalWarn = console.warn;
|
||||
const originalError = console.error;
|
||||
const originalInfo = console.info;
|
||||
|
||||
console.log = function(...args) {
|
||||
logs.push({type: 'log', message: args.map(arg => {
|
||||
try {
|
||||
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
||||
} catch (e) {
|
||||
return String(arg);
|
||||
}
|
||||
}).join(' ')});
|
||||
originalLog.apply(console, args);
|
||||
};
|
||||
|
||||
console.warn = function(...args) {
|
||||
logs.push({type: 'warn', message: args.map(arg => {
|
||||
try {
|
||||
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
||||
} catch (e) {
|
||||
return String(arg);
|
||||
}
|
||||
}).join(' ')});
|
||||
originalWarn.apply(console, args);
|
||||
};
|
||||
|
||||
console.error = function(...args) {
|
||||
logs.push({type: 'error', message: args.map(arg => {
|
||||
try {
|
||||
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
||||
} catch (e) {
|
||||
return String(arg);
|
||||
}
|
||||
}).join(' ')});
|
||||
originalError.apply(console, args);
|
||||
};
|
||||
|
||||
console.info = function(...args) {
|
||||
logs.push({type: 'info', message: args.map(arg => {
|
||||
try {
|
||||
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
||||
} catch (e) {
|
||||
return String(arg);
|
||||
}
|
||||
}).join(' ')});
|
||||
originalInfo.apply(console, args);
|
||||
};
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = (function() {
|
||||
${code}
|
||||
})();
|
||||
} catch (error) {
|
||||
result = {error: error.toString(), stack: error.stack};
|
||||
} finally {
|
||||
console.log = originalLog;
|
||||
console.warn = originalWarn;
|
||||
console.error = originalError;
|
||||
console.info = originalInfo;
|
||||
}
|
||||
|
||||
return {
|
||||
result: result,
|
||||
logs: logs
|
||||
};
|
||||
})();
|
||||
`;
|
||||
|
||||
const escapedCode = this.escapeForAppleScript(wrappedCode);
|
||||
|
||||
const script = tab_id
|
||||
? `
|
||||
tell application "Google Chrome"
|
||||
repeat with w in windows
|
||||
repeat with t in tabs of w
|
||||
if (id of t as string) is "${tab_id}" then
|
||||
set result to execute t javascript "${escapedCode}"
|
||||
return result
|
||||
end if
|
||||
end repeat
|
||||
end repeat
|
||||
return "Tab not found"
|
||||
end tell
|
||||
`
|
||||
: `
|
||||
tell application "Google Chrome"
|
||||
execute active tab of front window javascript "${escapedCode}"
|
||||
end tell
|
||||
`;
|
||||
|
||||
const result = await this.executeAppleScript(script);
|
||||
|
||||
// Parse the result to extract logs and actual result
|
||||
try {
|
||||
const parsed = JSON.parse(result);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
logs: parsed.logs || [],
|
||||
result: parsed.result,
|
||||
hasErrors:
|
||||
parsed.result && parsed.result.error ? true : false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (e) {
|
||||
// If parsing fails, return structured error
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
success: false,
|
||||
error: "Failed to parse JavaScript execution result",
|
||||
rawResult: result || "No result returned",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
case "get_page_content": {
|
||||
const { tab_id } = args;
|
||||
|
||||
const jsCode = "document.body.innerText";
|
||||
const script = tab_id
|
||||
? `
|
||||
tell application "Google Chrome"
|
||||
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 "Google Chrome"
|
||||
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 isChromeAvailable = await this.checkChromeAvailable();
|
||||
if (!isChromeAvailable) {
|
||||
console.error("Warning: Google Chrome is not available or not running");
|
||||
}
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
console.error("Chrome AppleScript MCP server running on stdio");
|
||||
} catch (error) {
|
||||
console.error("Failed to start Chrome AppleScript MCP server:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const server = new ChromeControlServer();
|
||||
server.run().catch(console.error);
|
||||
90
examples/chrome-applescript/yarn.lock
Normal file
90
examples/chrome-applescript/yarn.lock
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@modelcontextprotocol/sdk@^0.6.0":
|
||||
version "0.6.1"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/@modelcontextprotocol/sdk/-/sdk-0.6.1.tgz#4e144155992749faf191faa512ac0c7a23152522"
|
||||
integrity sha512-OkVXMix3EIbB5Z6yife2XTrSlOnVvCLR1Kg91I4pYFEsV9RbnoyQVScXCuVhGaZHOnTZgso8lMQN1Po2TadGKQ==
|
||||
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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
|
||||
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||
|
||||
content-type@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
||||
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||
|
||||
depd@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
raw-body@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/safer-buffer/2.1.2/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||
|
||||
statuses@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
|
||||
toidentifier@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/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://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||
|
||||
zod@^3.23.8:
|
||||
version "3.25.55"
|
||||
resolved "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/zod/-/zod-3.25.55.tgz#b1ffd48a34f558d1a912d1ed6bf84a6ca692fbe1"
|
||||
integrity sha512-219huNnkSLQnLsQ3uaRjXsxMrVm5C9W3OOpEVt2k5tvMKuA8nBSu38e0B//a+he9Iq2dvmk2VyYVlHqiHa4YBA==
|
||||
Loading…
Reference in a new issue