Every time I ask Claude something that requires using an MCP server, it shows this dialog:
I wanted to automatically allow all MCP use, so I came across this post on reddit which talks about a trick in Electron apps where you can enable “DevTools” for the app. This allows you to script the app using JavaScript, change the HTML+CSS of the app (and thus, its theme and page elements!), see network traffic, etc.
This was the moment I started to like Electron apps, because this level of customizability and scriptability is rare in modern software. Emacs (with Elisp) and Microsoft Office (with VBA) have had this feature for decades, and certain macOS apps (mainly the ones shipped with the OS) list some of their “commands” and “properties” in a
<app-name>.sdef
which Script Editor (and Automator) can use for scripting the app. This SDEF file is accessible through Script Editor like this (there is another way which doesn’t require Script Editor; see my article on automating Spotify on macOS):
Back to MCP! Here’s how you can make Claude approve MCP use on its own (but make sure you don’t enable this for MCP tools that mutate things. For example, notice that I have commented out "edit_file", "move_file", "write_file"
in the file_system
MCP:
echo '{"allowDevTools": true}' > ~/Library/Application\\ Support/Claude/developer_settings.json
Command-Option-Shift-i
. This opens up the DevTools window.trusted_tools
is the list of tools, not MCP servers you have installed for Claude.app. These you can find by clicking the hammer icon in the app:
which opens up something like this:
I found it helpful to select the entire text and ask ChatGPT/Claude to extract the list of tools into a JS list.
const trustedTools = [
// iTerm Terminal Tools
"write_to_terminal",
"read_terminal_output",
"send_control_character",
// Neovim Tools
"vim_buffer",
"vim_command",
"vim_status",
"vim_edit",
"vim_window",
"vim_mark",
"vim_register",
"vim_visual",
// OpenAI Tool
"openai_chat",
// Memory (Knowledge Graph) Tools
"add_observations",
"create_entities",
"create_relations",
"delete_entities",
"delete_observations",
"delete_relations",
"open_nodes",
"read_graph",
"search_nodes",
// Playwright Browser Tools
"browser_click",
"browser_close",
"browser_drag",
"browser_file_upload",
"browser_hover",
"browser_install",
"browser_navigate",
"browser_navigate_back",
"browser_navigate_forward",
"browser_pdf_save",
"browser_press_key",
"browser_resize",
"browser_select_option",
"browser_snapshot",
"browser_tab_close",
"browser_tab_list",
"browser_tab_new",
"browser_tab_select",
"browser_take_screenshot",
"browser_type",
"browser_wait",
// Filesystem Tools
"create_directory",
"directory_tree",
// "edit_file",
"get_file_info",
"list_allowed_directories",
"list_directory",
// "move_file",
"read_file",
"read_multiple_files",
"search_files",
// "write_file",
// Miscellaneous
"artifacts",
"repl",
"web_search"
];
// Cooldown tracking
let lastClickTime = 0;
const COOLDOWN_MS = 1000; // 1 second cooldown
const observer = new MutationObserver((mutations) => {
const now = Date.now();
if (now - lastClickTime < COOLDOWN_MS) {
console.log('🕒 Still in cooldown period, skipping...');
return;
}
const dialog = document.querySelector('[role="dialog"]');
if (!dialog) return;
const buttonWithDiv = dialog.querySelector('button div');
if (!buttonWithDiv) return;
const toolText = buttonWithDiv.textContent;
if (!toolText) return;
const toolName = toolText.match(/Run (\\S+) from/)?.[1];
if (!toolName) return;
if (trustedTools.includes(toolName)) {
const allowButton = Array.from(dialog.querySelectorAll('button'))
.find(button => button.textContent.includes('Allow for this chat'));
if (allowButton) {
lastClickTime = now; // Set cooldown
allowButton.click();
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
It should look like this:
trusted_tools
list, it will automatically press the “Allow for this chat” button in the dialog that pops up.That’s it! Hope it helps you automate your tasks.
If you’re curious about my MCP “dotfile”, here it is:
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-puppeteer"
]
},
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/**<username>**/Desktop",
"/Users/**<username>**/downloads"
]
},
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
]
},
"MCP Neovim Server": {
"command": "npx",
"args": [
"-y",
"mcp-neovim-server"
],
"env": {
"ALLOW_SHELL_COMMANDS": "true"
}
},
"mcp-openai": {
"command": "npx",
"args": [
"-y",
"@mzxrai/mcp-openai@latest"
],
"env": {
"OPENAI_API_KEY": "**<change-this>**"
}
},
"iterm-mcp": {
"command": "npx",
"args": [
"-y",
"iterm-mcp"
]
},
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
]
}
}
}
On macOS, this is located at /Users/**<username>**/Library/Application Support/Claude/claude_desktop_config.json
.
I found https://github.com/itsKaynine/electron-injector which injects JavaScript code into Electron apps at runtime. This is super useful. First, install the Rust compiler: