Skip to content

Instantly share code, notes, and snippets.

@skwid138
Last active April 1, 2026 23:03
Show Gist options
  • Select an option

  • Save skwid138/587f29bda62b9148bfd86dfa677ad115 to your computer and use it in GitHub Desktop.

Select an option

Save skwid138/587f29bda62b9148bfd86dfa677ad115 to your computer and use it in GitHub Desktop.
macOS Chrome launcher for DevTools MCP with URL, logging, and tab/window controls
#!/usr/bin/env bash
set -euo pipefail
CHROME_APP="Google Chrome"
CHROME_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
PORT="9222"
USER_DATA_DIR="/tmp/chrome-devtools-mcp-auth"
MODE="detached"
VERBOSE=0
URL=""
OPEN_TARGET="default" # default | tab | window
usage() {
cat <<'EOF'
Usage:
chrome-mcp [options]
Options:
-F, --foreground Run Chrome directly in the terminal
(attached to the shell; useful for live output)
-D, --detached Launch Chrome as a normal macOS app
(default)
-v, --verbose Enable Chrome logging
- foreground: logs stream to the terminal
- detached: logs are written to the profile dir
-U, --url URL Open Chrome to a specific URL
-T, --new-tab Open URL in a new tab when practical
- foreground: uses Chrome's default URL behavior
- detached: uses AppleScript if a matching Chrome
instance is already running
-W, --new-window Open URL in a new window
-p, --port PORT Remote debugging port (default: 9222)
-u, --user-data-dir PATH Chrome profile dir
(default: /tmp/chrome-devtools-mcp-auth)
-h, --help Show this help
Examples:
chrome_mcp
chrome_mcp --url "https://example.com/login"
chrome_mcp --foreground --verbose
chrome_mcp --foreground --url "https://example.com/login"
chrome_mcp --new-window --url "https://example.com/login"
chrome_mcp --new-tab --url "https://example.com/login"
chrome_mcp -F -v -p 9333
chrome_mcp -D -u /tmp/my-chrome-mcp-profile -U "https://example.com"
EOF
}
escape_applescript_string() {
local s="$1"
s=${s//\\/\\\\}
s=${s//\"/\\\"}
printf '%s' "$s"
}
matching_instance_running() {
local port_flag="--remote-debugging-port=${PORT}"
local profile_flag="--user-data-dir=${USER_DATA_DIR}"
ps ax -o command= \
| grep -F -- "$port_flag" \
| grep -F -- "$profile_flag" \
>/dev/null 2>&1
}
open_url_in_new_tab_applescript() {
local escaped_url
escaped_url="$(escape_applescript_string "$1")"
osascript <<EOF
tell application "$CHROME_APP"
activate
if (count of windows) = 0 then
make new window
end if
tell front window
set newTab to make new tab at end of tabs with properties {URL:"$escaped_url"}
set active tab index to (count of tabs)
end tell
end tell
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-F|--foreground)
MODE="foreground"
shift
;;
-D|--detached)
MODE="detached"
shift
;;
-v|--verbose)
VERBOSE=1
shift
;;
-U|--url)
[[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 1; }
URL="$2"
shift 2
;;
-T|--new-tab)
[[ "$OPEN_TARGET" == "window" ]] && {
echo "Cannot use --new-tab and --new-window together" >&2
exit 1
}
OPEN_TARGET="tab"
shift
;;
-W|--new-window)
[[ "$OPEN_TARGET" == "tab" ]] && {
echo "Cannot use --new-tab and --new-window together" >&2
exit 1
}
OPEN_TARGET="window"
shift
;;
-p|--port)
[[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 1; }
PORT="$2"
shift 2
;;
-u|--user-data-dir)
[[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 1; }
USER_DATA_DIR="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
echo >&2
usage >&2
exit 1
;;
esac
done
if [[ ! -x "$CHROME_BIN" ]]; then
echo "Chrome binary not found or not executable:" >&2
echo " $CHROME_BIN" >&2
exit 1
fi
COMMON_ARGS=(
"--remote-debugging-port=${PORT}"
"--user-data-dir=${USER_DATA_DIR}"
)
if [[ "$VERBOSE" -eq 1 ]]; then
if [[ "$MODE" == "foreground" ]]; then
COMMON_ARGS+=(
"--enable-logging=stderr"
"--log-level=0"
"--v=1"
)
else
COMMON_ARGS+=(
"--enable-logging"
"--log-level=0"
"--v=1"
)
fi
fi
if [[ "$OPEN_TARGET" == "window" ]]; then
COMMON_ARGS+=("--new-window")
fi
if [[ "$MODE" == "foreground" ]]; then
echo "Starting Chrome in foreground mode on port ${PORT}"
echo "Profile: ${USER_DATA_DIR}"
if [[ "$VERBOSE" -eq 1 ]]; then
echo "Verbose logging: terminal (stderr)"
fi
case "$OPEN_TARGET" in
tab)
echo "Open target: new tab (Chrome default URL behavior)"
;;
window)
echo "Open target: new window"
;;
esac
if [[ -n "$URL" ]]; then
echo "Opening URL: ${URL}"
exec "$CHROME_BIN" "${COMMON_ARGS[@]}" "$URL"
else
exec "$CHROME_BIN" "${COMMON_ARGS[@]}"
fi
else
echo "Starting Chrome in detached mode on port ${PORT}"
echo "Profile: ${USER_DATA_DIR}"
if [[ "$VERBOSE" -eq 1 ]]; then
echo "Verbose logging: written under the Chrome user data directory"
fi
case "$OPEN_TARGET" in
tab)
echo "Open target: new tab"
;;
window)
echo "Open target: new window"
;;
esac
if [[ "$OPEN_TARGET" == "tab" && -n "$URL" ]]; then
if matching_instance_running; then
echo "Matching Chrome instance found; opening URL in a new tab"
open_url_in_new_tab_applescript "$URL"
exit 0
else
echo "No matching Chrome instance found; falling back to launching a new window"
fi
fi
if [[ -n "$URL" ]]; then
echo "Opening URL: ${URL}"
open -na "$CHROME_APP" "$URL" --args "${COMMON_ARGS[@]}"
else
open -na "$CHROME_APP" --args "${COMMON_ARGS[@]}"
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment