machine-bridge-mcp 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +76 -23
- package/SECURITY.md +16 -2
- package/browser-extension/manifest.json +33 -0
- package/browser-extension/page-automation.js +246 -0
- package/browser-extension/pairing.js +19 -0
- package/browser-extension/service-worker.js +406 -0
- package/docs/AGENT_CONTEXT.md +134 -124
- package/docs/ARCHITECTURE.md +29 -9
- package/docs/CLIENTS.md +8 -0
- package/docs/ENGINEERING.md +10 -2
- package/docs/LOCAL_AUTOMATION.md +111 -0
- package/docs/LOGGING.md +10 -1
- package/docs/OPERATIONS.md +49 -0
- package/docs/PRIVACY.md +10 -1
- package/docs/RELEASING.md +2 -2
- package/docs/TESTING.md +7 -5
- package/package.json +13 -7
- package/scripts/sync-worker-version.mjs +27 -12
- package/src/local/agent-context.mjs +248 -15
- package/src/local/app-automation.mjs +400 -0
- package/src/local/browser-bridge.mjs +757 -0
- package/src/local/cli.mjs +156 -17
- package/src/local/default-instructions.mjs +337 -0
- package/src/local/runtime.mjs +114 -4
- package/src/local/state.mjs +8 -4
- package/src/local/stdio.mjs +12 -7
- package/src/shared/server-metadata.json +4 -1
- package/src/shared/tool-catalog.json +854 -1
- package/src/worker/index.ts +24 -4
package/src/local/runtime.mjs
CHANGED
|
@@ -11,10 +11,13 @@ import { MAX_COMMAND_BYTES, ProcessSessionManager, terminateProcessTree, validat
|
|
|
11
11
|
export { MAX_COMMAND_BYTES } from "./process-sessions.mjs";
|
|
12
12
|
import { allToolNames, assertCanonicalFullPolicy, isCanonicalFullPolicy, MCP_PROTOCOL_VERSION, MCP_SUPPORTED_PROTOCOL_VERSIONS, normalizePolicy, POLICY_PROFILES, SERVER_NAME, toolNamesForPolicy } from "./tools.mjs";
|
|
13
13
|
import { classifyOperationalError } from "./log.mjs";
|
|
14
|
-
import { ManagedJobManager } from "./managed-jobs.mjs";
|
|
14
|
+
import { inspectResourceFile, ManagedJobManager } from "./managed-jobs.mjs";
|
|
15
15
|
import { generateRegisteredSshKey } from "./resource-operations.mjs";
|
|
16
16
|
import { expandHome } from "./state.mjs";
|
|
17
17
|
import { AgentContextManager } from "./agent-context.mjs";
|
|
18
|
+
import { AppAutomationManager } from "./app-automation.mjs";
|
|
19
|
+
import { BrowserBridgeManager } from "./browser-bridge.mjs";
|
|
20
|
+
import { readBoundedRegularFileSync } from "./secure-file.mjs";
|
|
18
21
|
|
|
19
22
|
export const MAX_WRITE_BYTES = 5 * 1024 * 1024;
|
|
20
23
|
const MAX_WS_MESSAGE_BYTES = 8 * 1024 * 1024;
|
|
@@ -28,11 +31,26 @@ const SLOW_TOOL_CALL_MS = 30_000;
|
|
|
28
31
|
const RUNTIME_TOOL_HANDLERS = Object.freeze({
|
|
29
32
|
server_info: (runtime) => runtime.runtimeInfo(),
|
|
30
33
|
project_overview: (runtime, _args, context) => runtime.projectOverview(context),
|
|
34
|
+
session_bootstrap: (runtime, args, context) => runtime.sessionBootstrap(args, context),
|
|
31
35
|
agent_context: (runtime, args, context) => runtime.agentContextManager.agentContext(args, context),
|
|
36
|
+
resolve_task_capabilities: (runtime, args, context) => runtime.resolveTaskCapabilities(args, context),
|
|
32
37
|
list_local_skills: (runtime, args, context) => runtime.agentContextManager.listLocalSkills(args, context),
|
|
33
38
|
load_local_skill: (runtime, args, context) => runtime.agentContextManager.loadLocalSkill(args, context),
|
|
34
39
|
list_local_commands: (runtime, args, context) => runtime.agentContextManager.listLocalCommands(args, context),
|
|
35
40
|
run_local_command: (runtime, args, context) => runtime.runLocalCommand(args, context),
|
|
41
|
+
list_local_applications: (runtime, args, context) => runtime.appAutomationManager.listApplications(args, context),
|
|
42
|
+
open_local_application: (runtime, args, context) => runtime.appAutomationManager.openApplication(args, context),
|
|
43
|
+
inspect_local_application: (runtime, args, context) => runtime.appAutomationManager.inspectApplication(args, context),
|
|
44
|
+
operate_local_application: (runtime, args, context) => runtime.appAutomationManager.operateApplication(args, context),
|
|
45
|
+
browser_status: (runtime, _args, context) => runtime.browserBridgeManager.status(context),
|
|
46
|
+
pair_browser_extension: (runtime, args, context) => runtime.browserBridgeManager.pair(args, context),
|
|
47
|
+
browser_list_tabs: (runtime, args, context) => runtime.browserBridgeManager.listTabs(args, context),
|
|
48
|
+
browser_get_source: (runtime, args, context) => runtime.browserBridgeManager.getSource(args, context),
|
|
49
|
+
browser_inspect_page: (runtime, args, context) => runtime.browserBridgeManager.inspectPage(args, context),
|
|
50
|
+
browser_action: (runtime, args, context) => runtime.browserBridgeManager.act(args, context),
|
|
51
|
+
browser_fill_form: (runtime, args, context) => runtime.browserBridgeManager.fillForm(args, context),
|
|
52
|
+
browser_screenshot: (runtime, args, context) => runtime.browserBridgeManager.screenshot(args, context),
|
|
53
|
+
browser_upload_files: (runtime, args, context) => runtime.browserBridgeManager.uploadFiles(args, context),
|
|
36
54
|
list_roots: (runtime) => runtime.listRoots(),
|
|
37
55
|
list_dir: (runtime, args, context) => runtime.listDir(args.path || ".", context),
|
|
38
56
|
list_files: (runtime, args, context) => runtime.listFiles(args.path || ".", clampInt(args.max_files, 1000, 1, 10000), context),
|
|
@@ -67,7 +85,7 @@ export function runtimeToolHandlerNames() {
|
|
|
67
85
|
}
|
|
68
86
|
|
|
69
87
|
export class LocalRuntime {
|
|
70
|
-
constructor({ workerUrl = "", secret = "", expectedRelayVersion = "", workspace, policy, logger = console, onSuperseded = null, onFatal = null, jobRoot = "", resources = {}, resourceStatePath = "", recoverJobs = true }) {
|
|
88
|
+
constructor({ workerUrl = "", secret = "", expectedRelayVersion = "", workspace, policy, logger = console, onSuperseded = null, onFatal = null, jobRoot = "", resources = {}, resourceStatePath = "", browserStateRoot = "", agentHome = process.env.HOME || process.env.USERPROFILE || "", codexHome = process.env.CODEX_HOME || "", recoverJobs = true }) {
|
|
71
89
|
const remoteWorkerUrl = workerUrl ? String(workerUrl) : "";
|
|
72
90
|
const remoteSecret = secret || "";
|
|
73
91
|
this.workspaceInput = resolve(workspace || process.cwd());
|
|
@@ -113,6 +131,26 @@ export class LocalRuntime {
|
|
|
113
131
|
displayPath: (value) => this.displayPath(value),
|
|
114
132
|
resolveExistingPath: (value) => this.resolveExistingPath(value),
|
|
115
133
|
throwIfCancelled: (context) => this.throwIfCancelled(context),
|
|
134
|
+
home: agentHome,
|
|
135
|
+
codexHome,
|
|
136
|
+
});
|
|
137
|
+
const runProcess = (cmd, argv, timeoutMs, allowFailure, maxOutputBytes, context, cwd, stdin) => this.runProcess(cmd, argv, timeoutMs, allowFailure, maxOutputBytes, context, cwd, stdin);
|
|
138
|
+
const readResourceText = (name) => this.readLocalResourceText(name);
|
|
139
|
+
const readResourceBinary = (name) => this.readLocalResourceBinary(name);
|
|
140
|
+
this.appAutomationManager = new AppAutomationManager({
|
|
141
|
+
policy: this.policy,
|
|
142
|
+
displayPath: (value) => this.displayPath(value),
|
|
143
|
+
runProcess,
|
|
144
|
+
readResourceText,
|
|
145
|
+
throwIfCancelled: (context) => this.throwIfCancelled(context),
|
|
146
|
+
});
|
|
147
|
+
this.browserBridgeManager = new BrowserBridgeManager({
|
|
148
|
+
policy: this.policy,
|
|
149
|
+
stateRoot: browserStateRoot,
|
|
150
|
+
runProcess,
|
|
151
|
+
readResourceText,
|
|
152
|
+
readResourceBinary,
|
|
153
|
+
throwIfCancelled: (context) => this.throwIfCancelled(context),
|
|
116
154
|
});
|
|
117
155
|
this.relay = createRelayConnection(this, {
|
|
118
156
|
workerUrl: remoteWorkerUrl,
|
|
@@ -170,8 +208,13 @@ export class LocalRuntime {
|
|
|
170
208
|
};
|
|
171
209
|
}
|
|
172
210
|
|
|
173
|
-
start() {
|
|
211
|
+
async start() {
|
|
174
212
|
if (!this.relay) throw new Error("remote daemon start requires a Worker URL and daemon secret");
|
|
213
|
+
if (this.policy.profile === "full") {
|
|
214
|
+
void this.browserBridgeManager.ensureStarted().catch((error) => {
|
|
215
|
+
this.logger.warn?.("browser bridge did not start; browser tools remain unavailable", { error_class: classifyOperationalError(error) });
|
|
216
|
+
});
|
|
217
|
+
}
|
|
175
218
|
return this.relay.start();
|
|
176
219
|
}
|
|
177
220
|
|
|
@@ -179,6 +222,7 @@ export class LocalRuntime {
|
|
|
179
222
|
this.relay?.stop();
|
|
180
223
|
this.terminateActiveProcesses("SIGKILL");
|
|
181
224
|
this.processSessionManager.clear();
|
|
225
|
+
this.browserBridgeManager?.stop();
|
|
182
226
|
rmSync(this.runtimeDir, { recursive: true, force: true });
|
|
183
227
|
}
|
|
184
228
|
|
|
@@ -277,6 +321,7 @@ export class LocalRuntime {
|
|
|
277
321
|
cancelCall(callId, reason = "cancelled") {
|
|
278
322
|
this.cancelledCalls.add(callId);
|
|
279
323
|
this.processSessionManager.notifyCancellation();
|
|
324
|
+
this.browserBridgeManager?.cancelCall(callId);
|
|
280
325
|
for (const child of this.callProcesses.get(callId) || []) terminateProcessTree(child, "SIGTERM");
|
|
281
326
|
const children = [...(this.callProcesses.get(callId) || [])];
|
|
282
327
|
if (children.length) {
|
|
@@ -738,6 +783,57 @@ export class LocalRuntime {
|
|
|
738
783
|
};
|
|
739
784
|
}
|
|
740
785
|
|
|
786
|
+
|
|
787
|
+
async sessionBootstrap(args = {}, context = {}) {
|
|
788
|
+
const bootstrap = await this.agentContextManager.sessionBootstrap(args, context);
|
|
789
|
+
bootstrap.local_automation = {
|
|
790
|
+
applications: this.appAutomationManager.capabilities(),
|
|
791
|
+
browser: this.policy.profile === "full" ? {
|
|
792
|
+
existing_profile: true,
|
|
793
|
+
extension_bridge: true,
|
|
794
|
+
status_tool: "browser_status",
|
|
795
|
+
} : null,
|
|
796
|
+
};
|
|
797
|
+
return bootstrap;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
async resolveTaskCapabilities(args = {}, context = {}) {
|
|
801
|
+
const result = await this.agentContextManager.resolveTaskCapabilities(args, context);
|
|
802
|
+
const task = String(args.task || "");
|
|
803
|
+
if (/app|application|gui|window|应用|软件|窗口|界面/i.test(task) && this.policy.profile === "full") {
|
|
804
|
+
const applications = await this.appAutomationManager.listApplications({ query: "", max_results: 500 }, context).catch(() => ({ applications: [] }));
|
|
805
|
+
const lower = task.toLowerCase();
|
|
806
|
+
result.application_matches = applications.applications
|
|
807
|
+
.map((application) => ({ application, score: applicationMatchScore(lower, application) }))
|
|
808
|
+
.filter((item) => item.score > 0)
|
|
809
|
+
.sort((left, right) => right.score - left.score || left.application.name.localeCompare(right.application.name))
|
|
810
|
+
.slice(0, 20)
|
|
811
|
+
.map(({ application, score }) => ({ ...application, score }));
|
|
812
|
+
} else {
|
|
813
|
+
result.application_matches = [];
|
|
814
|
+
}
|
|
815
|
+
result.browser_backend = this.policy.profile === "full" ? { tool: "browser_status", existing_profile: true, extension_bridge: true } : null;
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
readLocalResourceBinary(name) {
|
|
820
|
+
const registry = this.managedJobManager.currentResources();
|
|
821
|
+
const resource = registry[name];
|
|
822
|
+
if (!resource) throw new Error(`unknown local resource: ${name}`);
|
|
823
|
+
const inspected = inspectResourceFile(resource.path, { allowInsecurePermissions: resource.allowInsecurePermissions === true });
|
|
824
|
+
if (inspected.size > 1024 * 1024) throw new Error("local resource exceeds 1 MiB browser injection limit");
|
|
825
|
+
return { buffer: readBoundedRegularFileSync(resource.path, 1024 * 1024), path: resource.path, size: inspected.size };
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
readLocalResourceText(name) {
|
|
829
|
+
const { buffer } = this.readLocalResourceBinary(name);
|
|
830
|
+
try {
|
|
831
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
832
|
+
} catch {
|
|
833
|
+
throw new Error(`local resource is not valid UTF-8 text: ${name}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
741
837
|
async runDirectProcess(args, context = {}) {
|
|
742
838
|
if (this.policy.execMode !== "direct" && this.policy.execMode !== "shell") throw new Error("run_process is disabled by daemon policy");
|
|
743
839
|
const argv = validateArgv(args.argv);
|
|
@@ -785,9 +881,10 @@ export class LocalRuntime {
|
|
|
785
881
|
}
|
|
786
882
|
}
|
|
787
883
|
|
|
788
|
-
async runProcess(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = 512 * 1024, context = {}, cwd = this.workspace) {
|
|
884
|
+
async runProcess(cmd, args, timeoutMs, allowFailure = false, maxOutputBytes = 512 * 1024, context = {}, cwd = this.workspace, stdin = null) {
|
|
789
885
|
this.throwIfCancelled(context);
|
|
790
886
|
return new Promise((resolvePromise, reject) => {
|
|
887
|
+
if (stdin !== null && Buffer.byteLength(String(stdin)) > 1024 * 1024) throw new Error("process stdin exceeds 1 MiB");
|
|
791
888
|
const child = spawn(cmd, args, {
|
|
792
889
|
cwd,
|
|
793
890
|
env: executionEnv(this.workspace, { fullEnv: this.policy.minimalEnv === false, runtimeDir: this.runtimeDir }),
|
|
@@ -795,6 +892,10 @@ export class LocalRuntime {
|
|
|
795
892
|
windowsHide: true,
|
|
796
893
|
});
|
|
797
894
|
this.activeProcesses.add(child);
|
|
895
|
+
if (stdin !== null) {
|
|
896
|
+
child.stdin.on("error", () => {});
|
|
897
|
+
child.stdin.end(String(stdin));
|
|
898
|
+
}
|
|
798
899
|
if (context.callId) {
|
|
799
900
|
const set = this.callProcesses.get(context.callId) || new Set();
|
|
800
901
|
set.add(child);
|
|
@@ -965,6 +1066,15 @@ export class LocalRuntime {
|
|
|
965
1066
|
}
|
|
966
1067
|
}
|
|
967
1068
|
|
|
1069
|
+
function applicationMatchScore(task, application) {
|
|
1070
|
+
const name = String(application.name || "").toLowerCase();
|
|
1071
|
+
const id = String(application.id || "").toLowerCase();
|
|
1072
|
+
if (!name) return 0;
|
|
1073
|
+
if (task.includes(name)) return 10 + Math.min(name.length, 20);
|
|
1074
|
+
const words = name.split(/[^\p{L}\p{N}]+/u).filter((word) => word.length >= 2);
|
|
1075
|
+
return words.reduce((score, word) => score + (task.includes(word) ? 2 : 0), id && task.includes(id) ? 5 : 0);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
968
1078
|
function policyMatchesNamedProfile(policy) {
|
|
969
1079
|
const named = POLICY_PROFILES[policy.profile];
|
|
970
1080
|
if (!named) return false;
|
package/src/local/state.mjs
CHANGED
|
@@ -170,15 +170,18 @@ function lockPathForState(state, name) {
|
|
|
170
170
|
return path.join(profileDir, name);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
export function acquireDaemonLock(state) {
|
|
174
|
-
|
|
173
|
+
export function acquireDaemonLock(state, metadata = {}) {
|
|
174
|
+
const details = {};
|
|
175
|
+
if (metadata?.mode === "foreground" || metadata?.mode === "service") details.mode = metadata.mode;
|
|
176
|
+
if (typeof metadata?.version === "string" && /^[0-9A-Za-z.+_-]{1,64}$/.test(metadata.version)) details.version = metadata.version;
|
|
177
|
+
return acquireProcessLock(daemonLockPathForState(state), state, "daemon", details);
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
export function acquireStartupLock(state) {
|
|
178
181
|
return acquireProcessLock(startupLockPathForState(state), state, "startup");
|
|
179
182
|
}
|
|
180
183
|
|
|
181
|
-
function acquireProcessLock(lockPath, state, purpose) {
|
|
184
|
+
function acquireProcessLock(lockPath, state, purpose, details = {}) {
|
|
182
185
|
ensureOwnerOnlyDir(path.dirname(lockPath));
|
|
183
186
|
const token = randomBytes(16).toString("hex");
|
|
184
187
|
const payload = {
|
|
@@ -188,6 +191,7 @@ function acquireProcessLock(lockPath, state, purpose) {
|
|
|
188
191
|
workspace: state?.workspace?.path || "",
|
|
189
192
|
startedAt: new Date().toISOString(),
|
|
190
193
|
entryScript: process.argv[1] || "",
|
|
194
|
+
...details,
|
|
191
195
|
};
|
|
192
196
|
|
|
193
197
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
@@ -361,7 +365,7 @@ function assertValidStateMarker(marker, options = {}) {
|
|
|
361
365
|
}
|
|
362
366
|
|
|
363
367
|
function hasOnlyStateEntries(entries) {
|
|
364
|
-
const allowed = new Set([STATE_MARKER, "config.json", "profiles", "logs"]);
|
|
368
|
+
const allowed = new Set([STATE_MARKER, "config.json", "browser-bridge.json", "profiles", "logs"]);
|
|
365
369
|
return entries.every((entry) => allowed.has(entry) || /^config\.json\.corrupt-\d+(?:-[a-f0-9]{8})?$/.test(entry));
|
|
366
370
|
}
|
|
367
371
|
|
package/src/local/stdio.mjs
CHANGED
|
@@ -17,9 +17,9 @@ const MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
|
17
17
|
const SLOW_TOOL_CALL_MS = 30_000;
|
|
18
18
|
const PACKAGE_VERSION = String(JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version);
|
|
19
19
|
|
|
20
|
-
export async function runStdioServer({ workspace, policy, logLevel = "info", jobRoot = "", resources = {}, resourceStatePath = "" }) {
|
|
20
|
+
export async function runStdioServer({ workspace, policy, logLevel = "info", jobRoot = "", resources = {}, resourceStatePath = "", browserStateRoot = "" }) {
|
|
21
21
|
const logger = createLogger({ component: "stdio", level: logLevel, stderrOnly: true, color: false });
|
|
22
|
-
const runtime = new LocalRuntime({ workspace, policy, logger, jobRoot, resources, resourceStatePath });
|
|
22
|
+
const runtime = new LocalRuntime({ workspace, policy, logger, jobRoot, resources, resourceStatePath, browserStateRoot });
|
|
23
23
|
const pending = new Map();
|
|
24
24
|
let negotiatedVersion = MCP_PROTOCOL_VERSION;
|
|
25
25
|
let initialized = false;
|
|
@@ -52,7 +52,7 @@ export async function runStdioServer({ workspace, policy, logLevel = "info", job
|
|
|
52
52
|
async function handleLine(line) {
|
|
53
53
|
const message = parseJsonRpcLine(line, send);
|
|
54
54
|
if (!message || typeof message.method !== "string") return;
|
|
55
|
-
if (handleControlMessage(message)) return;
|
|
55
|
+
if (await handleControlMessage(message)) return;
|
|
56
56
|
if (!initialized) {
|
|
57
57
|
send(rpcError(message.id, -32002, "Server is not initialized"));
|
|
58
58
|
return;
|
|
@@ -60,14 +60,14 @@ export async function runStdioServer({ workspace, policy, logLevel = "info", job
|
|
|
60
60
|
await handleInitializedMessage(message);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function handleControlMessage(message) {
|
|
63
|
+
async function handleControlMessage(message) {
|
|
64
64
|
if (message.method === "initialize") {
|
|
65
65
|
const requested = asObject(message.params).protocolVersion;
|
|
66
66
|
negotiatedVersion = typeof requested === "string" && MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
|
|
67
67
|
? requested
|
|
68
68
|
: MCP_PROTOCOL_VERSION;
|
|
69
69
|
initialized = true;
|
|
70
|
-
send(rpcResult(message.id, initializationResult(negotiatedVersion)));
|
|
70
|
+
send(rpcResult(message.id, await initializationResult(negotiatedVersion, runtime)));
|
|
71
71
|
return true;
|
|
72
72
|
}
|
|
73
73
|
if (message.method === "notifications/initialized") return true;
|
|
@@ -148,7 +148,12 @@ function parseJsonRpcLine(line, send) {
|
|
|
148
148
|
return message;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
function initializationResult(protocolVersion) {
|
|
151
|
+
async function initializationResult(protocolVersion, runtime) {
|
|
152
|
+
const bootstrap = await runtime.sessionBootstrap({ path: "." }).catch((error) => {
|
|
153
|
+
runtime.logger?.debug?.("session bootstrap unavailable during initialize", { error_class: classifyOperationalError(error) });
|
|
154
|
+
return null;
|
|
155
|
+
});
|
|
156
|
+
const localInstructions = bootstrap?.instructions ? `\n\n--- LOCAL SESSION INSTRUCTIONS ---\n${bootstrap.instructions}` : "";
|
|
152
157
|
return {
|
|
153
158
|
protocolVersion,
|
|
154
159
|
capabilities: { tools: { listChanged: false }, logging: {} },
|
|
@@ -158,7 +163,7 @@ function initializationResult(protocolVersion) {
|
|
|
158
163
|
version: PACKAGE_VERSION,
|
|
159
164
|
description: "Workspace-scoped local coding tools over MCP stdio or authenticated remote relay.",
|
|
160
165
|
},
|
|
161
|
-
instructions: MCP_INSTRUCTIONS
|
|
166
|
+
instructions: `${MCP_INSTRUCTIONS}${localInstructions}`,
|
|
162
167
|
};
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
],
|
|
9
9
|
"instructions": [
|
|
10
10
|
"You are connected to a local workspace through machine-bridge-mcp.",
|
|
11
|
-
"
|
|
11
|
+
"At the start of each substantive local task, call resolve_task_capabilities with the current user request and target path. Apply the returned session/project instructions, follow the automatically selected relevant skill when present, and prefer registered local commands for repeatable workflows.",
|
|
12
|
+
"The MCP initialize response automatically appends conservative built-in working agreements, bounded automatic project facts, the configured global model_instructions_file, and the current root instruction chain when the local daemon is reachable; session_bootstrap exposes the same material explicitly.",
|
|
13
|
+
"For existing-browser work, use the paired Machine Bridge browser extension. It operates the user current Chromium profile and tabs, can inspect DOM/source, fill complex forms, and inject sensitive text from registered local resources without returning those values.",
|
|
14
|
+
"For local application work, use structured application discovery and Accessibility actions. Arbitrary caller-supplied AppleScript or JavaScript is intentionally not accepted.",
|
|
12
15
|
"Remote mode uses a Cloudflare relay; stdio mode runs on the local machine. File and command operations execute on the user's local runtime, not in the Worker.",
|
|
13
16
|
"Filesystem scope and path display follow the active local policy; the default full profile is unrestricted.",
|
|
14
17
|
"Filename sensitivity is not classified by this server; the MCP host, connector gateway, local OS, or endpoint-security software may enforce additional independent rules.",
|