@mimeticinc/mim-cli 0.1.1 → 0.1.3
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/chunk-MS74BAYV.js +220 -0
- package/dist/{chunk-RXBIMVUG.js → chunk-UHJPAJVG.js} +86 -0
- package/dist/mim-bin.js +1 -1
- package/dist/mim-cli.d.ts +2 -1
- package/dist/mim-cli.js +3 -1
- package/dist/mim-mcp-bin.d.ts +1 -0
- package/dist/mim-mcp-bin.js +14 -0
- package/dist/mim-mcp-stdio.js +6 -212
- package/package.json +4 -4
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultMimProject,
|
|
3
|
+
loadMimConfig,
|
|
4
|
+
mimApiBaseUrl,
|
|
5
|
+
mimAuthToken,
|
|
6
|
+
normalizeMimApiBaseUrl,
|
|
7
|
+
trackMimUsageEvent
|
|
8
|
+
} from "./chunk-UHJPAJVG.js";
|
|
9
|
+
|
|
10
|
+
// src/mim-mcp-stdio.ts
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
import { pathToFileURL } from "url";
|
|
13
|
+
function mimMcpUsage() {
|
|
14
|
+
return `mim-mcp
|
|
15
|
+
|
|
16
|
+
Proxies MCP stdio requests to TryMimetic growth context.
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
mim-mcp --project my-site
|
|
20
|
+
mim-mcp --url https://trymimetic.com/api/mim/mcp --project my-site
|
|
21
|
+
|
|
22
|
+
Environment:
|
|
23
|
+
MIM_API_TOKEN Mimetic API token. Defaults to credentials from \`mim auth login\`.
|
|
24
|
+
MIM_API_BASE_URL TryMimetic API origin. Default: https://trymimetic.com.
|
|
25
|
+
MIM_MCP_URL Full MCP endpoint. Default: MIM_API_BASE_URL + /api/mim/mcp.
|
|
26
|
+
MIM_PROJECT Default project/site key.
|
|
27
|
+
MIM_MCP_TIMEOUT_MS Request timeout. Default: 30000.
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
function readValue(args, index, flag) {
|
|
31
|
+
const value = args[index + 1];
|
|
32
|
+
if (!value || value.startsWith("-")) throw new Error(`${flag} requires a value`);
|
|
33
|
+
return [value, index + 1];
|
|
34
|
+
}
|
|
35
|
+
function normalizeMcpUrl(value, source) {
|
|
36
|
+
return normalizeMimApiBaseUrl(value, source);
|
|
37
|
+
}
|
|
38
|
+
function parseMimMcpOptions(args = process.argv.slice(2), env = process.env) {
|
|
39
|
+
const config = loadMimConfig(env);
|
|
40
|
+
let apiBaseUrl = mimApiBaseUrl(config, env);
|
|
41
|
+
let mcpUrl = env.MIM_MCP_URL?.trim() || "";
|
|
42
|
+
let project = defaultMimProject(config, env);
|
|
43
|
+
let token = mimAuthToken(config, env);
|
|
44
|
+
let timeoutMs = Number(env.MIM_MCP_TIMEOUT_MS || "30000");
|
|
45
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
46
|
+
const arg = args[i];
|
|
47
|
+
if (arg === "--url" || arg === "--mcp-url") {
|
|
48
|
+
const [value, next] = readValue(args, i, arg);
|
|
49
|
+
mcpUrl = value;
|
|
50
|
+
i = next;
|
|
51
|
+
} else if (arg === "--api-base-url") {
|
|
52
|
+
const [value, next] = readValue(args, i, arg);
|
|
53
|
+
apiBaseUrl = normalizeMimApiBaseUrl(value, arg);
|
|
54
|
+
i = next;
|
|
55
|
+
} else if (arg === "--project") {
|
|
56
|
+
const [value, next] = readValue(args, i, arg);
|
|
57
|
+
project = value;
|
|
58
|
+
i = next;
|
|
59
|
+
} else if (arg === "--token") {
|
|
60
|
+
const [value, next] = readValue(args, i, arg);
|
|
61
|
+
token = value;
|
|
62
|
+
i = next;
|
|
63
|
+
} else if (arg === "--timeout-ms") {
|
|
64
|
+
const [value, next] = readValue(args, i, arg);
|
|
65
|
+
timeoutMs = Number(value);
|
|
66
|
+
i = next;
|
|
67
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
68
|
+
process.stdout.write(mimMcpUsage());
|
|
69
|
+
process.exit(0);
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error(`unknown mim-mcp option: ${arg}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!token) throw new Error("Mimetic auth is required; run `mim auth login` or set MIM_API_TOKEN");
|
|
75
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) throw new Error("--timeout-ms must be a positive integer");
|
|
76
|
+
return {
|
|
77
|
+
apiBaseUrl,
|
|
78
|
+
mcpUrl: mcpUrl ? normalizeMcpUrl(mcpUrl, "MIM_MCP_URL") : `${apiBaseUrl}/api/mim/mcp`,
|
|
79
|
+
token,
|
|
80
|
+
project,
|
|
81
|
+
timeoutMs,
|
|
82
|
+
traceId: randomUUID()
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function jsonRpcError(id, code, message) {
|
|
86
|
+
return JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
|
|
87
|
+
}
|
|
88
|
+
function requestId(payload) {
|
|
89
|
+
if (Array.isArray(payload)) return null;
|
|
90
|
+
if (payload && typeof payload === "object" && "id" in payload) {
|
|
91
|
+
return payload.id ?? null;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function toolName(payload) {
|
|
96
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return "";
|
|
97
|
+
const request = payload;
|
|
98
|
+
if (request.method !== "tools/call" || !request.params || typeof request.params !== "object") return "";
|
|
99
|
+
const name = request.params.name;
|
|
100
|
+
return typeof name === "string" ? name : "";
|
|
101
|
+
}
|
|
102
|
+
async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
|
|
103
|
+
const controller = new AbortController();
|
|
104
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
105
|
+
try {
|
|
106
|
+
return await fetchImpl(url, { ...init, signal: controller.signal });
|
|
107
|
+
} finally {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function proxyMimMcpMessage(raw, options, fetchImpl = fetch) {
|
|
112
|
+
const line = raw.trim();
|
|
113
|
+
if (!line) return null;
|
|
114
|
+
let payload;
|
|
115
|
+
try {
|
|
116
|
+
payload = JSON.parse(line);
|
|
117
|
+
} catch {
|
|
118
|
+
return jsonRpcError(null, -32700, "parse error");
|
|
119
|
+
}
|
|
120
|
+
const started = Date.now();
|
|
121
|
+
const tool = toolName(payload);
|
|
122
|
+
let status = "success";
|
|
123
|
+
let httpStatus = 200;
|
|
124
|
+
try {
|
|
125
|
+
const response = await fetchWithTimeout(
|
|
126
|
+
fetchImpl,
|
|
127
|
+
options.mcpUrl,
|
|
128
|
+
{
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: {
|
|
131
|
+
accept: "application/json",
|
|
132
|
+
authorization: `Bearer ${options.token}`,
|
|
133
|
+
"content-type": "application/json",
|
|
134
|
+
"x-mim-client": "mim-mcp",
|
|
135
|
+
"x-mim-client-version": "0.1.0",
|
|
136
|
+
"x-mim-trace-id": options.traceId,
|
|
137
|
+
...options.project ? { "x-mim-project": options.project } : {}
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(payload)
|
|
140
|
+
},
|
|
141
|
+
options.timeoutMs
|
|
142
|
+
);
|
|
143
|
+
httpStatus = response.status;
|
|
144
|
+
const text = await response.text();
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
status = "error";
|
|
147
|
+
const message = response.status === 401 || response.status === 403 ? "Mimetic MCP authentication failed" : `Mimetic MCP HTTP request failed with ${response.status}`;
|
|
148
|
+
return jsonRpcError(requestId(payload), -32e3, message);
|
|
149
|
+
}
|
|
150
|
+
if (!text.trim()) return null;
|
|
151
|
+
return JSON.stringify(JSON.parse(text));
|
|
152
|
+
} catch (error) {
|
|
153
|
+
status = "error";
|
|
154
|
+
const message = error instanceof Error && error.name === "AbortError" ? "Mimetic MCP request timed out" : "Mimetic MCP request failed";
|
|
155
|
+
return jsonRpcError(requestId(payload), -32e3, message);
|
|
156
|
+
} finally {
|
|
157
|
+
if (tool || payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
158
|
+
await trackMimUsageEvent(
|
|
159
|
+
{ apiBaseUrl: options.apiBaseUrl, token: options.token, traceId: options.traceId },
|
|
160
|
+
{
|
|
161
|
+
event: "mcp_request",
|
|
162
|
+
client: "mim-mcp",
|
|
163
|
+
project: options.project || void 0,
|
|
164
|
+
tool: tool || void 0,
|
|
165
|
+
status,
|
|
166
|
+
duration_ms: Date.now() - started,
|
|
167
|
+
metadata: {
|
|
168
|
+
method: !Array.isArray(payload) && payload && typeof payload === "object" ? payload.method : "batch",
|
|
169
|
+
http_status: httpStatus
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
fetchImpl
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function runMimMcpStdio(options = parseMimMcpOptions()) {
|
|
178
|
+
process.stdin.setEncoding("utf8");
|
|
179
|
+
let buffer = "";
|
|
180
|
+
let chain = Promise.resolve();
|
|
181
|
+
const handleLine = (line) => {
|
|
182
|
+
chain = chain.then(async () => {
|
|
183
|
+
const response = await proxyMimMcpMessage(line, options);
|
|
184
|
+
if (response) process.stdout.write(`${response}
|
|
185
|
+
`);
|
|
186
|
+
}).catch((error) => {
|
|
187
|
+
process.stderr.write(`mim-mcp: ${error instanceof Error ? error.message : "request failed"}
|
|
188
|
+
`);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
process.stdin.on("data", (chunk) => {
|
|
192
|
+
buffer += chunk;
|
|
193
|
+
let newline = buffer.indexOf("\n");
|
|
194
|
+
while (newline !== -1) {
|
|
195
|
+
const line = buffer.slice(0, newline).replace(/\r$/, "");
|
|
196
|
+
buffer = buffer.slice(newline + 1);
|
|
197
|
+
handleLine(line);
|
|
198
|
+
newline = buffer.indexOf("\n");
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
process.stdin.on("end", () => {
|
|
202
|
+
if (buffer.trim()) handleLine(buffer);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
206
|
+
try {
|
|
207
|
+
runMimMcpStdio();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
process.stderr.write(`mim-mcp: ${error instanceof Error ? error.message : "startup failed"}
|
|
210
|
+
`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export {
|
|
216
|
+
mimMcpUsage,
|
|
217
|
+
parseMimMcpOptions,
|
|
218
|
+
proxyMimMcpMessage,
|
|
219
|
+
runMimMcpStdio
|
|
220
|
+
};
|
|
@@ -21,6 +21,8 @@ Commands:
|
|
|
21
21
|
recordings List recent session recording summaries.
|
|
22
22
|
replay [session_id] Print one persisted replay insight. Defaults to latest.
|
|
23
23
|
findings List audit/backlog findings from TryMimetic.
|
|
24
|
+
query-ga4 Query your connected GA4 (needs --metrics, e.g. sessions).
|
|
25
|
+
query-gsc Query your connected Search Console (top queries/pages).
|
|
24
26
|
audit start <url> Start an audit, reusing a running/completed audit when available.
|
|
25
27
|
audit rerun <url> Force a fresh audit for a site that was already audited.
|
|
26
28
|
audit status <audit_id> Check audit progress.
|
|
@@ -616,6 +618,87 @@ async function runCollectionCommand(command, args) {
|
|
|
616
618
|
else if (command === "recordings") console.log(formatRecordingsPayload(payload, { project: resolvedProject }));
|
|
617
619
|
else console.log(formatMimListPayload(payload, command));
|
|
618
620
|
}
|
|
621
|
+
function buildQueryToolArgs(tool, project, rest) {
|
|
622
|
+
const toolArgs = { project };
|
|
623
|
+
let metrics = [];
|
|
624
|
+
let dimensions = [];
|
|
625
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
626
|
+
const arg = rest[i];
|
|
627
|
+
if (arg === "--metrics") {
|
|
628
|
+
const [value, next] = readValue(rest, i, arg);
|
|
629
|
+
metrics = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
630
|
+
i = next;
|
|
631
|
+
} else if (arg === "--dimensions" || arg === "--dims") {
|
|
632
|
+
const [value, next] = readValue(rest, i, arg);
|
|
633
|
+
dimensions = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
634
|
+
i = next;
|
|
635
|
+
} else if (arg === "--start-date" || arg === "--start") {
|
|
636
|
+
const [value, next] = readValue(rest, i, arg);
|
|
637
|
+
toolArgs.start_date = value;
|
|
638
|
+
i = next;
|
|
639
|
+
} else if (arg === "--end-date" || arg === "--end") {
|
|
640
|
+
const [value, next] = readValue(rest, i, arg);
|
|
641
|
+
toolArgs.end_date = value;
|
|
642
|
+
i = next;
|
|
643
|
+
} else if (arg === "--limit") {
|
|
644
|
+
const [value, next] = readValue(rest, i, arg);
|
|
645
|
+
const n = Number(value);
|
|
646
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error("--limit must be a positive integer");
|
|
647
|
+
if (tool === "query_gsc") toolArgs.row_limit = n;
|
|
648
|
+
else toolArgs.limit = n;
|
|
649
|
+
i = next;
|
|
650
|
+
} else if (arg === "--order-by") {
|
|
651
|
+
const [value, next] = readValue(rest, i, arg);
|
|
652
|
+
toolArgs.order_by_metric = value;
|
|
653
|
+
i = next;
|
|
654
|
+
} else {
|
|
655
|
+
throw new Error(`unknown ${tool} option: ${arg}`);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (metrics.length) toolArgs.metrics = metrics;
|
|
659
|
+
if (dimensions.length) toolArgs.dimensions = dimensions;
|
|
660
|
+
if (tool === "query_ga4" && !metrics.length) {
|
|
661
|
+
throw new Error("query-ga4 requires --metrics (e.g. --metrics sessions,conversions)");
|
|
662
|
+
}
|
|
663
|
+
return toolArgs;
|
|
664
|
+
}
|
|
665
|
+
async function runQuery(tool, args) {
|
|
666
|
+
const { runtime, args: kept, project } = runtimeFromArgs(args);
|
|
667
|
+
requireAuth(runtime);
|
|
668
|
+
const resolvedProject = requireProject(project);
|
|
669
|
+
const parsedFormat = parseFormat(kept, "json");
|
|
670
|
+
const toolArgs = buildQueryToolArgs(tool, resolvedProject, parsedFormat.args);
|
|
671
|
+
const body = { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: tool, arguments: toolArgs } };
|
|
672
|
+
const response = await runTrackedMimCommand(
|
|
673
|
+
runtime,
|
|
674
|
+
{
|
|
675
|
+
command: tool,
|
|
676
|
+
project: resolvedProject,
|
|
677
|
+
metadata: {
|
|
678
|
+
metrics: Array.isArray(toolArgs.metrics) ? toolArgs.metrics.length : 0,
|
|
679
|
+
dimensions: Array.isArray(toolArgs.dimensions) ? toolArgs.dimensions.length : 0
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
() => mimRequest(
|
|
683
|
+
runtime,
|
|
684
|
+
"/api/mim/mcp",
|
|
685
|
+
{ body }
|
|
686
|
+
)
|
|
687
|
+
);
|
|
688
|
+
if (response?.error) throw new Error(response.error.message || "request failed");
|
|
689
|
+
const result = response?.result;
|
|
690
|
+
const text = result?.content?.[0]?.text ?? "";
|
|
691
|
+
let parsed = text;
|
|
692
|
+
try {
|
|
693
|
+
parsed = JSON.parse(text);
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
if (result?.isError) {
|
|
697
|
+
const message = parsed && typeof parsed === "object" && "error" in parsed ? String(parsed.error) : String(parsed);
|
|
698
|
+
throw new Error(message || "query failed");
|
|
699
|
+
}
|
|
700
|
+
printJson(parsed);
|
|
701
|
+
}
|
|
619
702
|
async function runReplay(args) {
|
|
620
703
|
const { runtime, args: kept, project } = runtimeFromArgs(args);
|
|
621
704
|
requireAuth(runtime);
|
|
@@ -1141,6 +1224,8 @@ async function mimMain(argv = process.argv.slice(2)) {
|
|
|
1141
1224
|
if (command === "recordings") return runCollectionCommand("recordings", args);
|
|
1142
1225
|
if (command === "replay") return runReplay(args);
|
|
1143
1226
|
if (command === "findings") return runCollectionCommand("findings", args);
|
|
1227
|
+
if (command === "query-ga4") return runQuery("query_ga4", args);
|
|
1228
|
+
if (command === "query-gsc") return runQuery("query_gsc", args);
|
|
1144
1229
|
if (command === "audit") return runAudit(args);
|
|
1145
1230
|
if (command === "web-audit") return runWebAudit(args);
|
|
1146
1231
|
if (command === "ipa-audit") return runIpaAudit(args);
|
|
@@ -1174,6 +1259,7 @@ export {
|
|
|
1174
1259
|
formatMimListPayload,
|
|
1175
1260
|
formatRecordingsPayload,
|
|
1176
1261
|
formatReplayInsightPayload,
|
|
1262
|
+
buildQueryToolArgs,
|
|
1177
1263
|
formatFixesPayload,
|
|
1178
1264
|
claudeMcpInstallCommand,
|
|
1179
1265
|
codexMcpConfig,
|
package/dist/mim-bin.js
CHANGED
package/dist/mim-cli.d.ts
CHANGED
|
@@ -91,9 +91,10 @@ declare function formatRecordingsPayload(payload: unknown, options?: {
|
|
|
91
91
|
project?: string;
|
|
92
92
|
}): string;
|
|
93
93
|
declare function formatReplayInsightPayload(payload: unknown): string;
|
|
94
|
+
declare function buildQueryToolArgs(tool: "query_ga4" | "query_gsc", project: string, rest: string[]): Record<string, unknown>;
|
|
94
95
|
declare function formatFixesPayload(payload: FixesResponse): string;
|
|
95
96
|
declare function claudeMcpInstallCommand(project?: string): string[];
|
|
96
97
|
declare function codexMcpConfig(project?: string): string;
|
|
97
98
|
declare function mimMain(argv?: string[]): Promise<void>;
|
|
98
99
|
|
|
99
|
-
export { type MimConfig, type MimRuntime, buildMimUsageEvent, claudeMcpInstallCommand, clearMimConfig, codexMcpConfig, defaultMimProject, formatFixesPayload, formatMimListPayload, formatRecordingsPayload, formatReplayInsightPayload, loadMimConfig, mimApiBaseUrl, mimAuthToken, mimConfigDir, mimConfigPath, mimMain, mimUsage, normalizeMimApiBaseUrl, sanitizeMimMetadata, saveMimConfig, shouldSendMimTelemetry, trackMimUsageEvent };
|
|
100
|
+
export { type MimConfig, type MimRuntime, buildMimUsageEvent, buildQueryToolArgs, claudeMcpInstallCommand, clearMimConfig, codexMcpConfig, defaultMimProject, formatFixesPayload, formatMimListPayload, formatRecordingsPayload, formatReplayInsightPayload, loadMimConfig, mimApiBaseUrl, mimAuthToken, mimConfigDir, mimConfigPath, mimMain, mimUsage, normalizeMimApiBaseUrl, sanitizeMimMetadata, saveMimConfig, shouldSendMimTelemetry, trackMimUsageEvent };
|
package/dist/mim-cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildMimUsageEvent,
|
|
4
|
+
buildQueryToolArgs,
|
|
4
5
|
claudeMcpInstallCommand,
|
|
5
6
|
clearMimConfig,
|
|
6
7
|
codexMcpConfig,
|
|
@@ -21,9 +22,10 @@ import {
|
|
|
21
22
|
saveMimConfig,
|
|
22
23
|
shouldSendMimTelemetry,
|
|
23
24
|
trackMimUsageEvent
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-UHJPAJVG.js";
|
|
25
26
|
export {
|
|
26
27
|
buildMimUsageEvent,
|
|
28
|
+
buildQueryToolArgs,
|
|
27
29
|
claudeMcpInstallCommand,
|
|
28
30
|
clearMimConfig,
|
|
29
31
|
codexMcpConfig,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runMimMcpStdio
|
|
4
|
+
} from "./chunk-MS74BAYV.js";
|
|
5
|
+
import "./chunk-UHJPAJVG.js";
|
|
6
|
+
|
|
7
|
+
// src/mim-mcp-bin.ts
|
|
8
|
+
try {
|
|
9
|
+
runMimMcpStdio();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
process.stderr.write(`mim-mcp: ${error instanceof Error ? error.message : "startup failed"}
|
|
12
|
+
`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
package/dist/mim-mcp-stdio.js
CHANGED
|
@@ -1,217 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "./chunk-RXBIMVUG.js";
|
|
10
|
-
|
|
11
|
-
// src/mim-mcp-stdio.ts
|
|
12
|
-
import { randomUUID } from "crypto";
|
|
13
|
-
import { pathToFileURL } from "url";
|
|
14
|
-
function mimMcpUsage() {
|
|
15
|
-
return `mim-mcp
|
|
16
|
-
|
|
17
|
-
Proxies MCP stdio requests to TryMimetic growth context.
|
|
18
|
-
|
|
19
|
-
Usage:
|
|
20
|
-
mim-mcp --project my-site
|
|
21
|
-
mim-mcp --url https://trymimetic.com/api/mim/mcp --project my-site
|
|
22
|
-
|
|
23
|
-
Environment:
|
|
24
|
-
MIM_API_TOKEN Mimetic API token. Defaults to credentials from \`mim auth login\`.
|
|
25
|
-
MIM_API_BASE_URL TryMimetic API origin. Default: https://trymimetic.com.
|
|
26
|
-
MIM_MCP_URL Full MCP endpoint. Default: MIM_API_BASE_URL + /api/mim/mcp.
|
|
27
|
-
MIM_PROJECT Default project/site key.
|
|
28
|
-
MIM_MCP_TIMEOUT_MS Request timeout. Default: 30000.
|
|
29
|
-
`;
|
|
30
|
-
}
|
|
31
|
-
function readValue(args, index, flag) {
|
|
32
|
-
const value = args[index + 1];
|
|
33
|
-
if (!value || value.startsWith("-")) throw new Error(`${flag} requires a value`);
|
|
34
|
-
return [value, index + 1];
|
|
35
|
-
}
|
|
36
|
-
function normalizeMcpUrl(value, source) {
|
|
37
|
-
return normalizeMimApiBaseUrl(value, source);
|
|
38
|
-
}
|
|
39
|
-
function parseMimMcpOptions(args = process.argv.slice(2), env = process.env) {
|
|
40
|
-
const config = loadMimConfig(env);
|
|
41
|
-
let apiBaseUrl = mimApiBaseUrl(config, env);
|
|
42
|
-
let mcpUrl = env.MIM_MCP_URL?.trim() || "";
|
|
43
|
-
let project = defaultMimProject(config, env);
|
|
44
|
-
let token = mimAuthToken(config, env);
|
|
45
|
-
let timeoutMs = Number(env.MIM_MCP_TIMEOUT_MS || "30000");
|
|
46
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
47
|
-
const arg = args[i];
|
|
48
|
-
if (arg === "--url" || arg === "--mcp-url") {
|
|
49
|
-
const [value, next] = readValue(args, i, arg);
|
|
50
|
-
mcpUrl = value;
|
|
51
|
-
i = next;
|
|
52
|
-
} else if (arg === "--api-base-url") {
|
|
53
|
-
const [value, next] = readValue(args, i, arg);
|
|
54
|
-
apiBaseUrl = normalizeMimApiBaseUrl(value, arg);
|
|
55
|
-
i = next;
|
|
56
|
-
} else if (arg === "--project") {
|
|
57
|
-
const [value, next] = readValue(args, i, arg);
|
|
58
|
-
project = value;
|
|
59
|
-
i = next;
|
|
60
|
-
} else if (arg === "--token") {
|
|
61
|
-
const [value, next] = readValue(args, i, arg);
|
|
62
|
-
token = value;
|
|
63
|
-
i = next;
|
|
64
|
-
} else if (arg === "--timeout-ms") {
|
|
65
|
-
const [value, next] = readValue(args, i, arg);
|
|
66
|
-
timeoutMs = Number(value);
|
|
67
|
-
i = next;
|
|
68
|
-
} else if (arg === "-h" || arg === "--help") {
|
|
69
|
-
process.stdout.write(mimMcpUsage());
|
|
70
|
-
process.exit(0);
|
|
71
|
-
} else {
|
|
72
|
-
throw new Error(`unknown mim-mcp option: ${arg}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!token) throw new Error("Mimetic auth is required; run `mim auth login` or set MIM_API_TOKEN");
|
|
76
|
-
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) throw new Error("--timeout-ms must be a positive integer");
|
|
77
|
-
return {
|
|
78
|
-
apiBaseUrl,
|
|
79
|
-
mcpUrl: mcpUrl ? normalizeMcpUrl(mcpUrl, "MIM_MCP_URL") : `${apiBaseUrl}/api/mim/mcp`,
|
|
80
|
-
token,
|
|
81
|
-
project,
|
|
82
|
-
timeoutMs,
|
|
83
|
-
traceId: randomUUID()
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function jsonRpcError(id, code, message) {
|
|
87
|
-
return JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
|
|
88
|
-
}
|
|
89
|
-
function requestId(payload) {
|
|
90
|
-
if (Array.isArray(payload)) return null;
|
|
91
|
-
if (payload && typeof payload === "object" && "id" in payload) {
|
|
92
|
-
return payload.id ?? null;
|
|
93
|
-
}
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
function toolName(payload) {
|
|
97
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return "";
|
|
98
|
-
const request = payload;
|
|
99
|
-
if (request.method !== "tools/call" || !request.params || typeof request.params !== "object") return "";
|
|
100
|
-
const name = request.params.name;
|
|
101
|
-
return typeof name === "string" ? name : "";
|
|
102
|
-
}
|
|
103
|
-
async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
|
|
104
|
-
const controller = new AbortController();
|
|
105
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
106
|
-
try {
|
|
107
|
-
return await fetchImpl(url, { ...init, signal: controller.signal });
|
|
108
|
-
} finally {
|
|
109
|
-
clearTimeout(timeout);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async function proxyMimMcpMessage(raw, options, fetchImpl = fetch) {
|
|
113
|
-
const line = raw.trim();
|
|
114
|
-
if (!line) return null;
|
|
115
|
-
let payload;
|
|
116
|
-
try {
|
|
117
|
-
payload = JSON.parse(line);
|
|
118
|
-
} catch {
|
|
119
|
-
return jsonRpcError(null, -32700, "parse error");
|
|
120
|
-
}
|
|
121
|
-
const started = Date.now();
|
|
122
|
-
const tool = toolName(payload);
|
|
123
|
-
let status = "success";
|
|
124
|
-
let httpStatus = 200;
|
|
125
|
-
try {
|
|
126
|
-
const response = await fetchWithTimeout(
|
|
127
|
-
fetchImpl,
|
|
128
|
-
options.mcpUrl,
|
|
129
|
-
{
|
|
130
|
-
method: "POST",
|
|
131
|
-
headers: {
|
|
132
|
-
accept: "application/json",
|
|
133
|
-
authorization: `Bearer ${options.token}`,
|
|
134
|
-
"content-type": "application/json",
|
|
135
|
-
"x-mim-client": "mim-mcp",
|
|
136
|
-
"x-mim-client-version": "0.1.0",
|
|
137
|
-
"x-mim-trace-id": options.traceId,
|
|
138
|
-
...options.project ? { "x-mim-project": options.project } : {}
|
|
139
|
-
},
|
|
140
|
-
body: JSON.stringify(payload)
|
|
141
|
-
},
|
|
142
|
-
options.timeoutMs
|
|
143
|
-
);
|
|
144
|
-
httpStatus = response.status;
|
|
145
|
-
const text = await response.text();
|
|
146
|
-
if (!response.ok) {
|
|
147
|
-
status = "error";
|
|
148
|
-
const message = response.status === 401 || response.status === 403 ? "Mimetic MCP authentication failed" : `Mimetic MCP HTTP request failed with ${response.status}`;
|
|
149
|
-
return jsonRpcError(requestId(payload), -32e3, message);
|
|
150
|
-
}
|
|
151
|
-
if (!text.trim()) return null;
|
|
152
|
-
return JSON.stringify(JSON.parse(text));
|
|
153
|
-
} catch (error) {
|
|
154
|
-
status = "error";
|
|
155
|
-
const message = error instanceof Error && error.name === "AbortError" ? "Mimetic MCP request timed out" : "Mimetic MCP request failed";
|
|
156
|
-
return jsonRpcError(requestId(payload), -32e3, message);
|
|
157
|
-
} finally {
|
|
158
|
-
if (tool || payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
159
|
-
await trackMimUsageEvent(
|
|
160
|
-
{ apiBaseUrl: options.apiBaseUrl, token: options.token, traceId: options.traceId },
|
|
161
|
-
{
|
|
162
|
-
event: "mcp_request",
|
|
163
|
-
client: "mim-mcp",
|
|
164
|
-
project: options.project || void 0,
|
|
165
|
-
tool: tool || void 0,
|
|
166
|
-
status,
|
|
167
|
-
duration_ms: Date.now() - started,
|
|
168
|
-
metadata: {
|
|
169
|
-
method: !Array.isArray(payload) && payload && typeof payload === "object" ? payload.method : "batch",
|
|
170
|
-
http_status: httpStatus
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
fetchImpl
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function runMimMcpStdio(options = parseMimMcpOptions()) {
|
|
179
|
-
process.stdin.setEncoding("utf8");
|
|
180
|
-
let buffer = "";
|
|
181
|
-
let chain = Promise.resolve();
|
|
182
|
-
const handleLine = (line) => {
|
|
183
|
-
chain = chain.then(async () => {
|
|
184
|
-
const response = await proxyMimMcpMessage(line, options);
|
|
185
|
-
if (response) process.stdout.write(`${response}
|
|
186
|
-
`);
|
|
187
|
-
}).catch((error) => {
|
|
188
|
-
process.stderr.write(`mim-mcp: ${error instanceof Error ? error.message : "request failed"}
|
|
189
|
-
`);
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
process.stdin.on("data", (chunk) => {
|
|
193
|
-
buffer += chunk;
|
|
194
|
-
let newline = buffer.indexOf("\n");
|
|
195
|
-
while (newline !== -1) {
|
|
196
|
-
const line = buffer.slice(0, newline).replace(/\r$/, "");
|
|
197
|
-
buffer = buffer.slice(newline + 1);
|
|
198
|
-
handleLine(line);
|
|
199
|
-
newline = buffer.indexOf("\n");
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
process.stdin.on("end", () => {
|
|
203
|
-
if (buffer.trim()) handleLine(buffer);
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
207
|
-
try {
|
|
208
|
-
runMimMcpStdio();
|
|
209
|
-
} catch (error) {
|
|
210
|
-
process.stderr.write(`mim-mcp: ${error instanceof Error ? error.message : "startup failed"}
|
|
211
|
-
`);
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
3
|
+
mimMcpUsage,
|
|
4
|
+
parseMimMcpOptions,
|
|
5
|
+
proxyMimMcpMessage,
|
|
6
|
+
runMimMcpStdio
|
|
7
|
+
} from "./chunk-MS74BAYV.js";
|
|
8
|
+
import "./chunk-UHJPAJVG.js";
|
|
215
9
|
export {
|
|
216
10
|
mimMcpUsage,
|
|
217
11
|
parseMimMcpOptions,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimeticinc/mim-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Mimetic CLI
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Mimetic CLI \u2014 growth context for Claude Code, Codex, and shell workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Mimetic Inc.",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"bin": {
|
|
25
25
|
"mim": "dist/mim-bin.js",
|
|
26
|
-
"mim-mcp": "dist/mim-mcp-
|
|
26
|
+
"mim-mcp": "dist/mim-mcp-bin.js"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=20"
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"typescript": "^5.7.0",
|
|
57
57
|
"vitest": "^3.2.0"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|