@remixhq/mcp 0.1.21 → 0.1.22
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/dist/cli.js +882 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +882 -2
- package/dist/index.js.map +1 -1
- package/dist/server.js +882 -2
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -361,6 +361,19 @@ function assertConfirm(confirm, operation) {
|
|
|
361
361
|
if (confirm) return;
|
|
362
362
|
throw createPolicyError(`${operation} requires explicit confirmation.`, "Pass confirm=true to run this tool.");
|
|
363
363
|
}
|
|
364
|
+
function truncateText(policy, value) {
|
|
365
|
+
const originalChars = value.length;
|
|
366
|
+
if (originalChars <= policy.maxDiffOutputChars) {
|
|
367
|
+
return { value, truncated: false, originalChars };
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
value: `${value.slice(0, policy.maxDiffOutputChars)}
|
|
371
|
+
|
|
372
|
+
[truncated ${originalChars - policy.maxDiffOutputChars} chars]`,
|
|
373
|
+
truncated: true,
|
|
374
|
+
originalChars
|
|
375
|
+
};
|
|
376
|
+
}
|
|
364
377
|
|
|
365
378
|
// src/bootstrap/context.ts
|
|
366
379
|
function createServerContext(params) {
|
|
@@ -380,6 +393,8 @@ function createServerContext(params) {
|
|
|
380
393
|
}
|
|
381
394
|
|
|
382
395
|
// src/tools/collab/register.ts
|
|
396
|
+
import fs from "fs/promises";
|
|
397
|
+
import path3 from "path";
|
|
383
398
|
import { z as z3 } from "zod";
|
|
384
399
|
|
|
385
400
|
// src/contracts/collab.ts
|
|
@@ -833,7 +848,7 @@ function collectWarnings(value) {
|
|
|
833
848
|
function collectResultWarnings(value) {
|
|
834
849
|
return collectWarnings(value.warnings);
|
|
835
850
|
}
|
|
836
|
-
function
|
|
851
|
+
function truncateText2(value, maxChars) {
|
|
837
852
|
if (value.length <= maxChars) {
|
|
838
853
|
return {
|
|
839
854
|
text: value,
|
|
@@ -1086,7 +1101,7 @@ async function viewMergeRequest(params) {
|
|
|
1086
1101
|
api,
|
|
1087
1102
|
mrId: params.mrId
|
|
1088
1103
|
});
|
|
1089
|
-
const truncatedDiff = params.includeUnifiedDiff ?
|
|
1104
|
+
const truncatedDiff = params.includeUnifiedDiff ? truncateText2(review.unifiedDiff, params.diffMaxChars) : null;
|
|
1090
1105
|
return {
|
|
1091
1106
|
data: {
|
|
1092
1107
|
mergeRequest: review.mergeRequest,
|
|
@@ -1780,6 +1795,243 @@ function spawnHistoryImportDetached(repoRoot, options) {
|
|
|
1780
1795
|
}
|
|
1781
1796
|
|
|
1782
1797
|
// src/tools/collab/register.ts
|
|
1798
|
+
var runtimeEnvScopeSchema = {
|
|
1799
|
+
cwd: z3.string().trim().min(1).optional(),
|
|
1800
|
+
requestId: z3.string().trim().min(1).optional(),
|
|
1801
|
+
projectId: z3.string().trim().min(1).optional(),
|
|
1802
|
+
repoFingerprint: z3.string().trim().min(1).optional(),
|
|
1803
|
+
environment: z3.string().trim().min(1).default("development").optional(),
|
|
1804
|
+
target: z3.string().trim().min(1).default("sandbox").optional()
|
|
1805
|
+
};
|
|
1806
|
+
var runtimeEnvMetadataSchema = z3.object({
|
|
1807
|
+
id: z3.string(),
|
|
1808
|
+
projectId: z3.string(),
|
|
1809
|
+
repoFingerprint: z3.string(),
|
|
1810
|
+
environment: z3.string(),
|
|
1811
|
+
target: z3.string(),
|
|
1812
|
+
name: z3.string(),
|
|
1813
|
+
inheritancePolicy: z3.string(),
|
|
1814
|
+
createdBy: z3.string(),
|
|
1815
|
+
updatedBy: z3.string().nullable(),
|
|
1816
|
+
createdAt: z3.string(),
|
|
1817
|
+
updatedAt: z3.string(),
|
|
1818
|
+
version: z3.number()
|
|
1819
|
+
});
|
|
1820
|
+
var runtimeEnvListSuccessSchema = makeSuccessSchema(
|
|
1821
|
+
z3.object({
|
|
1822
|
+
items: z3.array(runtimeEnvMetadataSchema)
|
|
1823
|
+
})
|
|
1824
|
+
);
|
|
1825
|
+
var runtimeEnvWriteSuccessSchema = makeSuccessSchema(
|
|
1826
|
+
z3.object({
|
|
1827
|
+
item: runtimeEnvMetadataSchema.optional(),
|
|
1828
|
+
deleted: z3.string().optional()
|
|
1829
|
+
})
|
|
1830
|
+
);
|
|
1831
|
+
var runtimeTargetScopeSchema = {
|
|
1832
|
+
cwd: z3.string().trim().min(1).optional(),
|
|
1833
|
+
requestId: z3.string().trim().min(1).optional(),
|
|
1834
|
+
projectId: z3.string().trim().min(1).optional(),
|
|
1835
|
+
repoFingerprint: z3.string().trim().min(1).optional(),
|
|
1836
|
+
environment: z3.string().trim().min(1).default("development").optional()
|
|
1837
|
+
};
|
|
1838
|
+
var runtimeTargetAppSchema = {
|
|
1839
|
+
cwd: z3.string().trim().min(1).optional(),
|
|
1840
|
+
requestId: z3.string().trim().min(1).optional(),
|
|
1841
|
+
appId: z3.string().trim().min(1).optional(),
|
|
1842
|
+
environment: z3.string().trim().min(1).default("development").optional()
|
|
1843
|
+
};
|
|
1844
|
+
var runtimeTargetMetadataSchema = z3.object({
|
|
1845
|
+
id: z3.string().optional(),
|
|
1846
|
+
projectId: z3.string().optional(),
|
|
1847
|
+
repoFingerprint: z3.string(),
|
|
1848
|
+
environment: z3.string(),
|
|
1849
|
+
targetId: z3.string(),
|
|
1850
|
+
targetKind: z3.enum(["web", "cli", "mobile", "test", "worker"]),
|
|
1851
|
+
commandKind: z3.enum(["install", "dev", "build", "preview", "test", "custom"]),
|
|
1852
|
+
command: z3.string(),
|
|
1853
|
+
cwd: z3.string(),
|
|
1854
|
+
port: z3.number().nullable(),
|
|
1855
|
+
readiness: z3.record(z3.unknown()).or(z3.object({}).passthrough()),
|
|
1856
|
+
inheritancePolicy: z3.enum(["project_family", "none"]).optional(),
|
|
1857
|
+
enabled: z3.boolean(),
|
|
1858
|
+
version: z3.number().optional()
|
|
1859
|
+
});
|
|
1860
|
+
var runtimeTargetListSuccessSchema = makeSuccessSchema(z3.object({ items: z3.array(runtimeTargetMetadataSchema) }));
|
|
1861
|
+
var runtimeTargetWriteSuccessSchema = makeSuccessSchema(
|
|
1862
|
+
z3.object({
|
|
1863
|
+
item: runtimeTargetMetadataSchema.optional(),
|
|
1864
|
+
items: z3.array(runtimeTargetMetadataSchema).optional(),
|
|
1865
|
+
deleted: z3.string().optional(),
|
|
1866
|
+
result: z3.unknown().optional(),
|
|
1867
|
+
logs: z3.string().optional()
|
|
1868
|
+
})
|
|
1869
|
+
);
|
|
1870
|
+
var sandboxCommandRunSchema = z3.object({
|
|
1871
|
+
id: z3.string(),
|
|
1872
|
+
appId: z3.string(),
|
|
1873
|
+
actorUserId: z3.string().nullable(),
|
|
1874
|
+
sandboxExternalId: z3.string(),
|
|
1875
|
+
command: z3.string(),
|
|
1876
|
+
cwd: z3.string(),
|
|
1877
|
+
environment: z3.string(),
|
|
1878
|
+
status: z3.enum(["succeeded", "failed", "timed_out"]),
|
|
1879
|
+
exitCode: z3.number().nullable(),
|
|
1880
|
+
stdout: z3.string(),
|
|
1881
|
+
stderr: z3.string(),
|
|
1882
|
+
startedAt: z3.string(),
|
|
1883
|
+
finishedAt: z3.string(),
|
|
1884
|
+
durationMs: z3.number(),
|
|
1885
|
+
metadata: z3.record(z3.unknown()).nullable().optional()
|
|
1886
|
+
});
|
|
1887
|
+
var sandboxCommandSuccessSchema = makeSuccessSchema(
|
|
1888
|
+
z3.object({
|
|
1889
|
+
result: sandboxCommandRunSchema.optional(),
|
|
1890
|
+
items: z3.array(sandboxCommandRunSchema).optional()
|
|
1891
|
+
})
|
|
1892
|
+
);
|
|
1893
|
+
var triggerStepInputSchema = z3.object({
|
|
1894
|
+
name: z3.string().trim().min(1).nullable().optional(),
|
|
1895
|
+
type: z3.enum(["shell", "runtime_target"]),
|
|
1896
|
+
command: z3.string().trim().min(1).nullable().optional(),
|
|
1897
|
+
runtimeTargetId: z3.string().trim().min(1).nullable().optional(),
|
|
1898
|
+
cwd: z3.string().trim().min(1).nullable().optional(),
|
|
1899
|
+
timeoutMs: z3.number().int().positive().nullable().optional(),
|
|
1900
|
+
continueOnFailure: z3.boolean().optional(),
|
|
1901
|
+
metadata: z3.record(z3.unknown()).optional()
|
|
1902
|
+
});
|
|
1903
|
+
var triggerScopeSchema = {
|
|
1904
|
+
cwd: z3.string().trim().min(1).optional(),
|
|
1905
|
+
requestId: z3.string().trim().min(1).optional(),
|
|
1906
|
+
projectId: z3.string().trim().min(1).optional(),
|
|
1907
|
+
repoFingerprint: z3.string().trim().min(1).optional(),
|
|
1908
|
+
environment: z3.string().trim().min(1).default("development").optional(),
|
|
1909
|
+
eventType: z3.string().trim().min(1).optional()
|
|
1910
|
+
};
|
|
1911
|
+
var triggerAppSchema = {
|
|
1912
|
+
cwd: z3.string().trim().min(1).optional(),
|
|
1913
|
+
requestId: z3.string().trim().min(1).optional(),
|
|
1914
|
+
appId: z3.string().trim().min(1).optional(),
|
|
1915
|
+
environment: z3.string().trim().min(1).default("development").optional()
|
|
1916
|
+
};
|
|
1917
|
+
var triggerSuccessSchema = makeSuccessSchema(
|
|
1918
|
+
z3.object({
|
|
1919
|
+
result: z3.unknown().optional(),
|
|
1920
|
+
item: z3.unknown().optional(),
|
|
1921
|
+
items: z3.array(z3.unknown()).optional(),
|
|
1922
|
+
deleted: z3.string().optional()
|
|
1923
|
+
})
|
|
1924
|
+
);
|
|
1925
|
+
var DETECTION_FILES = [
|
|
1926
|
+
"package.json",
|
|
1927
|
+
"package-lock.json",
|
|
1928
|
+
"pnpm-lock.yaml",
|
|
1929
|
+
"yarn.lock",
|
|
1930
|
+
"bun.lockb",
|
|
1931
|
+
"bun.lock",
|
|
1932
|
+
"vite.config.ts",
|
|
1933
|
+
"vite.config.js",
|
|
1934
|
+
"next.config.js",
|
|
1935
|
+
"next.config.mjs",
|
|
1936
|
+
"astro.config.mjs",
|
|
1937
|
+
"astro.config.ts"
|
|
1938
|
+
];
|
|
1939
|
+
async function resolveRuntimeEnvScope(input) {
|
|
1940
|
+
const { repoRoot, binding } = await loadBindingContext(input.cwd);
|
|
1941
|
+
const projectId = input.projectId?.trim() || binding?.projectId || null;
|
|
1942
|
+
const repoFingerprint = input.repoFingerprint?.trim() || binding?.repoFingerprint || null;
|
|
1943
|
+
if (!projectId) throw makeNotBoundError("Project id was not provided and the current repository is not bound to Remix.");
|
|
1944
|
+
if (!repoFingerprint) {
|
|
1945
|
+
throw makeNotBoundError(
|
|
1946
|
+
"Repo fingerprint was not provided and the current repository binding does not include one.",
|
|
1947
|
+
"Pass repoFingerprint explicitly or run from a bound repository."
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
return {
|
|
1951
|
+
repoRoot,
|
|
1952
|
+
projectId,
|
|
1953
|
+
scope: {
|
|
1954
|
+
repoFingerprint,
|
|
1955
|
+
environment: input.environment?.trim() || "development",
|
|
1956
|
+
target: input.target?.trim() || "sandbox"
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
async function resolveRuntimeTargetScope(input) {
|
|
1961
|
+
const { repoRoot, binding } = await loadBindingContext(input.cwd);
|
|
1962
|
+
const projectId = input.projectId?.trim() || binding?.projectId || null;
|
|
1963
|
+
const repoFingerprint = input.repoFingerprint?.trim() || binding?.repoFingerprint || null;
|
|
1964
|
+
if (!projectId) throw makeNotBoundError("Project id was not provided and the current repository is not bound to Remix.");
|
|
1965
|
+
if (!repoFingerprint) {
|
|
1966
|
+
throw makeNotBoundError(
|
|
1967
|
+
"Repo fingerprint was not provided and the current repository binding does not include one.",
|
|
1968
|
+
"Pass repoFingerprint explicitly or run from a bound repository."
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
return {
|
|
1972
|
+
repoRoot,
|
|
1973
|
+
projectId,
|
|
1974
|
+
scope: {
|
|
1975
|
+
repoFingerprint,
|
|
1976
|
+
environment: input.environment?.trim() || "development"
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
async function resolveTriggerScope(input) {
|
|
1981
|
+
const { repoRoot, binding } = await loadBindingContext(input.cwd);
|
|
1982
|
+
const projectId = input.projectId?.trim() || binding?.projectId || null;
|
|
1983
|
+
const repoFingerprint = input.repoFingerprint?.trim() || binding?.repoFingerprint || null;
|
|
1984
|
+
if (!projectId) throw makeNotBoundError("Project id was not provided and the current repository is not bound to Remix.");
|
|
1985
|
+
if (!repoFingerprint) {
|
|
1986
|
+
throw makeNotBoundError(
|
|
1987
|
+
"Repo fingerprint was not provided and the current repository binding does not include one.",
|
|
1988
|
+
"Pass repoFingerprint explicitly or run from a bound repository."
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
return {
|
|
1992
|
+
repoRoot,
|
|
1993
|
+
projectId,
|
|
1994
|
+
scope: {
|
|
1995
|
+
repoFingerprint,
|
|
1996
|
+
environment: input.environment?.trim() || "development",
|
|
1997
|
+
eventType: input.eventType?.trim() || void 0
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
async function resolveRuntimeApp(input) {
|
|
2002
|
+
const { repoRoot, binding } = await loadBindingContext(input.cwd);
|
|
2003
|
+
const appId = input.appId?.trim() || binding?.currentAppId || null;
|
|
2004
|
+
if (!appId) throw makeNotBoundError("App id was not provided and the current repository is not bound to Remix.");
|
|
2005
|
+
return { repoRoot, appId };
|
|
2006
|
+
}
|
|
2007
|
+
async function readRuntimeDetectionFiles(repoRoot) {
|
|
2008
|
+
const root = repoRoot ?? process.cwd();
|
|
2009
|
+
const files = {};
|
|
2010
|
+
for (const file of DETECTION_FILES) {
|
|
2011
|
+
try {
|
|
2012
|
+
files[file] = await fs.readFile(path3.join(root, file), "utf8");
|
|
2013
|
+
} catch {
|
|
2014
|
+
files[file] = null;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return files;
|
|
2018
|
+
}
|
|
2019
|
+
function redactRuntimeLogs(logs) {
|
|
2020
|
+
return logs.replace(/\b([A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|API_KEY)[A-Z0-9_]*)=([^\s]+)/gi, "$1=[REDACTED]");
|
|
2021
|
+
}
|
|
2022
|
+
function runtimeStopWarning(result) {
|
|
2023
|
+
if (result.state === "not_stoppable") return "Target is not long-running, so there is no runtime process to stop.";
|
|
2024
|
+
if (result.state === "not_running") return "No running process was found for this target.";
|
|
2025
|
+
if (result.state === "running") return "Stop was requested, but the process still appears to be running.";
|
|
2026
|
+
if (result.stopped === false) return "No runtime process was stopped.";
|
|
2027
|
+
return null;
|
|
2028
|
+
}
|
|
2029
|
+
function runtimePreviewWarning(result) {
|
|
2030
|
+
if (!result || typeof result !== "object" || !("previewUrl" in result)) return null;
|
|
2031
|
+
const previewUrl = result.previewUrl;
|
|
2032
|
+
if (typeof previewUrl !== "string" || !previewUrl) return null;
|
|
2033
|
+
return "This result includes a temporary E2B preview URL for a running web target. Treat it as runtime state, not a stable or shareable Remix URL.";
|
|
2034
|
+
}
|
|
1783
2035
|
function getAnnotations(access, options) {
|
|
1784
2036
|
if (access === "read") {
|
|
1785
2037
|
return {
|
|
@@ -1913,6 +2165,12 @@ function registerTool(server, context, params) {
|
|
|
1913
2165
|
}
|
|
1914
2166
|
);
|
|
1915
2167
|
}
|
|
2168
|
+
var runtimeDetectInputSchema = {
|
|
2169
|
+
...runtimeTargetScopeSchema,
|
|
2170
|
+
appId: z3.string().trim().min(1).optional(),
|
|
2171
|
+
apply: z3.boolean().optional(),
|
|
2172
|
+
confirm: z3.boolean().optional()
|
|
2173
|
+
};
|
|
1916
2174
|
function registerCollabTools(server, context) {
|
|
1917
2175
|
registerTool(server, context, {
|
|
1918
2176
|
name: "remix_collab_status",
|
|
@@ -1929,6 +2187,628 @@ function registerCollabTools(server, context) {
|
|
|
1929
2187
|
});
|
|
1930
2188
|
}
|
|
1931
2189
|
});
|
|
2190
|
+
registerTool(server, context, {
|
|
2191
|
+
name: "remix_collab_env_list",
|
|
2192
|
+
description: "List metadata for Remix runtime environment variables for the bound project/repo. Values are never returned.",
|
|
2193
|
+
access: "read",
|
|
2194
|
+
inputSchema: runtimeEnvScopeSchema,
|
|
2195
|
+
outputSchema: runtimeEnvListSuccessSchema,
|
|
2196
|
+
run: async (args) => {
|
|
2197
|
+
const input = z3.object(runtimeEnvScopeSchema).parse(args);
|
|
2198
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2199
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeEnvScope({ ...input, cwd });
|
|
2200
|
+
const api = await createApiClient();
|
|
2201
|
+
const items = unwrapResponseObject(
|
|
2202
|
+
await api.listProjectRuntimeEnv(projectId, scope),
|
|
2203
|
+
"runtime env list"
|
|
2204
|
+
);
|
|
2205
|
+
return {
|
|
2206
|
+
data: { items },
|
|
2207
|
+
warnings: ["Secret values are write-only and are not returned by this tool."],
|
|
2208
|
+
logContext: { repoRoot }
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
registerTool(server, context, {
|
|
2213
|
+
name: "remix_collab_env_set",
|
|
2214
|
+
description: "Create or rotate a Remix runtime environment variable. Requires confirm=true. The provided value is sent to the backend Vault path and is never echoed in the result.",
|
|
2215
|
+
access: "remote_write",
|
|
2216
|
+
inputSchema: {
|
|
2217
|
+
...runtimeEnvScopeSchema,
|
|
2218
|
+
name: z3.string().trim().min(1),
|
|
2219
|
+
value: z3.string(),
|
|
2220
|
+
confirm: z3.boolean().optional()
|
|
2221
|
+
},
|
|
2222
|
+
outputSchema: runtimeEnvWriteSuccessSchema,
|
|
2223
|
+
run: async (args) => {
|
|
2224
|
+
const input = z3.object({
|
|
2225
|
+
...runtimeEnvScopeSchema,
|
|
2226
|
+
name: z3.string().trim().min(1),
|
|
2227
|
+
value: z3.string(),
|
|
2228
|
+
confirm: z3.boolean().optional()
|
|
2229
|
+
}).parse(args);
|
|
2230
|
+
assertConfirm(input.confirm, "Setting a runtime environment variable");
|
|
2231
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2232
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeEnvScope({ ...input, cwd });
|
|
2233
|
+
const api = await createApiClient();
|
|
2234
|
+
const item = unwrapResponseObject(
|
|
2235
|
+
await api.setProjectRuntimeEnv(projectId, input.name, { ...scope, value: input.value }),
|
|
2236
|
+
"runtime env set"
|
|
2237
|
+
);
|
|
2238
|
+
return {
|
|
2239
|
+
data: { item },
|
|
2240
|
+
warnings: ["Secret value accepted but not returned."],
|
|
2241
|
+
logContext: { repoRoot }
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
registerTool(server, context, {
|
|
2246
|
+
name: "remix_collab_env_delete",
|
|
2247
|
+
description: "Delete a Remix runtime environment variable. Requires confirm=true.",
|
|
2248
|
+
access: "remote_write",
|
|
2249
|
+
inputSchema: {
|
|
2250
|
+
...runtimeEnvScopeSchema,
|
|
2251
|
+
name: z3.string().trim().min(1),
|
|
2252
|
+
confirm: z3.boolean().optional()
|
|
2253
|
+
},
|
|
2254
|
+
outputSchema: runtimeEnvWriteSuccessSchema,
|
|
2255
|
+
run: async (args) => {
|
|
2256
|
+
const input = z3.object({
|
|
2257
|
+
...runtimeEnvScopeSchema,
|
|
2258
|
+
name: z3.string().trim().min(1),
|
|
2259
|
+
confirm: z3.boolean().optional()
|
|
2260
|
+
}).parse(args);
|
|
2261
|
+
assertConfirm(input.confirm, "Deleting a runtime environment variable");
|
|
2262
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2263
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeEnvScope({ ...input, cwd });
|
|
2264
|
+
const api = await createApiClient();
|
|
2265
|
+
await api.deleteProjectRuntimeEnv(projectId, input.name, scope);
|
|
2266
|
+
return {
|
|
2267
|
+
data: { deleted: input.name },
|
|
2268
|
+
warnings: ["Secret value was not read or returned."],
|
|
2269
|
+
logContext: { repoRoot }
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
registerTool(server, context, {
|
|
2274
|
+
name: "remix_collab_runtime_list",
|
|
2275
|
+
description: "List Remix runtime targets (commands) for the bound project/repo. Returns metadata only; does not execute commands.",
|
|
2276
|
+
access: "read",
|
|
2277
|
+
inputSchema: runtimeTargetScopeSchema,
|
|
2278
|
+
outputSchema: runtimeTargetListSuccessSchema,
|
|
2279
|
+
run: async (args) => {
|
|
2280
|
+
const input = z3.object(runtimeTargetScopeSchema).parse(args);
|
|
2281
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2282
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeTargetScope({ ...input, cwd });
|
|
2283
|
+
const api = await createApiClient();
|
|
2284
|
+
const items = unwrapResponseObject(
|
|
2285
|
+
await api.listProjectRuntimeTargets(projectId, scope),
|
|
2286
|
+
"runtime target list"
|
|
2287
|
+
);
|
|
2288
|
+
return {
|
|
2289
|
+
data: { items },
|
|
2290
|
+
logContext: { repoRoot }
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
registerTool(server, context, {
|
|
2295
|
+
name: "remix_collab_runtime_detect",
|
|
2296
|
+
description: "Detect runtime install/dev/build targets from repository files or, when appId is set, from the app's E2B workspace. Passing apply=true saves detected targets remotely and requires confirm=true plus remote-write policy.",
|
|
2297
|
+
access: "read",
|
|
2298
|
+
annotations: {
|
|
2299
|
+
readOnlyHint: false,
|
|
2300
|
+
idempotentHint: false,
|
|
2301
|
+
openWorldHint: false
|
|
2302
|
+
},
|
|
2303
|
+
inputSchema: runtimeDetectInputSchema,
|
|
2304
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2305
|
+
run: async (args) => {
|
|
2306
|
+
const input = z3.object(runtimeDetectInputSchema).parse(args);
|
|
2307
|
+
const apply = Boolean(input.apply);
|
|
2308
|
+
if (apply) {
|
|
2309
|
+
assertToolAccess(context.policy, "remote_write");
|
|
2310
|
+
assertConfirm(input.confirm, "Applying detected runtime targets");
|
|
2311
|
+
}
|
|
2312
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2313
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeTargetScope({ ...input, cwd });
|
|
2314
|
+
const api = await createApiClient();
|
|
2315
|
+
const trimmedAppId = input.appId?.trim();
|
|
2316
|
+
const files = trimmedAppId ? void 0 : await readRuntimeDetectionFiles(repoRoot);
|
|
2317
|
+
const detected = unwrapResponseObject(
|
|
2318
|
+
await api.detectProjectRuntimeTargets(projectId, {
|
|
2319
|
+
...scope,
|
|
2320
|
+
apply,
|
|
2321
|
+
appId: trimmedAppId,
|
|
2322
|
+
...files !== void 0 ? { files } : {}
|
|
2323
|
+
}),
|
|
2324
|
+
"runtime targets detect"
|
|
2325
|
+
);
|
|
2326
|
+
const warnings = trimmedAppId ? ["Detection read files from the sandbox workspace for appId.", "Sandbox must exist for sandbox-backed detection."] : apply ? [`Applied ${detected.length} runtime target definitions.`] : ["Dry run only.", "Pass apply=true plus confirm=true to persist detected targets remotely."];
|
|
2327
|
+
return {
|
|
2328
|
+
data: { items: detected },
|
|
2329
|
+
logContext: { repoRoot },
|
|
2330
|
+
warnings
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
registerTool(server, context, {
|
|
2335
|
+
name: "remix_collab_runtime_set",
|
|
2336
|
+
description: "Create or update a Remix runtime target for install/dev/build/preview/etc. Requires confirm=true. Does not execute the command.",
|
|
2337
|
+
access: "remote_write",
|
|
2338
|
+
inputSchema: {
|
|
2339
|
+
...runtimeTargetScopeSchema,
|
|
2340
|
+
targetId: z3.string().trim().min(1),
|
|
2341
|
+
targetKind: z3.enum(["web", "cli", "mobile", "test", "worker"]),
|
|
2342
|
+
commandKind: z3.enum(["install", "dev", "build", "preview", "test", "custom"]),
|
|
2343
|
+
command: z3.string().trim().min(1),
|
|
2344
|
+
targetCwd: z3.string().trim().min(1).optional().default("/home/user/app"),
|
|
2345
|
+
port: z3.number().int().positive().max(65535).nullable().optional(),
|
|
2346
|
+
readiness: z3.record(z3.unknown()).optional(),
|
|
2347
|
+
inheritancePolicy: z3.enum(["project_family", "none"]).optional(),
|
|
2348
|
+
enabled: z3.boolean().optional(),
|
|
2349
|
+
confirm: z3.boolean().optional()
|
|
2350
|
+
},
|
|
2351
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2352
|
+
run: async (args) => {
|
|
2353
|
+
const input = z3.object({
|
|
2354
|
+
...runtimeTargetScopeSchema,
|
|
2355
|
+
targetId: z3.string().trim().min(1),
|
|
2356
|
+
targetKind: z3.enum(["web", "cli", "mobile", "test", "worker"]),
|
|
2357
|
+
commandKind: z3.enum(["install", "dev", "build", "preview", "test", "custom"]),
|
|
2358
|
+
command: z3.string().trim().min(1),
|
|
2359
|
+
targetCwd: z3.string().trim().min(1).optional().default("/home/user/app"),
|
|
2360
|
+
port: z3.number().int().positive().max(65535).nullable().optional(),
|
|
2361
|
+
readiness: z3.record(z3.unknown()).optional(),
|
|
2362
|
+
inheritancePolicy: z3.enum(["project_family", "none"]).optional(),
|
|
2363
|
+
enabled: z3.boolean().optional(),
|
|
2364
|
+
confirm: z3.boolean().optional()
|
|
2365
|
+
}).parse(args);
|
|
2366
|
+
assertConfirm(input.confirm, "Updating a runtime target");
|
|
2367
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2368
|
+
const { repoRoot, projectId, scope } = await resolveRuntimeTargetScope({ ...input, cwd });
|
|
2369
|
+
const api = await createApiClient();
|
|
2370
|
+
const item = unwrapResponseObject(
|
|
2371
|
+
await api.setProjectRuntimeTarget(projectId, input.targetId, {
|
|
2372
|
+
...scope,
|
|
2373
|
+
targetKind: input.targetKind,
|
|
2374
|
+
commandKind: input.commandKind,
|
|
2375
|
+
command: input.command,
|
|
2376
|
+
cwd: input.targetCwd?.trim() ?? "/home/user/app",
|
|
2377
|
+
port: input.port ?? null,
|
|
2378
|
+
...input.readiness !== void 0 ? { readiness: input.readiness } : {},
|
|
2379
|
+
inheritancePolicy: input.inheritancePolicy ?? "project_family",
|
|
2380
|
+
enabled: input.enabled ?? true
|
|
2381
|
+
}),
|
|
2382
|
+
"runtime target set"
|
|
2383
|
+
);
|
|
2384
|
+
return { data: { item }, warnings: ["Command definitions are persisted but not executed by this tool."], logContext: { repoRoot } };
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
registerTool(server, context, {
|
|
2388
|
+
name: "remix_collab_runtime_run",
|
|
2389
|
+
description: "Run a Remix runtime target in the app's E2B sandbox (explicit execution only \u2014 not used by collaboration recording). Running web targets with ports may return a temporary E2B preview URL. Requires confirm=true.",
|
|
2390
|
+
access: "remote_write",
|
|
2391
|
+
inputSchema: {
|
|
2392
|
+
...runtimeTargetAppSchema,
|
|
2393
|
+
targetId: z3.string().trim().min(1),
|
|
2394
|
+
forceInstall: z3.boolean().optional(),
|
|
2395
|
+
forceRestart: z3.boolean().optional(),
|
|
2396
|
+
confirm: z3.boolean().optional()
|
|
2397
|
+
},
|
|
2398
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2399
|
+
run: async (args) => {
|
|
2400
|
+
const input = z3.object({
|
|
2401
|
+
...runtimeTargetAppSchema,
|
|
2402
|
+
targetId: z3.string().trim().min(1),
|
|
2403
|
+
forceInstall: z3.boolean().optional(),
|
|
2404
|
+
forceRestart: z3.boolean().optional(),
|
|
2405
|
+
confirm: z3.boolean().optional()
|
|
2406
|
+
}).parse(args);
|
|
2407
|
+
assertConfirm(input.confirm, "Running a runtime target in sandbox");
|
|
2408
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2409
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2410
|
+
const api = await createApiClient();
|
|
2411
|
+
const result = unwrapResponseObject(
|
|
2412
|
+
await api.runAppRuntimeTarget(appId, input.targetId, {
|
|
2413
|
+
environment: input.environment?.trim() || "development",
|
|
2414
|
+
forceInstall: Boolean(input.forceInstall),
|
|
2415
|
+
forceRestart: Boolean(input.forceRestart)
|
|
2416
|
+
}),
|
|
2417
|
+
"runtime target run"
|
|
2418
|
+
);
|
|
2419
|
+
const previewWarning = runtimePreviewWarning(result);
|
|
2420
|
+
return {
|
|
2421
|
+
data: { result },
|
|
2422
|
+
warnings: [
|
|
2423
|
+
"Process output may include sensitive values; inspect logs separately with remix_collab_runtime_logs.",
|
|
2424
|
+
...previewWarning ? [previewWarning] : []
|
|
2425
|
+
],
|
|
2426
|
+
logContext: { repoRoot, appId }
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
registerTool(server, context, {
|
|
2431
|
+
name: "remix_collab_runtime_logs",
|
|
2432
|
+
description: "Tail recent Remix runtime stdout/stderr logs for any target. Long-running targets return live logs; one-shot targets return the latest completed run. Output is lightly redacted; secret values must still be handled carefully.",
|
|
2433
|
+
access: "read",
|
|
2434
|
+
inputSchema: {
|
|
2435
|
+
...runtimeTargetAppSchema,
|
|
2436
|
+
targetId: z3.string().trim().min(1),
|
|
2437
|
+
limit: z3.number().int().positive().max(2e4).optional()
|
|
2438
|
+
},
|
|
2439
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2440
|
+
run: async (args) => {
|
|
2441
|
+
const input = z3.object({
|
|
2442
|
+
...runtimeTargetAppSchema,
|
|
2443
|
+
targetId: z3.string().trim().min(1),
|
|
2444
|
+
limit: z3.number().int().positive().max(2e4).optional()
|
|
2445
|
+
}).parse(args);
|
|
2446
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2447
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2448
|
+
const api = await createApiClient();
|
|
2449
|
+
const bundle = unwrapResponseObject(
|
|
2450
|
+
await api.getAppRuntimeTargetLogs(appId, input.targetId, {
|
|
2451
|
+
environment: input.environment?.trim() || "development",
|
|
2452
|
+
limit: input.limit
|
|
2453
|
+
}),
|
|
2454
|
+
"runtime target logs"
|
|
2455
|
+
);
|
|
2456
|
+
const logs = truncateText(context.policy, redactRuntimeLogs(bundle.logs ?? ""));
|
|
2457
|
+
return {
|
|
2458
|
+
data: {
|
|
2459
|
+
logs: logs.value
|
|
2460
|
+
},
|
|
2461
|
+
warnings: logs.truncated ? [
|
|
2462
|
+
"Log tail was truncated to fit MCP output limits.",
|
|
2463
|
+
"Values resembling tokens/passwords were redacted heuristically.",
|
|
2464
|
+
"One-shot targets show latest completed-run logs; long-running targets show live sandbox logs."
|
|
2465
|
+
] : [
|
|
2466
|
+
"Values resembling tokens/passwords were redacted heuristically.",
|
|
2467
|
+
"One-shot targets show latest completed-run logs; long-running targets show live sandbox logs."
|
|
2468
|
+
],
|
|
2469
|
+
logContext: { repoRoot, appId }
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
});
|
|
2473
|
+
registerTool(server, context, {
|
|
2474
|
+
name: "remix_collab_runtime_status",
|
|
2475
|
+
description: "Read process/readiness status plus latest run metadata for a Remix runtime target. Running web targets with ports may include a temporary E2B preview URL. This is separate from app lifecycle status and does not execute or stop commands.",
|
|
2476
|
+
access: "read",
|
|
2477
|
+
inputSchema: {
|
|
2478
|
+
...runtimeTargetAppSchema,
|
|
2479
|
+
targetId: z3.string().trim().min(1)
|
|
2480
|
+
},
|
|
2481
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2482
|
+
run: async (args) => {
|
|
2483
|
+
const input = z3.object({
|
|
2484
|
+
...runtimeTargetAppSchema,
|
|
2485
|
+
targetId: z3.string().trim().min(1)
|
|
2486
|
+
}).parse(args);
|
|
2487
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2488
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2489
|
+
const api = await createApiClient();
|
|
2490
|
+
const result = unwrapResponseObject(
|
|
2491
|
+
await api.getAppRuntimeTargetStatus(appId, input.targetId, {
|
|
2492
|
+
environment: input.environment?.trim() || "development"
|
|
2493
|
+
}),
|
|
2494
|
+
"runtime target status"
|
|
2495
|
+
);
|
|
2496
|
+
const previewWarning = runtimePreviewWarning(result);
|
|
2497
|
+
return { data: { result }, warnings: previewWarning ? [previewWarning] : void 0, logContext: { repoRoot, appId } };
|
|
2498
|
+
}
|
|
2499
|
+
});
|
|
2500
|
+
registerTool(server, context, {
|
|
2501
|
+
name: "remix_collab_runtime_stop",
|
|
2502
|
+
description: "Stop a long-running Remix runtime target process in E2B. Non-long-running targets report not_stoppable. Requires confirm=true.",
|
|
2503
|
+
access: "remote_write",
|
|
2504
|
+
inputSchema: {
|
|
2505
|
+
...runtimeTargetAppSchema,
|
|
2506
|
+
targetId: z3.string().trim().min(1),
|
|
2507
|
+
confirm: z3.boolean().optional()
|
|
2508
|
+
},
|
|
2509
|
+
outputSchema: runtimeTargetWriteSuccessSchema,
|
|
2510
|
+
run: async (args) => {
|
|
2511
|
+
const input = z3.object({
|
|
2512
|
+
...runtimeTargetAppSchema,
|
|
2513
|
+
targetId: z3.string().trim().min(1),
|
|
2514
|
+
confirm: z3.boolean().optional()
|
|
2515
|
+
}).parse(args);
|
|
2516
|
+
assertConfirm(input.confirm, "Stopping a runtime target process");
|
|
2517
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2518
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2519
|
+
const api = await createApiClient();
|
|
2520
|
+
const result = unwrapResponseObject(
|
|
2521
|
+
await api.stopAppRuntimeTarget(appId, input.targetId, { environment: input.environment?.trim() || "development" }),
|
|
2522
|
+
"runtime target stop"
|
|
2523
|
+
);
|
|
2524
|
+
const warning = runtimeStopWarning(result);
|
|
2525
|
+
return { data: { result }, warnings: warning ? [warning] : void 0, logContext: { repoRoot, appId } };
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2528
|
+
registerTool(server, context, {
|
|
2529
|
+
name: "remix_collab_sandbox_command_run",
|
|
2530
|
+
description: "Run a one-shot shell command inside an app's E2B sandbox. This can mutate sandbox state and dirty the repo. Requires confirm=true.",
|
|
2531
|
+
access: "remote_write",
|
|
2532
|
+
inputSchema: {
|
|
2533
|
+
...runtimeTargetAppSchema,
|
|
2534
|
+
command: z3.string().trim().min(1),
|
|
2535
|
+
commandCwd: z3.string().trim().min(1).optional().default("/home/user/app"),
|
|
2536
|
+
timeoutMs: z3.number().int().min(1e3).max(6e5).optional(),
|
|
2537
|
+
confirm: z3.boolean().optional()
|
|
2538
|
+
},
|
|
2539
|
+
outputSchema: sandboxCommandSuccessSchema,
|
|
2540
|
+
run: async (args) => {
|
|
2541
|
+
const input = z3.object({
|
|
2542
|
+
...runtimeTargetAppSchema,
|
|
2543
|
+
command: z3.string().trim().min(1),
|
|
2544
|
+
commandCwd: z3.string().trim().min(1).optional().default("/home/user/app"),
|
|
2545
|
+
timeoutMs: z3.number().int().min(1e3).max(6e5).optional(),
|
|
2546
|
+
confirm: z3.boolean().optional()
|
|
2547
|
+
}).parse(args);
|
|
2548
|
+
assertConfirm(input.confirm, "Running an arbitrary sandbox command");
|
|
2549
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2550
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2551
|
+
const api = await createApiClient();
|
|
2552
|
+
const run = unwrapResponseObject(
|
|
2553
|
+
await api.runAppSandboxCommand(appId, {
|
|
2554
|
+
command: input.command,
|
|
2555
|
+
cwd: input.commandCwd?.trim() || "/home/user/app",
|
|
2556
|
+
timeoutMs: input.timeoutMs ?? 12e4,
|
|
2557
|
+
environment: input.environment?.trim() || "development"
|
|
2558
|
+
}),
|
|
2559
|
+
"sandbox command run"
|
|
2560
|
+
);
|
|
2561
|
+
const stdout = truncateText(context.policy, typeof run.stdout === "string" ? run.stdout : "");
|
|
2562
|
+
const stderr = truncateText(context.policy, typeof run.stderr === "string" ? run.stderr : "");
|
|
2563
|
+
const result = { ...run, stdout: stdout.value, stderr: stderr.value };
|
|
2564
|
+
return {
|
|
2565
|
+
data: { result },
|
|
2566
|
+
warnings: [
|
|
2567
|
+
"Sandbox commands can mutate files, install tools, change runtime state, and dirty the app repository.",
|
|
2568
|
+
"Returned stdout/stderr are redacted by the backend before persistence.",
|
|
2569
|
+
...stdout.truncated || stderr.truncated ? ["Command output was truncated to fit MCP output limits."] : []
|
|
2570
|
+
],
|
|
2571
|
+
logContext: { repoRoot, appId }
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
registerTool(server, context, {
|
|
2576
|
+
name: "remix_collab_sandbox_command_history",
|
|
2577
|
+
description: "List recent one-shot sandbox commands for an app. Output is redacted and may be truncated.",
|
|
2578
|
+
access: "read",
|
|
2579
|
+
inputSchema: {
|
|
2580
|
+
...runtimeTargetAppSchema,
|
|
2581
|
+
limit: z3.number().int().positive().max(100).optional()
|
|
2582
|
+
},
|
|
2583
|
+
outputSchema: sandboxCommandSuccessSchema,
|
|
2584
|
+
run: async (args) => {
|
|
2585
|
+
const input = z3.object({
|
|
2586
|
+
...runtimeTargetAppSchema,
|
|
2587
|
+
limit: z3.number().int().positive().max(100).optional()
|
|
2588
|
+
}).parse(args);
|
|
2589
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2590
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2591
|
+
const api = await createApiClient();
|
|
2592
|
+
const history = unwrapResponseObject(
|
|
2593
|
+
await api.listAppSandboxCommandHistory(appId, { limit: input.limit ?? 20 }),
|
|
2594
|
+
"sandbox command history"
|
|
2595
|
+
);
|
|
2596
|
+
const items = (history.items ?? []).map((item) => {
|
|
2597
|
+
const stdout = truncateText(context.policy, typeof item.stdout === "string" ? item.stdout : "");
|
|
2598
|
+
const stderr = truncateText(context.policy, typeof item.stderr === "string" ? item.stderr : "");
|
|
2599
|
+
return { ...item, stdout: stdout.value, stderr: stderr.value };
|
|
2600
|
+
});
|
|
2601
|
+
return {
|
|
2602
|
+
data: { items },
|
|
2603
|
+
warnings: ["Sandbox command history contains redacted command output. Large outputs may be truncated for MCP."],
|
|
2604
|
+
logContext: { repoRoot, appId }
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
});
|
|
2608
|
+
registerTool(server, context, {
|
|
2609
|
+
name: "remix_collab_triggers_events",
|
|
2610
|
+
description: "List supported Remix project trigger events and whether each event is currently wired.",
|
|
2611
|
+
access: "read",
|
|
2612
|
+
inputSchema: triggerScopeSchema,
|
|
2613
|
+
outputSchema: triggerSuccessSchema,
|
|
2614
|
+
run: async (args) => {
|
|
2615
|
+
const input = z3.object(triggerScopeSchema).parse(args);
|
|
2616
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2617
|
+
const { repoRoot, projectId } = await resolveTriggerScope({ ...input, cwd, repoFingerprint: input.repoFingerprint ?? "events" });
|
|
2618
|
+
const api = await createApiClient();
|
|
2619
|
+
const items = unwrapResponseObject(
|
|
2620
|
+
await api.listProjectTriggerEvents(projectId),
|
|
2621
|
+
"trigger events"
|
|
2622
|
+
);
|
|
2623
|
+
return { data: { items }, logContext: { repoRoot } };
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
registerTool(server, context, {
|
|
2627
|
+
name: "remix_collab_triggers_list",
|
|
2628
|
+
description: "List Remix project triggers for the bound project/repo/environment.",
|
|
2629
|
+
access: "read",
|
|
2630
|
+
inputSchema: triggerScopeSchema,
|
|
2631
|
+
outputSchema: triggerSuccessSchema,
|
|
2632
|
+
run: async (args) => {
|
|
2633
|
+
const input = z3.object(triggerScopeSchema).parse(args);
|
|
2634
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2635
|
+
const { repoRoot, projectId, scope } = await resolveTriggerScope({ ...input, cwd });
|
|
2636
|
+
const api = await createApiClient();
|
|
2637
|
+
const items = unwrapResponseObject(
|
|
2638
|
+
await api.listProjectTriggers(projectId, scope),
|
|
2639
|
+
"trigger list"
|
|
2640
|
+
);
|
|
2641
|
+
return { data: { items }, logContext: { repoRoot } };
|
|
2642
|
+
}
|
|
2643
|
+
});
|
|
2644
|
+
registerTool(server, context, {
|
|
2645
|
+
name: "remix_collab_triggers_create",
|
|
2646
|
+
description: "Create a Remix project trigger. Requires confirm=true because future events may execute configured commands.",
|
|
2647
|
+
access: "remote_write",
|
|
2648
|
+
inputSchema: {
|
|
2649
|
+
...triggerScopeSchema,
|
|
2650
|
+
name: z3.string().trim().min(1),
|
|
2651
|
+
description: z3.string().nullable().optional(),
|
|
2652
|
+
mode: z3.enum(["blocking", "async"]).optional(),
|
|
2653
|
+
priority: z3.number().int().optional(),
|
|
2654
|
+
enabled: z3.boolean().optional(),
|
|
2655
|
+
steps: z3.array(triggerStepInputSchema).default([]),
|
|
2656
|
+
confirm: z3.boolean().optional()
|
|
2657
|
+
},
|
|
2658
|
+
outputSchema: triggerSuccessSchema,
|
|
2659
|
+
run: async (args) => {
|
|
2660
|
+
const input = z3.object({
|
|
2661
|
+
...triggerScopeSchema,
|
|
2662
|
+
name: z3.string().trim().min(1),
|
|
2663
|
+
description: z3.string().nullable().optional(),
|
|
2664
|
+
mode: z3.enum(["blocking", "async"]).optional(),
|
|
2665
|
+
priority: z3.number().int().optional(),
|
|
2666
|
+
enabled: z3.boolean().optional(),
|
|
2667
|
+
steps: z3.array(triggerStepInputSchema).default([]),
|
|
2668
|
+
confirm: z3.boolean().optional()
|
|
2669
|
+
}).parse(args);
|
|
2670
|
+
assertConfirm(input.confirm, "Creating a trigger");
|
|
2671
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2672
|
+
const { repoRoot, projectId, scope } = await resolveTriggerScope({ ...input, cwd });
|
|
2673
|
+
if (!scope.eventType) throw makeNotBoundError("eventType is required to create a trigger.");
|
|
2674
|
+
const api = await createApiClient();
|
|
2675
|
+
const item = unwrapResponseObject(
|
|
2676
|
+
await api.createProjectTrigger(projectId, {
|
|
2677
|
+
...scope,
|
|
2678
|
+
eventType: scope.eventType,
|
|
2679
|
+
name: input.name,
|
|
2680
|
+
description: input.description ?? null,
|
|
2681
|
+
mode: input.mode ?? "async",
|
|
2682
|
+
priority: input.priority ?? 100,
|
|
2683
|
+
enabled: input.enabled ?? true,
|
|
2684
|
+
steps: input.steps
|
|
2685
|
+
}),
|
|
2686
|
+
"trigger create"
|
|
2687
|
+
);
|
|
2688
|
+
return { data: { item }, logContext: { repoRoot } };
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
registerTool(server, context, {
|
|
2692
|
+
name: "remix_collab_triggers_update",
|
|
2693
|
+
description: "Update trigger metadata. Requires confirm=true.",
|
|
2694
|
+
access: "remote_write",
|
|
2695
|
+
inputSchema: {
|
|
2696
|
+
...triggerScopeSchema,
|
|
2697
|
+
triggerId: z3.string().trim().min(1),
|
|
2698
|
+
name: z3.string().trim().min(1).optional(),
|
|
2699
|
+
enabled: z3.boolean().optional(),
|
|
2700
|
+
mode: z3.enum(["blocking", "async"]).optional(),
|
|
2701
|
+
priority: z3.number().int().optional(),
|
|
2702
|
+
confirm: z3.boolean().optional()
|
|
2703
|
+
},
|
|
2704
|
+
outputSchema: triggerSuccessSchema,
|
|
2705
|
+
run: async (args) => {
|
|
2706
|
+
const input = z3.object({
|
|
2707
|
+
...triggerScopeSchema,
|
|
2708
|
+
triggerId: z3.string().trim().min(1),
|
|
2709
|
+
name: z3.string().trim().min(1).optional(),
|
|
2710
|
+
enabled: z3.boolean().optional(),
|
|
2711
|
+
mode: z3.enum(["blocking", "async"]).optional(),
|
|
2712
|
+
priority: z3.number().int().optional(),
|
|
2713
|
+
confirm: z3.boolean().optional()
|
|
2714
|
+
}).parse(args);
|
|
2715
|
+
assertConfirm(input.confirm, "Updating a trigger");
|
|
2716
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2717
|
+
const { repoRoot, projectId } = await resolveTriggerScope({ ...input, cwd });
|
|
2718
|
+
const api = await createApiClient();
|
|
2719
|
+
const item = unwrapResponseObject(
|
|
2720
|
+
await api.updateProjectTrigger(projectId, input.triggerId, {
|
|
2721
|
+
name: input.name,
|
|
2722
|
+
enabled: input.enabled,
|
|
2723
|
+
mode: input.mode,
|
|
2724
|
+
priority: input.priority
|
|
2725
|
+
}),
|
|
2726
|
+
"trigger update"
|
|
2727
|
+
);
|
|
2728
|
+
return { data: { item }, logContext: { repoRoot } };
|
|
2729
|
+
}
|
|
2730
|
+
});
|
|
2731
|
+
registerTool(server, context, {
|
|
2732
|
+
name: "remix_collab_triggers_delete",
|
|
2733
|
+
description: "Delete a trigger. Requires confirm=true.",
|
|
2734
|
+
access: "remote_write",
|
|
2735
|
+
inputSchema: { ...triggerScopeSchema, triggerId: z3.string().trim().min(1), confirm: z3.boolean().optional() },
|
|
2736
|
+
outputSchema: triggerSuccessSchema,
|
|
2737
|
+
run: async (args) => {
|
|
2738
|
+
const input = z3.object({ ...triggerScopeSchema, triggerId: z3.string().trim().min(1), confirm: z3.boolean().optional() }).parse(args);
|
|
2739
|
+
assertConfirm(input.confirm, "Deleting a trigger");
|
|
2740
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2741
|
+
const { repoRoot, projectId } = await resolveTriggerScope({ ...input, cwd });
|
|
2742
|
+
const api = await createApiClient();
|
|
2743
|
+
await api.deleteProjectTrigger(projectId, input.triggerId);
|
|
2744
|
+
return { data: { deleted: input.triggerId }, logContext: { repoRoot } };
|
|
2745
|
+
}
|
|
2746
|
+
});
|
|
2747
|
+
registerTool(server, context, {
|
|
2748
|
+
name: "remix_collab_triggers_run",
|
|
2749
|
+
description: "Manually run one trigger for the bound app. Requires confirm=true because it may execute commands.",
|
|
2750
|
+
access: "remote_write",
|
|
2751
|
+
inputSchema: { ...triggerAppSchema, triggerId: z3.string().trim().min(1), confirm: z3.boolean().optional() },
|
|
2752
|
+
outputSchema: triggerSuccessSchema,
|
|
2753
|
+
run: async (args) => {
|
|
2754
|
+
const input = z3.object({ ...triggerAppSchema, triggerId: z3.string().trim().min(1), confirm: z3.boolean().optional() }).parse(args);
|
|
2755
|
+
assertConfirm(input.confirm, "Running a trigger");
|
|
2756
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2757
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2758
|
+
const api = await createApiClient();
|
|
2759
|
+
const result = unwrapResponseObject(
|
|
2760
|
+
await api.runAppTrigger(appId, input.triggerId, { eventSource: "mcp", eventContext: {} }),
|
|
2761
|
+
"trigger run"
|
|
2762
|
+
);
|
|
2763
|
+
return { data: { result }, logContext: { repoRoot, appId } };
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
registerTool(server, context, {
|
|
2767
|
+
name: "remix_collab_triggers_run_event",
|
|
2768
|
+
description: "Manually run all triggers resolved for one event. Requires confirm=true because it may execute commands.",
|
|
2769
|
+
access: "remote_write",
|
|
2770
|
+
inputSchema: { ...triggerAppSchema, eventType: z3.string().trim().min(1), confirm: z3.boolean().optional() },
|
|
2771
|
+
outputSchema: triggerSuccessSchema,
|
|
2772
|
+
run: async (args) => {
|
|
2773
|
+
const input = z3.object({ ...triggerAppSchema, eventType: z3.string().trim().min(1), confirm: z3.boolean().optional() }).parse(args);
|
|
2774
|
+
assertConfirm(input.confirm, "Running trigger event");
|
|
2775
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2776
|
+
const { repoRoot, appId } = await resolveRuntimeApp({ ...input, cwd });
|
|
2777
|
+
const api = await createApiClient();
|
|
2778
|
+
const result = unwrapResponseObject(
|
|
2779
|
+
await api.runAppTriggerEvent(appId, {
|
|
2780
|
+
eventType: input.eventType,
|
|
2781
|
+
eventSource: "mcp",
|
|
2782
|
+
eventContext: {},
|
|
2783
|
+
environment: input.environment?.trim() || "development"
|
|
2784
|
+
}),
|
|
2785
|
+
"trigger run event"
|
|
2786
|
+
);
|
|
2787
|
+
return { data: { result }, logContext: { repoRoot, appId } };
|
|
2788
|
+
}
|
|
2789
|
+
});
|
|
2790
|
+
registerTool(server, context, {
|
|
2791
|
+
name: "remix_collab_trigger_runs",
|
|
2792
|
+
description: "List trigger run history for the bound project.",
|
|
2793
|
+
access: "read",
|
|
2794
|
+
inputSchema: { ...triggerScopeSchema, limit: z3.number().int().positive().max(100).optional(), appId: z3.string().trim().min(1).optional() },
|
|
2795
|
+
outputSchema: triggerSuccessSchema,
|
|
2796
|
+
run: async (args) => {
|
|
2797
|
+
const input = z3.object({ ...triggerScopeSchema, limit: z3.number().int().positive().max(100).optional(), appId: z3.string().trim().min(1).optional() }).parse(args);
|
|
2798
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2799
|
+
const { repoRoot, projectId } = await resolveTriggerScope({ ...input, cwd });
|
|
2800
|
+
const api = await createApiClient();
|
|
2801
|
+
const items = unwrapResponseObject(
|
|
2802
|
+
await api.listProjectTriggerRuns(projectId, {
|
|
2803
|
+
limit: input.limit ?? 20,
|
|
2804
|
+
eventType: input.eventType,
|
|
2805
|
+
appId: input.appId
|
|
2806
|
+
}),
|
|
2807
|
+
"trigger runs"
|
|
2808
|
+
);
|
|
2809
|
+
return { data: { items }, logContext: { repoRoot } };
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
1932
2812
|
registerTool(server, context, {
|
|
1933
2813
|
name: "remix_collab_init",
|
|
1934
2814
|
description: "Import the current repository into Remix and write the local binding file. Synchronous: by the time this tool resolves, the local binding file AND the local Remix baseline are both on disk, so automatic hook recording can capture subsequent completed turns. Brand-new init on the default branch typically takes ~10s; non-default-branch init can take 30-90s while the server provisions a feature lane. The result includes `reused: boolean` (false for a brand-new app, true if a binding already existed) plus the canonical app/project identifiers and the dashboard URL. Use forceNew=true only when intentionally creating a new canonical family from scratch in a previously-bound repo; do NOT use forceNew as a retry mechanism for a failed init \u2014 it creates orphan backend apps and triggers canonical-family ambiguity errors on subsequent inits in this directory.",
|