Repository navigation
Expand file tree
/
Copy pathdisableProxy.ts
More file actions
184 lines (167 loc) · 6.66 KB
/
Copy pathdisableProxy.ts
File metadata and controls
184 lines (167 loc) · 6.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
* mcp/skills/disableProxy.ts — disable_proxy skill
*
* Switches the system proxy OFF for one network service, leaving the
* configured server and port in place. The read-only counterpart is
* `check_proxy_settings`, which reports what is currently set.
*
* Why disable rather than clear
* -----------------------------
* Clearing the address (`networksetup -setwebproxy "" 0`) would lose a
* setting the user may need again — a corporate proxy that is merely
* unreachable right now is still the correct configuration for the office.
* Flipping only the state is one toggle away from reversible in the OS UI,
* which makes this the least-destructive fix for the common fault: a stale
* proxy left behind by a VPN or a captive portal.
*
* Platform strategy
* -----------------
* darwin `networksetup -setwebproxystate / -setsecurewebproxystate /
* -setautoproxystate "<service>" off` — admin, routed through the
* privileged helper daemon (op `disable_proxy`).
* win32 HKCU Internet Settings `ProxyEnable=0` — per-user, but routed
* through the same helper op for a uniform audit trail.
*
* Never touches WinHTTP (`netsh winhttp`): that is the system-service proxy,
* is not what `check_proxy_settings` reads, and resetting it can break
* Windows Update and other service traffic.
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/disableProxy.ts
*/
import * as os from "os";
import { z } from "zod";
import { execAsync } from "./_shared/platform";
const EXEC_TIMEOUT_MS = 15_000;
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "disable_proxy",
description:
"Switches the system proxy off for a network service, leaving the configured " +
"server and port intact so it can be re-enabled from System Settings. " +
"Use when check_proxy_settings shows an enabled proxy pointing at an " +
"unreachable server — the classic 'I have internet but no websites load'.",
// Medium, not high: this flips a reversible state flag and destroys no
// configuration. reset_network_settings is high because it discards custom
// locations, static IPs and manual DNS.
riskLevel: "medium",
destructive: false,
requiresConsent: true,
supportsDryRun: true,
affectedScope: ["network", "system"],
auditRequired: true,
escalationHint: {
darwin: "sudo networksetup -setwebproxystate \"<service>\" off # repeat for -setsecurewebproxystate / -setautoproxystate",
win32: "Settings → Network & Internet → Proxy → turn off \"Use a proxy server\"",
},
outputKeys: ["interface","disabled","dryRun","message","platform"],
schema: {
// snake_case-free: the helper's Params field is `interface`, and G4
// forwards executor params verbatim, so this name is the wire contract.
interface: z
.string()
.describe(
"Network service to disable the proxy on. macOS: the service name as " +
"shown in System Settings → Network (e.g. \"Wi-Fi\", \"Ethernet\"). " +
"Windows: accepted for parity but unused — the proxy is per-user.",
),
protocols: z
.array(z.enum(["web", "secureweb", "auto"]))
.nullable().optional()
.describe(
"Which proxy kinds to switch off. Omit to disable all three.",
),
dryRun: z
.boolean()
.nullable().optional()
.describe("If true, report what would be disabled without changing anything. Default: true"),
},
} as const;
// -- Types --------------------------------------------------------------------
type Protocol = "web" | "secureweb" | "auto";
const ALL_PROTOCOLS: Protocol[] = ["web", "secureweb", "auto"];
interface DisableProxyResult {
interface: string;
disabled: Protocol[];
dryRun: boolean;
message: string;
platform: string;
}
// -- Exported run function ----------------------------------------------------
/**
* Local implementation. In production G4 routes the real call through the
* privileged helper (`affectedScope` includes "system"); this body serves the
* dry-run preview and the helper-unavailable path, where it fails cleanly
* rather than half-applying.
*/
export async function run({
interface: iface,
protocols,
dryRun = true,
}: {
interface: string;
protocols?: Protocol[] | null;
dryRun?: boolean | null;
}): Promise<DisableProxyResult> {
const platform = os.platform();
const targets = protocols && protocols.length > 0 ? protocols : ALL_PROTOCOLS;
if (!iface || !iface.trim()) {
throw new Error("[disable_proxy] 'interface' is required (network service name).");
}
if (dryRun !== false) {
return {
interface: iface,
disabled: targets,
dryRun: true,
message:
platform === "win32"
? `Dry run: would set ProxyEnable=0 for the current user (${targets.join(", ")}). The proxy address is kept so it can be switched back on.`
: `Dry run: would switch off ${targets.join(", ")} proxy on "${iface}". The server and port are kept so it can be switched back on.`,
platform,
};
}
// Real execution normally happens in the helper. Reaching here means the
// helper was unavailable and G4 fell back — attempt locally and let the
// caller surface a permission failure rather than reporting a false success.
const flags: Record<Protocol, string> = {
web: "-setwebproxystate",
secureweb: "-setsecurewebproxystate",
auto: "-setautoproxystate",
};
try {
if (platform === "win32") {
await execAsync(
`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f`,
{ timeout: EXEC_TIMEOUT_MS },
);
} else {
for (const p of targets) {
await execAsync(
`networksetup ${flags[p]} '${iface.replace(/'/g, "'\\''")}' off`,
{ timeout: EXEC_TIMEOUT_MS },
);
}
}
} catch (e) {
return {
interface: iface,
disabled: [],
dryRun: false,
message: `Failed to disable the proxy on "${iface}": ${(e as Error).message}. This needs administrator rights — the privileged helper was not available.`,
platform,
};
}
return {
interface: iface,
disabled: targets,
dryRun: false,
message: `Proxy switched off (${targets.join(", ")}) on "${iface}". The server and port are unchanged, so it can be re-enabled from System Settings.`,
platform,
};
}
// -- CLI smoke test -----------------------------------------------------------
if (false) {
run({ interface: "Wi-Fi" })
.then(r => console.log(JSON.stringify(r, null, 2)))
.catch((err: Error) => { console.error(err.message); process.exit(1); });
}