amplitude-cli 0.3.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/README.md +123 -0
- package/dist/commands/auth.d.ts +6 -0
- package/dist/commands/auth.js +133 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/charts.d.ts +6 -0
- package/dist/commands/charts.js +136 -0
- package/dist/commands/charts.js.map +1 -0
- package/dist/commands/cohorts.d.ts +6 -0
- package/dist/commands/cohorts.js +79 -0
- package/dist/commands/cohorts.js.map +1 -0
- package/dist/commands/dashboards.d.ts +6 -0
- package/dist/commands/dashboards.js +76 -0
- package/dist/commands/dashboards.js.map +1 -0
- package/dist/commands/events.d.ts +6 -0
- package/dist/commands/events.js +46 -0
- package/dist/commands/events.js.map +1 -0
- package/dist/commands/experiments.d.ts +6 -0
- package/dist/commands/experiments.js +57 -0
- package/dist/commands/experiments.js.map +1 -0
- package/dist/commands/query.d.ts +6 -0
- package/dist/commands/query.js +227 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/users.d.ts +6 -0
- package/dist/commands/users.js +49 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-client.d.ts +90 -0
- package/dist/mcp-client.js +291 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/utils/errors.d.ts +4 -0
- package/dist/utils/errors.js +34 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.js +90 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/mcp-helpers.d.ts +13 -0
- package/dist/utils/mcp-helpers.js +49 -0
- package/dist/utils/mcp-helpers.js.map +1 -0
- package/dist/utils/oauth.d.ts +62 -0
- package/dist/utils/oauth.js +344 -0
- package/dist/utils/oauth.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amplitude MCP client.
|
|
3
|
+
* Calls Amplitude's MCP server tools (search, query_dataset, save_chart_edits,
|
|
4
|
+
* create_dashboard, etc.) using OAuth tokens.
|
|
5
|
+
*
|
|
6
|
+
* The MCP server exposes tools via JSON-RPC over HTTP (Streamable HTTP transport).
|
|
7
|
+
*/
|
|
8
|
+
import { getAccessToken, getMcpBaseUrl, getOAuthConfig } from "./utils/oauth.js";
|
|
9
|
+
export class AmplitudeMcpClient {
|
|
10
|
+
region;
|
|
11
|
+
sessionId;
|
|
12
|
+
initialized = false;
|
|
13
|
+
constructor(region) {
|
|
14
|
+
const oauth = getOAuthConfig();
|
|
15
|
+
this.region =
|
|
16
|
+
region ||
|
|
17
|
+
process.env.AMPLITUDE_REGION ||
|
|
18
|
+
oauth?.region ||
|
|
19
|
+
"us";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize MCP session. Must be called before any tool calls.
|
|
23
|
+
* Auto-called by callTool if not yet initialized.
|
|
24
|
+
*/
|
|
25
|
+
async ensureSession() {
|
|
26
|
+
if (this.initialized && this.sessionId)
|
|
27
|
+
return;
|
|
28
|
+
const token = await getAccessToken(this.region);
|
|
29
|
+
const baseUrl = getMcpBaseUrl(this.region);
|
|
30
|
+
const body = {
|
|
31
|
+
jsonrpc: "2.0",
|
|
32
|
+
id: Date.now(),
|
|
33
|
+
method: "initialize",
|
|
34
|
+
params: {
|
|
35
|
+
protocolVersion: "2024-11-05",
|
|
36
|
+
capabilities: {},
|
|
37
|
+
clientInfo: { name: "amplitude-cli", version: "0.2.0" },
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const res = await fetch(`${baseUrl}/mcp`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
Accept: "application/json, text/event-stream",
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
const newSessionId = res.headers.get("mcp-session-id");
|
|
50
|
+
if (newSessionId) {
|
|
51
|
+
this.sessionId = newSessionId;
|
|
52
|
+
}
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const text = await res.text();
|
|
55
|
+
throw new McpError(res.status, text, "initialize");
|
|
56
|
+
}
|
|
57
|
+
this.initialized = true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Call an MCP tool on the Amplitude server.
|
|
61
|
+
*/
|
|
62
|
+
async callTool(toolName, args = {}) {
|
|
63
|
+
try {
|
|
64
|
+
return await this._callToolOnce(toolName, args);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// Auto-recover from session errors by re-initializing
|
|
68
|
+
if (err instanceof McpError && err.detail.toLowerCase().includes("session")) {
|
|
69
|
+
this.sessionId = undefined;
|
|
70
|
+
this.initialized = false;
|
|
71
|
+
return await this._callToolOnce(toolName, args);
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async _callToolOnce(toolName, args = {}) {
|
|
77
|
+
await this.ensureSession();
|
|
78
|
+
const token = await getAccessToken(this.region);
|
|
79
|
+
const baseUrl = getMcpBaseUrl(this.region);
|
|
80
|
+
const body = {
|
|
81
|
+
jsonrpc: "2.0",
|
|
82
|
+
id: Date.now(),
|
|
83
|
+
method: "tools/call",
|
|
84
|
+
params: {
|
|
85
|
+
name: toolName,
|
|
86
|
+
arguments: args,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const headers = {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
Accept: "application/json, text/event-stream",
|
|
92
|
+
Authorization: `Bearer ${token}`,
|
|
93
|
+
};
|
|
94
|
+
// Include session ID for continuity if we have one
|
|
95
|
+
if (this.sessionId) {
|
|
96
|
+
headers["Mcp-Session-Id"] = this.sessionId;
|
|
97
|
+
}
|
|
98
|
+
const res = await fetch(`${baseUrl}/mcp`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers,
|
|
101
|
+
body: JSON.stringify(body),
|
|
102
|
+
});
|
|
103
|
+
// Capture session ID from response
|
|
104
|
+
const newSessionId = res.headers.get("mcp-session-id");
|
|
105
|
+
if (newSessionId) {
|
|
106
|
+
this.sessionId = newSessionId;
|
|
107
|
+
}
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
const text = await res.text();
|
|
110
|
+
throw new McpError(res.status, text, toolName);
|
|
111
|
+
}
|
|
112
|
+
const contentType = res.headers.get("content-type") || "";
|
|
113
|
+
// Handle SSE (text/event-stream) response
|
|
114
|
+
if (contentType.includes("text/event-stream")) {
|
|
115
|
+
return this.parseSSEResponse(res);
|
|
116
|
+
}
|
|
117
|
+
// Handle direct JSON response
|
|
118
|
+
const result = (await res.json());
|
|
119
|
+
if (result.error) {
|
|
120
|
+
throw new McpError(result.error.code, result.error.message, toolName);
|
|
121
|
+
}
|
|
122
|
+
return result.result || { content: [] };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse SSE response from MCP server.
|
|
126
|
+
*/
|
|
127
|
+
async parseSSEResponse(res) {
|
|
128
|
+
const text = await res.text();
|
|
129
|
+
const lines = text.split("\n");
|
|
130
|
+
let lastData = "";
|
|
131
|
+
for (const line of lines) {
|
|
132
|
+
if (line.startsWith("data: ")) {
|
|
133
|
+
lastData = line.slice(6);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!lastData) {
|
|
137
|
+
return { content: [] };
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(lastData);
|
|
141
|
+
if (parsed.error) {
|
|
142
|
+
throw new McpError(parsed.error.code, parsed.error.message, "sse");
|
|
143
|
+
}
|
|
144
|
+
return parsed.result || { content: [] };
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
if (err instanceof McpError)
|
|
148
|
+
throw err;
|
|
149
|
+
// Return raw text as content
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: "text", text: lastData }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* List available MCP tools.
|
|
157
|
+
*/
|
|
158
|
+
async listTools() {
|
|
159
|
+
await this.ensureSession();
|
|
160
|
+
const token = await getAccessToken(this.region);
|
|
161
|
+
const baseUrl = getMcpBaseUrl(this.region);
|
|
162
|
+
const headers = {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
Accept: "application/json, text/event-stream",
|
|
165
|
+
Authorization: `Bearer ${token}`,
|
|
166
|
+
};
|
|
167
|
+
if (this.sessionId) {
|
|
168
|
+
headers["Mcp-Session-Id"] = this.sessionId;
|
|
169
|
+
}
|
|
170
|
+
const res = await fetch(`${baseUrl}/mcp`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers,
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
jsonrpc: "2.0",
|
|
175
|
+
id: Date.now(),
|
|
176
|
+
method: "tools/list",
|
|
177
|
+
params: {},
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
const newSessionId = res.headers.get("mcp-session-id");
|
|
181
|
+
if (newSessionId) {
|
|
182
|
+
this.sessionId = newSessionId;
|
|
183
|
+
}
|
|
184
|
+
if (!res.ok) {
|
|
185
|
+
const text = await res.text();
|
|
186
|
+
throw new McpError(res.status, text, "tools/list");
|
|
187
|
+
}
|
|
188
|
+
const contentType = res.headers.get("content-type") || "";
|
|
189
|
+
if (contentType.includes("text/event-stream")) {
|
|
190
|
+
return this.parseSSEResponse(res);
|
|
191
|
+
}
|
|
192
|
+
return res.json();
|
|
193
|
+
}
|
|
194
|
+
// ─── Convenience methods for common MCP tools ─────────────────────────
|
|
195
|
+
/**
|
|
196
|
+
* Search for entities (charts, dashboards, events, cohorts, etc.)
|
|
197
|
+
*/
|
|
198
|
+
async search(query, entityTypes, limit) {
|
|
199
|
+
return this.callTool("search", {
|
|
200
|
+
query,
|
|
201
|
+
...(entityTypes && { entity_types: entityTypes }),
|
|
202
|
+
...(limit && { limit }),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get context (project info, user info).
|
|
207
|
+
*/
|
|
208
|
+
async getContext() {
|
|
209
|
+
return this.callTool("get_context");
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get chart definitions by ID.
|
|
213
|
+
*/
|
|
214
|
+
async getCharts(chartIds) {
|
|
215
|
+
return this.callTool("get_charts", { chart_ids: chartIds });
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get dashboard by ID.
|
|
219
|
+
*/
|
|
220
|
+
async getDashboard(dashboardId) {
|
|
221
|
+
return this.callTool("get_dashboard", { dashboard_id: dashboardId });
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Query a dataset (create/preview a chart).
|
|
225
|
+
* Returns data + an editId that can be saved.
|
|
226
|
+
*/
|
|
227
|
+
async queryDataset(definition) {
|
|
228
|
+
return this.callTool("query_dataset", definition);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Save a chart from query_dataset results.
|
|
232
|
+
*/
|
|
233
|
+
async saveChart(editId, name, description) {
|
|
234
|
+
return this.callTool("save_chart_edits", {
|
|
235
|
+
edit_id: editId,
|
|
236
|
+
name,
|
|
237
|
+
...(description && { description }),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Create a dashboard with charts and layout.
|
|
242
|
+
*/
|
|
243
|
+
async createDashboard(name, rows, description) {
|
|
244
|
+
return this.callTool("create_dashboard", {
|
|
245
|
+
name,
|
|
246
|
+
rows,
|
|
247
|
+
...(description && { description }),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get event properties for an event type.
|
|
252
|
+
*/
|
|
253
|
+
async getEventProperties(eventType) {
|
|
254
|
+
return this.callTool("get_event_properties", { event_type: eventType });
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Query an existing chart's data.
|
|
258
|
+
*/
|
|
259
|
+
async queryChart(chartId) {
|
|
260
|
+
return this.callTool("query_chart", { chart_id: chartId });
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get experiment details.
|
|
264
|
+
*/
|
|
265
|
+
async getExperiments(experimentIds) {
|
|
266
|
+
return this.callTool("get_experiments", {
|
|
267
|
+
experiment_ids: experimentIds,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Query experiment results.
|
|
272
|
+
*/
|
|
273
|
+
async queryExperiment(experimentId) {
|
|
274
|
+
return this.callTool("query_experiment", {
|
|
275
|
+
experiment_id: experimentId,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
export class McpError extends Error {
|
|
280
|
+
code;
|
|
281
|
+
detail;
|
|
282
|
+
tool;
|
|
283
|
+
constructor(code, detail, tool) {
|
|
284
|
+
super(`MCP error (${code}) calling ${tool}: ${detail}`);
|
|
285
|
+
this.code = code;
|
|
286
|
+
this.detail = detail;
|
|
287
|
+
this.tool = tool;
|
|
288
|
+
this.name = "McpError";
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=mcp-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized error handling for CLI commands.
|
|
3
|
+
*/
|
|
4
|
+
import { McpError } from "../mcp-client.js";
|
|
5
|
+
export function handleError(err) {
|
|
6
|
+
if (err instanceof McpError) {
|
|
7
|
+
console.error(`\nAmplitude MCP Error (${err.code}) calling ${err.tool}`);
|
|
8
|
+
if (err.code === 401 || err.code === 403) {
|
|
9
|
+
console.error("OAuth token invalid or expired.\n" +
|
|
10
|
+
"If using Nango: check that AMPLITUDE_ACCESS_TOKEN is set.\n" +
|
|
11
|
+
"If interactive: run 'amp auth login' to re-authenticate.");
|
|
12
|
+
}
|
|
13
|
+
else if (err.code === 429) {
|
|
14
|
+
console.error("Rate limited. Try again in a few minutes.");
|
|
15
|
+
}
|
|
16
|
+
else if (err.code === -32601) {
|
|
17
|
+
console.error("Unknown MCP tool. Run 'amp auth tools' to list available tools.");
|
|
18
|
+
}
|
|
19
|
+
console.error(`Detail: ${err.detail.slice(0, 500)}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (err instanceof Error) {
|
|
23
|
+
if (err.message.includes("Not logged in")) {
|
|
24
|
+
console.error(`\n${err.message}`);
|
|
25
|
+
console.error("Set AMPLITUDE_ACCESS_TOKEN env var, or run 'amp auth login'.");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
console.error(`\nError: ${err.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.error("\nUnknown error:", err);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,CAAC,IAAI,aAAa,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAEzE,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CACX,mCAAmC;gBACnC,6DAA6D;gBAC7D,0DAA0D,CAC3D,CAAC;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACnF,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CACX,8DAA8D,CAC/D,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting utilities.
|
|
3
|
+
* Supports JSON (default), compact JSON, and CSV.
|
|
4
|
+
*/
|
|
5
|
+
export type OutputFormat = "json" | "compact" | "csv";
|
|
6
|
+
export declare function formatOutput(data: unknown, format: OutputFormat): string;
|
|
7
|
+
/**
|
|
8
|
+
* Print to stdout. Commands use this for consistent output.
|
|
9
|
+
*/
|
|
10
|
+
export declare function output(data: unknown, format?: OutputFormat): void;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting utilities.
|
|
3
|
+
* Supports JSON (default), compact JSON, and CSV.
|
|
4
|
+
*/
|
|
5
|
+
export function formatOutput(data, format) {
|
|
6
|
+
switch (format) {
|
|
7
|
+
case "json":
|
|
8
|
+
return JSON.stringify(data, null, 2);
|
|
9
|
+
case "compact":
|
|
10
|
+
return JSON.stringify(data);
|
|
11
|
+
case "csv":
|
|
12
|
+
return toCSV(data);
|
|
13
|
+
default:
|
|
14
|
+
return JSON.stringify(data, null, 2);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Best-effort CSV conversion for tabular data.
|
|
19
|
+
* Handles arrays of objects and Amplitude's series format.
|
|
20
|
+
*/
|
|
21
|
+
function toCSV(data) {
|
|
22
|
+
if (!data || typeof data !== "object") {
|
|
23
|
+
return String(data);
|
|
24
|
+
}
|
|
25
|
+
// Array of objects → standard CSV
|
|
26
|
+
if (Array.isArray(data)) {
|
|
27
|
+
if (data.length === 0)
|
|
28
|
+
return "";
|
|
29
|
+
if (typeof data[0] !== "object" || data[0] === null) {
|
|
30
|
+
return data.map(String).join("\n");
|
|
31
|
+
}
|
|
32
|
+
const keys = Object.keys(data[0]);
|
|
33
|
+
const header = keys.map(escapeCSV).join(",");
|
|
34
|
+
const rows = data.map((row) => {
|
|
35
|
+
const r = row;
|
|
36
|
+
return keys.map((k) => escapeCSV(String(r[k] ?? ""))).join(",");
|
|
37
|
+
});
|
|
38
|
+
return [header, ...rows].join("\n");
|
|
39
|
+
}
|
|
40
|
+
// Amplitude API wraps data in a `data` key — unwrap it for CSV
|
|
41
|
+
const obj = data;
|
|
42
|
+
if (obj.data && typeof obj.data === "object" && !Array.isArray(obj.data)) {
|
|
43
|
+
const inner = obj.data;
|
|
44
|
+
if (inner.series && inner.xValues) {
|
|
45
|
+
return seriestoCSV(inner);
|
|
46
|
+
}
|
|
47
|
+
// Unwrap and try again
|
|
48
|
+
return toCSV(inner);
|
|
49
|
+
}
|
|
50
|
+
// Direct series format: { series: [...], xValues: [...] }
|
|
51
|
+
if (obj.series && obj.xValues) {
|
|
52
|
+
return seriestoCSV(obj);
|
|
53
|
+
}
|
|
54
|
+
// Fallback: key-value pairs
|
|
55
|
+
const entries = Object.entries(obj);
|
|
56
|
+
return entries.map(([k, v]) => `${escapeCSV(k)},${escapeCSV(String(v))}`).join("\n");
|
|
57
|
+
}
|
|
58
|
+
function seriestoCSV(data) {
|
|
59
|
+
const xValues = data.xValues;
|
|
60
|
+
const seriesLabels = data.seriesLabels;
|
|
61
|
+
const series = data.series;
|
|
62
|
+
if (!xValues || !series) {
|
|
63
|
+
return JSON.stringify(data, null, 2);
|
|
64
|
+
}
|
|
65
|
+
const headers = ["date"];
|
|
66
|
+
if (seriesLabels) {
|
|
67
|
+
headers.push(...seriesLabels.map((l) => escapeCSV(String(l))));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
series.forEach((_, i) => headers.push(`series_${i}`));
|
|
71
|
+
}
|
|
72
|
+
const rows = xValues.map((x, i) => {
|
|
73
|
+
const values = series.map((s) => String(s[i] ?? ""));
|
|
74
|
+
return [escapeCSV(x), ...values.map(escapeCSV)].join(",");
|
|
75
|
+
});
|
|
76
|
+
return [headers.join(","), ...rows].join("\n");
|
|
77
|
+
}
|
|
78
|
+
function escapeCSV(val) {
|
|
79
|
+
if (val.includes(",") || val.includes('"') || val.includes("\n")) {
|
|
80
|
+
return `"${val.replace(/"/g, '""')}"`;
|
|
81
|
+
}
|
|
82
|
+
return val;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Print to stdout. Commands use this for consistent output.
|
|
86
|
+
*/
|
|
87
|
+
export function output(data, format = "json") {
|
|
88
|
+
console.log(formatOutput(data, format));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP response helpers.
|
|
3
|
+
*/
|
|
4
|
+
import type { McpToolResult } from "../mcp-client.js";
|
|
5
|
+
/**
|
|
6
|
+
* Extract text content from MCP tool result.
|
|
7
|
+
* If the text is JSON, parse it; otherwise return the raw string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractMcpText(result: McpToolResult): unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Try to extract an editId from MCP query_dataset result.
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractEditId(data: unknown): string | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP response helpers.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Extract text content from MCP tool result.
|
|
6
|
+
* If the text is JSON, parse it; otherwise return the raw string.
|
|
7
|
+
*/
|
|
8
|
+
export function extractMcpText(result) {
|
|
9
|
+
const texts = result.content
|
|
10
|
+
.filter((c) => c.type === "text" && c.text)
|
|
11
|
+
.map((c) => c.text);
|
|
12
|
+
if (texts.length === 0)
|
|
13
|
+
return result;
|
|
14
|
+
if (texts.length === 1) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(texts[0]);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return texts[0];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return texts;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Try to extract an editId from MCP query_dataset result.
|
|
26
|
+
*/
|
|
27
|
+
export function extractEditId(data) {
|
|
28
|
+
if (typeof data === "string") {
|
|
29
|
+
const match = data.match(/editId["\s:]+["']?([a-zA-Z0-9_-]+)/);
|
|
30
|
+
return match?.[1] || null;
|
|
31
|
+
}
|
|
32
|
+
if (typeof data === "object" && data !== null) {
|
|
33
|
+
const obj = data;
|
|
34
|
+
if (typeof obj.editId === "string")
|
|
35
|
+
return obj.editId;
|
|
36
|
+
if (typeof obj.edit_id === "string")
|
|
37
|
+
return obj.edit_id;
|
|
38
|
+
for (const key of Object.keys(obj)) {
|
|
39
|
+
const val = obj[key];
|
|
40
|
+
if (typeof val === "object" && val !== null) {
|
|
41
|
+
const found = extractEditId(val);
|
|
42
|
+
if (found)
|
|
43
|
+
return found;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=mcp-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-helpers.js","sourceRoot":"","sources":["../../src/utils/mcp-helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC,CAAC;IAEvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC/D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,IAA+B,CAAC;QAC5C,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC;QACtD,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC,OAAO,CAAC;QACxD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBAC5C,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBACjC,IAAI,KAAK;oBAAE,OAAO,KAAK,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 utilities for Amplitude MCP server.
|
|
3
|
+
* Handles:
|
|
4
|
+
* - Dynamic client registration
|
|
5
|
+
* - Authorization code flow with PKCE (S256)
|
|
6
|
+
* - Token storage and refresh
|
|
7
|
+
* - Local callback server
|
|
8
|
+
*/
|
|
9
|
+
export interface OAuthTokens {
|
|
10
|
+
access_token: string;
|
|
11
|
+
refresh_token?: string;
|
|
12
|
+
token_type: string;
|
|
13
|
+
expires_in?: number;
|
|
14
|
+
expires_at?: number;
|
|
15
|
+
scope?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface OAuthConfig {
|
|
18
|
+
client_id: string;
|
|
19
|
+
region: string;
|
|
20
|
+
tokens: OAuthTokens;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the full config file (may contain apiKey/secretKey AND/OR oauth).
|
|
24
|
+
*/
|
|
25
|
+
export declare function readConfig(): Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Write config, preserving existing fields.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeConfig(updates: Record<string, unknown>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get stored OAuth tokens if they exist.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getOAuthConfig(): OAuthConfig | null;
|
|
34
|
+
/**
|
|
35
|
+
* Check if an OAuth token is available from any source.
|
|
36
|
+
* Does NOT throw — returns true/false.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hasOAuthToken(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get a valid access token. Checks sources in order:
|
|
41
|
+
* 1. AMPLITUDE_ACCESS_TOKEN (or AMPLITUDE_OAUTH_TOKEN) env var (injected by OpenClaw/Masterclaw via Nango)
|
|
42
|
+
* 2. AMPLITUDE_OAUTH_REFRESH_TOKEN env var (for auto-refresh via env)
|
|
43
|
+
* 3. ~/.amplituderc oauth config (from `amp auth login`)
|
|
44
|
+
*
|
|
45
|
+
* Env vars take priority — this is the managed-environment path where
|
|
46
|
+
* Masterclaw handles OAuth via Nango and injects tokens through
|
|
47
|
+
* openclaw.json → skills.entries.amplitude.env.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getAccessToken(region?: string): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Get MCP base URL for region.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getMcpBaseUrl(region?: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Run the full OAuth login flow.
|
|
56
|
+
* Returns the stored OAuth config.
|
|
57
|
+
*/
|
|
58
|
+
export declare function login(region?: string): Promise<OAuthConfig>;
|
|
59
|
+
/**
|
|
60
|
+
* Revoke tokens and clear OAuth config.
|
|
61
|
+
*/
|
|
62
|
+
export declare function logout(): Promise<void>;
|