Skip to content

Commit 434b289

Browse files
author
Daniel McIlvaney
authored
fix: Fix mcp console focus stealing (#396)
1 parent 5dc25e1 commit 434b289

2 files changed

Lines changed: 119 additions & 7 deletions

File tree

.vscode/mcp.json

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@
33
"azldev-mage-builder": {
44
"type": "stdio",
55
"command": "go",
6-
"args": ["run", "-C", "./magefiles/magemcp/", "magemcp.go", "${workspaceFolder}"],
6+
"args": [
7+
"run",
8+
"-C",
9+
"./magefiles/magemcp/",
10+
"magemcp.go",
11+
"${workspaceFolder}"
12+
],
713
"cwd": "${workspaceFolder}"
814
},
915
"run-azldev-from-out-bin": {
1016
"type": "stdio",
11-
"command": "./out/bin/azldev",
12-
"args": ["advanced", "mcp"],
13-
"cwd": "${workspaceFolder}",
14-
"dev": {
15-
"watch": "out/bin/azldev"
16-
}
17+
"command": "bash",
18+
"args": [
19+
".vscode/run-azldev-mcp.sh"
20+
],
21+
"cwd": "${workspaceFolder}"
1722
}
1823
}
1924
}

.vscode/run-azldev-mcp.sh

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) Microsoft Corporation.
4+
# Licensed under the MIT License.
5+
6+
# Wrapper for the azldev MCP server with inotify-based auto-restart.
7+
#
8+
# - Uses out/bin/azldev if it exists, otherwise falls back to 'go run'.
9+
# - Watches for binary changes via inotifywait and restarts automatically.
10+
# - Falls back to plain exec (no auto-restart) if inotifywait is unavailable.
11+
#
12+
# A wrapper is needed instead of using vscode's "dev" flag directly because it triggers every time the binary
13+
# is built, and this causes the MCP server's output console to grab focus and interrupt the developer's workflow.
14+
#
15+
# Install inotify-tools for auto-restart:
16+
# sudo dnf install inotify-tools (Fedora/RHEL)
17+
# sudo apt install inotify-tools (Debian/Ubuntu)
18+
19+
set -uo pipefail
20+
21+
BINARY="./out/bin/azldev"
22+
BINARY_DIR="$(dirname "$BINARY")"
23+
SERVER_PID=""
24+
WATCH_PID=""
25+
26+
# Save original stdin to fd 3. Bash redirects background processes' stdin to
27+
# /dev/null, so we need to explicitly restore it for the MCP server which
28+
# communicates over stdio.
29+
exec 3<&0
30+
31+
cleanup() {
32+
[[ -n "$SERVER_PID" ]] && kill "$SERVER_PID" 2>/dev/null
33+
[[ -n "$WATCH_PID" ]] && kill "$WATCH_PID" 2>/dev/null
34+
wait 2>/dev/null
35+
}
36+
trap cleanup EXIT SIGTERM SIGINT
37+
38+
# Start the server in the background and save its PID.
39+
start_server() {
40+
if [[ -x "$BINARY" ]]; then
41+
echo "Starting azldev MCP server from $BINARY..." >&2
42+
"$BINARY" advanced mcp 0<&3 &
43+
else
44+
echo "Binary not found; running azldev MCP server via 'go run'..." >&2
45+
go run ./cmd/azldev advanced mcp 0<&3 &
46+
fi
47+
SERVER_PID=$!
48+
}
49+
50+
# Completely replace the current process with the server (no auto-restart).
51+
replace_server() {
52+
if [[ -x "$BINARY" ]]; then
53+
echo "Starting azldev MCP server from $BINARY..." >&2
54+
exec "$BINARY" advanced mcp 0<&3
55+
else
56+
echo "Binary not found; running azldev MCP server via 'go run'..." >&2
57+
exec go run ./cmd/azldev advanced mcp 0<&3
58+
fi
59+
# No return expected since exec replaces the shell
60+
}
61+
62+
# No inotifywait → run directly without auto-restart.
63+
if ! command -v inotifywait &>/dev/null; then
64+
echo "inotifywait not found; running without auto-restart. Install inotify-tools for auto-restart." >&2
65+
replace_server
66+
fi
67+
68+
while true; do
69+
start_server
70+
71+
# Watch the directory for the binary being created/replaced.
72+
# go build writes to a temp file then renames, so watching the file
73+
# directly misses the event (inode changes).
74+
(
75+
while true; do
76+
while [[ ! -d "$BINARY_DIR" ]]; do sleep 2; done
77+
inotifywait -qq -e close_write -e moved_to -e create "$BINARY_DIR" 2>/dev/null
78+
# Only restart if the actual binary was affected.
79+
if [[ -x "$BINARY" ]]; then
80+
break
81+
fi
82+
done
83+
) &
84+
WATCH_PID=$!
85+
86+
# Wait for either the server to exit or the file watcher to trigger.
87+
wait -n "$SERVER_PID" "$WATCH_PID" 2>/dev/null || true
88+
89+
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
90+
# Server exited on its own → clean up watcher and exit.
91+
kill "$WATCH_PID" 2>/dev/null || true
92+
wait "$WATCH_PID" 2>/dev/null || true
93+
echo "azldev MCP server exited" >&2
94+
exit 1
95+
fi
96+
97+
# Binary changed → kill server and restart after debounce.
98+
kill "$WATCH_PID" 2>/dev/null || true
99+
wait "$WATCH_PID" 2>/dev/null || true
100+
101+
# Debounce: wait for file activity to settle (go build does multiple ops).
102+
sleep 1
103+
104+
kill "$SERVER_PID" 2>/dev/null || true
105+
wait "$SERVER_PID" 2>/dev/null || true
106+
echo "azldev MCP server restarted (binary changed)" >&2
107+
done

0 commit comments

Comments
 (0)