flutter-devtools-mcp 0.3.1

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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/README.zh-CN.md +292 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +55 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/services/ai-analysis.d.ts +6 -0
  9. package/dist/services/ai-analysis.d.ts.map +1 -0
  10. package/dist/services/ai-analysis.js +728 -0
  11. package/dist/services/ai-analysis.js.map +1 -0
  12. package/dist/services/collect-progress.d.ts +6 -0
  13. package/dist/services/collect-progress.d.ts.map +1 -0
  14. package/dist/services/collect-progress.js +23 -0
  15. package/dist/services/collect-progress.js.map +1 -0
  16. package/dist/services/cpu-profiler-service.d.ts +42 -0
  17. package/dist/services/cpu-profiler-service.d.ts.map +1 -0
  18. package/dist/services/cpu-profiler-service.js +208 -0
  19. package/dist/services/cpu-profiler-service.js.map +1 -0
  20. package/dist/services/cpu-symbol-filter.d.ts +10 -0
  21. package/dist/services/cpu-symbol-filter.d.ts.map +1 -0
  22. package/dist/services/cpu-symbol-filter.js +90 -0
  23. package/dist/services/cpu-symbol-filter.js.map +1 -0
  24. package/dist/services/http-profile-parser.d.ts +4 -0
  25. package/dist/services/http-profile-parser.d.ts.map +1 -0
  26. package/dist/services/http-profile-parser.js +71 -0
  27. package/dist/services/http-profile-parser.js.map +1 -0
  28. package/dist/services/json-parse-aggregate.d.ts +30 -0
  29. package/dist/services/json-parse-aggregate.d.ts.map +1 -0
  30. package/dist/services/json-parse-aggregate.js +155 -0
  31. package/dist/services/json-parse-aggregate.js.map +1 -0
  32. package/dist/services/network-aggregate.d.ts +50 -0
  33. package/dist/services/network-aggregate.d.ts.map +1 -0
  34. package/dist/services/network-aggregate.js +185 -0
  35. package/dist/services/network-aggregate.js.map +1 -0
  36. package/dist/services/network-capture-service.d.ts +50 -0
  37. package/dist/services/network-capture-service.d.ts.map +1 -0
  38. package/dist/services/network-capture-service.js +187 -0
  39. package/dist/services/network-capture-service.js.map +1 -0
  40. package/dist/services/network-capture.d.ts +2 -0
  41. package/dist/services/network-capture.d.ts.map +1 -0
  42. package/dist/services/network-capture.js +2 -0
  43. package/dist/services/network-capture.js.map +1 -0
  44. package/dist/services/performance-session.d.ts +41 -0
  45. package/dist/services/performance-session.d.ts.map +1 -0
  46. package/dist/services/performance-session.js +697 -0
  47. package/dist/services/performance-session.js.map +1 -0
  48. package/dist/services/performance-thresholds.d.ts +17 -0
  49. package/dist/services/performance-thresholds.d.ts.map +1 -0
  50. package/dist/services/performance-thresholds.js +19 -0
  51. package/dist/services/performance-thresholds.js.map +1 -0
  52. package/dist/services/profiler.d.ts +71 -0
  53. package/dist/services/profiler.d.ts.map +1 -0
  54. package/dist/services/profiler.js +314 -0
  55. package/dist/services/profiler.js.map +1 -0
  56. package/dist/services/rebuild-business-resolver.d.ts +24 -0
  57. package/dist/services/rebuild-business-resolver.d.ts.map +1 -0
  58. package/dist/services/rebuild-business-resolver.js +599 -0
  59. package/dist/services/rebuild-business-resolver.js.map +1 -0
  60. package/dist/services/rebuild-tracker-service.d.ts +45 -0
  61. package/dist/services/rebuild-tracker-service.d.ts.map +1 -0
  62. package/dist/services/rebuild-tracker-service.js +196 -0
  63. package/dist/services/rebuild-tracker-service.js.map +1 -0
  64. package/dist/services/session-types.d.ts +240 -0
  65. package/dist/services/session-types.d.ts.map +1 -0
  66. package/dist/services/session-types.js +180 -0
  67. package/dist/services/session-types.js.map +1 -0
  68. package/dist/services/threshold-alerts.d.ts +49 -0
  69. package/dist/services/threshold-alerts.d.ts.map +1 -0
  70. package/dist/services/threshold-alerts.js +258 -0
  71. package/dist/services/threshold-alerts.js.map +1 -0
  72. package/dist/services/timeline-cpu-parser.d.ts +28 -0
  73. package/dist/services/timeline-cpu-parser.d.ts.map +1 -0
  74. package/dist/services/timeline-cpu-parser.js +174 -0
  75. package/dist/services/timeline-cpu-parser.js.map +1 -0
  76. package/dist/services/timeline-extras.d.ts +61 -0
  77. package/dist/services/timeline-extras.d.ts.map +1 -0
  78. package/dist/services/timeline-extras.js +449 -0
  79. package/dist/services/timeline-extras.js.map +1 -0
  80. package/dist/services/vm-service-client.d.ts +123 -0
  81. package/dist/services/vm-service-client.d.ts.map +1 -0
  82. package/dist/services/vm-service-client.js +418 -0
  83. package/dist/services/vm-service-client.js.map +1 -0
  84. package/dist/tools/connection.d.ts +4 -0
  85. package/dist/tools/connection.d.ts.map +1 -0
  86. package/dist/tools/connection.js +127 -0
  87. package/dist/tools/connection.js.map +1 -0
  88. package/dist/tools/debug-actions.d.ts +4 -0
  89. package/dist/tools/debug-actions.d.ts.map +1 -0
  90. package/dist/tools/debug-actions.js +210 -0
  91. package/dist/tools/debug-actions.js.map +1 -0
  92. package/dist/tools/discover.d.ts +4 -0
  93. package/dist/tools/discover.d.ts.map +1 -0
  94. package/dist/tools/discover.js +183 -0
  95. package/dist/tools/discover.js.map +1 -0
  96. package/dist/tools/memory.d.ts +4 -0
  97. package/dist/tools/memory.d.ts.map +1 -0
  98. package/dist/tools/memory.js +145 -0
  99. package/dist/tools/memory.js.map +1 -0
  100. package/dist/tools/network.d.ts +4 -0
  101. package/dist/tools/network.d.ts.map +1 -0
  102. package/dist/tools/network.js +265 -0
  103. package/dist/tools/network.js.map +1 -0
  104. package/dist/tools/performance-session.d.ts +4 -0
  105. package/dist/tools/performance-session.d.ts.map +1 -0
  106. package/dist/tools/performance-session.js +105 -0
  107. package/dist/tools/performance-session.js.map +1 -0
  108. package/dist/tools/profiling.d.ts +5 -0
  109. package/dist/tools/profiling.d.ts.map +1 -0
  110. package/dist/tools/profiling.js +142 -0
  111. package/dist/tools/profiling.js.map +1 -0
  112. package/dist/tools/rebuild-tracker.d.ts +4 -0
  113. package/dist/tools/rebuild-tracker.d.ts.map +1 -0
  114. package/dist/tools/rebuild-tracker.js +228 -0
  115. package/dist/tools/rebuild-tracker.js.map +1 -0
  116. package/dist/tools/snapshot-diff.d.ts +4 -0
  117. package/dist/tools/snapshot-diff.d.ts.map +1 -0
  118. package/dist/tools/snapshot-diff.js +235 -0
  119. package/dist/tools/snapshot-diff.js.map +1 -0
  120. package/dist/tools/widget-tree.d.ts +4 -0
  121. package/dist/tools/widget-tree.d.ts.map +1 -0
  122. package/dist/tools/widget-tree.js +156 -0
  123. package/dist/tools/widget-tree.js.map +1 -0
  124. package/docs/README.md +9 -0
  125. package/docs/performance-audit-requirements.md +403 -0
  126. package/docs/performance-metrics-guide.md +207 -0
  127. package/docs/performance-session-simple-design.md +399 -0
  128. package/examples/app_network_image.dart +242 -0
  129. package/examples/app_network_image_README.md +51 -0
  130. package/package.json +69 -0
@@ -0,0 +1,265 @@
1
+ import { z } from "zod";
2
+ function formatBytes(bytes) {
3
+ if (bytes === 0)
4
+ return "0 B";
5
+ const units = ["B", "KB", "MB"];
6
+ const k = 1024;
7
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
8
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${units[i]}`;
9
+ }
10
+ function formatDuration(ms) {
11
+ if (ms < 1)
12
+ return "<1ms";
13
+ if (ms < 1000)
14
+ return `${Math.round(ms)}ms`;
15
+ return `${(ms / 1000).toFixed(2)}s`;
16
+ }
17
+ export function registerNetworkTools(server, client) {
18
+ let capturing = false;
19
+ let requests = new Map();
20
+ let captureStartTime = 0;
21
+ const httpListener = (event) => {
22
+ if (!event)
23
+ return;
24
+ const kind = event.extensionKind ?? event.kind;
25
+ const data = event.extensionData ?? event;
26
+ if (kind === "dart:io.httpClient.request.start" || kind === "HttpClientRequest") {
27
+ const id = data?.id?.toString() ?? data?.isolateId ?? `req_${Date.now()}`;
28
+ requests.set(id, {
29
+ id,
30
+ method: data?.method ?? "GET",
31
+ uri: data?.uri ?? data?.url ?? "unknown",
32
+ startTime: Date.now(),
33
+ requestSize: data?.contentLength ?? data?.requestSize,
34
+ });
35
+ }
36
+ if (kind === "dart:io.httpClient.request.finish" || kind === "HttpClientResponse") {
37
+ const id = data?.id?.toString() ?? data?.isolateId;
38
+ const req = id ? requests.get(id) : undefined;
39
+ if (req) {
40
+ req.endTime = Date.now();
41
+ req.statusCode = data?.statusCode ?? data?.status;
42
+ req.responseSize =
43
+ data?.contentLength ?? data?.responseSize ?? data?.compressionState?.length;
44
+ req.contentType = data?.contentType ?? data?.headers?.["content-type"];
45
+ }
46
+ }
47
+ if (kind === "dart:io.httpClient.request.error") {
48
+ const id = data?.id?.toString();
49
+ const req = id ? requests.get(id) : undefined;
50
+ if (req) {
51
+ req.endTime = Date.now();
52
+ req.error = data?.error ?? data?.message ?? "Unknown error";
53
+ }
54
+ }
55
+ if (kind === "Extension" &&
56
+ data?.extensionKind?.includes("http")) {
57
+ const extData = data.extensionData;
58
+ if (extData?.method && extData?.uri) {
59
+ const id = extData.id?.toString() ?? `ext_${Date.now()}`;
60
+ const existing = requests.get(id);
61
+ if (!existing) {
62
+ requests.set(id, {
63
+ id,
64
+ method: extData.method,
65
+ uri: extData.uri,
66
+ startTime: extData.startTime ?? Date.now(),
67
+ endTime: extData.endTime,
68
+ statusCode: extData.statusCode ?? extData.status,
69
+ responseSize: extData.responseSize ?? extData.contentLength,
70
+ contentType: extData.contentType,
71
+ });
72
+ }
73
+ }
74
+ }
75
+ };
76
+ server.tool("start_network_capture", "Start capturing HTTP network traffic from the running Flutter app. After starting, use the app to trigger API calls, then call stop_network_capture to see all requests with timing, status codes, and sizes.", {}, async () => {
77
+ if (!client.connected) {
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: "Not connected. Use the `connect` tool first.",
83
+ },
84
+ ],
85
+ isError: true,
86
+ };
87
+ }
88
+ if (capturing) {
89
+ return {
90
+ content: [
91
+ {
92
+ type: "text",
93
+ text: "Already capturing network traffic. Call stop_network_capture first.",
94
+ },
95
+ ],
96
+ isError: true,
97
+ };
98
+ }
99
+ requests = new Map();
100
+ captureStartTime = Date.now();
101
+ capturing = true;
102
+ client.on("stream:Extension", httpListener);
103
+ client.on("stream:Logging", httpListener);
104
+ client.on("event", httpListener);
105
+ try {
106
+ await client
107
+ .callServiceExtension("ext.dart.io.httpEnableTimelineLogging", undefined, {
108
+ enabled: true,
109
+ })
110
+ .catch(() => { });
111
+ }
112
+ catch {
113
+ // Extension may not be available
114
+ }
115
+ return {
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: "✅ Network capture started. Use the app to trigger API calls, then call `stop_network_capture` to see the results.",
120
+ },
121
+ ],
122
+ };
123
+ });
124
+ server.tool("stop_network_capture", "Stop capturing network traffic and get a detailed report of all HTTP requests including method, URL, status code, response time, and payload size.", {
125
+ sortBy: z
126
+ .enum(["time", "duration", "size"])
127
+ .default("time")
128
+ .describe("Sort requests by: time (chronological), duration (slowest first), size (largest first)"),
129
+ }, async ({ sortBy }) => {
130
+ if (!capturing) {
131
+ return {
132
+ content: [
133
+ {
134
+ type: "text",
135
+ text: "Not capturing network traffic. Call start_network_capture first.",
136
+ },
137
+ ],
138
+ isError: true,
139
+ };
140
+ }
141
+ capturing = false;
142
+ client.off("stream:Extension", httpListener);
143
+ client.off("stream:Logging", httpListener);
144
+ client.off("event", httpListener);
145
+ try {
146
+ await client
147
+ .callServiceExtension("ext.dart.io.httpEnableTimelineLogging", undefined, {
148
+ enabled: false,
149
+ })
150
+ .catch(() => { });
151
+ }
152
+ catch {
153
+ // Best effort
154
+ }
155
+ const durationMs = Date.now() - captureStartTime;
156
+ const allRequests = Array.from(requests.values());
157
+ if (allRequests.length === 0) {
158
+ return {
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: [
163
+ `Captured for ${(durationMs / 1000).toFixed(1)}s — no HTTP requests detected.`,
164
+ "",
165
+ "This can happen if:",
166
+ "• The app didn't make any network calls during capture",
167
+ "• HTTP timeline logging is not supported in this Flutter version",
168
+ "• The app uses a custom HTTP client that doesn't go through dart:io",
169
+ "",
170
+ "Try making the app load data (pull to refresh, navigate to a new screen).",
171
+ ].join("\n"),
172
+ },
173
+ ],
174
+ };
175
+ }
176
+ const sorted = [...allRequests];
177
+ switch (sortBy) {
178
+ case "duration":
179
+ sorted.sort((a, b) => {
180
+ const durA = a.endTime ? a.endTime - a.startTime : 0;
181
+ const durB = b.endTime ? b.endTime - b.startTime : 0;
182
+ return durB - durA;
183
+ });
184
+ break;
185
+ case "size":
186
+ sorted.sort((a, b) => (b.responseSize ?? 0) - (a.responseSize ?? 0));
187
+ break;
188
+ default:
189
+ sorted.sort((a, b) => a.startTime - b.startTime);
190
+ }
191
+ const completedRequests = sorted.filter((r) => r.endTime);
192
+ const failedRequests = sorted.filter((r) => r.error);
193
+ const pendingRequests = sorted.filter((r) => !r.endTime && !r.error);
194
+ const totalSize = sorted.reduce((sum, r) => sum + (r.responseSize ?? 0), 0);
195
+ const durations = completedRequests.map((r) => r.endTime - r.startTime);
196
+ const avgDuration = durations.length > 0
197
+ ? durations.reduce((a, b) => a + b, 0) / durations.length
198
+ : 0;
199
+ const maxDuration = durations.length > 0 ? Math.max(...durations) : 0;
200
+ const output = [
201
+ "═══════════════════════════════════════════════════════════",
202
+ " NETWORK TRAFFIC REPORT",
203
+ "═══════════════════════════════════════════════════════════",
204
+ "",
205
+ "📊 SUMMARY",
206
+ "───────────────────────────────────────────────────────────",
207
+ `Captured for ${(durationMs / 1000).toFixed(1)}s`,
208
+ `Total requests: ${allRequests.length}`,
209
+ `Completed: ${completedRequests.length} | Failed: ${failedRequests.length} | Pending: ${pendingRequests.length}`,
210
+ `Total response size: ${formatBytes(totalSize)}`,
211
+ `Average response time: ${formatDuration(avgDuration)}`,
212
+ `Slowest request: ${formatDuration(maxDuration)}`,
213
+ "",
214
+ "📡 REQUESTS",
215
+ "───────────────────────────────────────────────────────────",
216
+ ];
217
+ for (const req of sorted) {
218
+ const duration = req.endTime
219
+ ? formatDuration(req.endTime - req.startTime)
220
+ : "pending...";
221
+ const status = req.error
222
+ ? `❌ ${req.error}`
223
+ : req.statusCode
224
+ ? req.statusCode >= 400
225
+ ? `🔴 ${req.statusCode}`
226
+ : req.statusCode >= 300
227
+ ? `🟡 ${req.statusCode}`
228
+ : `🟢 ${req.statusCode}`
229
+ : "⏳";
230
+ const size = req.responseSize
231
+ ? formatBytes(req.responseSize)
232
+ : "-";
233
+ output.push(`${status} ${req.method.padEnd(6)} ${duration.padStart(8)} | ${size.padStart(8)} | ${req.uri}`);
234
+ }
235
+ const slowRequests = completedRequests.filter((r) => r.endTime - r.startTime > 2000);
236
+ const largeResponses = completedRequests.filter((r) => (r.responseSize ?? 0) > 500000);
237
+ const errors = failedRequests;
238
+ if (slowRequests.length > 0 ||
239
+ largeResponses.length > 0 ||
240
+ errors.length > 0) {
241
+ output.push("");
242
+ output.push("⚠️ CONCERNS");
243
+ output.push("───────────────────────────────────────────────────────────");
244
+ for (const req of slowRequests.slice(0, 3)) {
245
+ const dur = formatDuration(req.endTime - req.startTime);
246
+ output.push(`• SLOW: ${req.method} ${req.uri} took ${dur}`);
247
+ }
248
+ for (const req of largeResponses.slice(0, 3)) {
249
+ output.push(`• LARGE: ${req.method} ${req.uri} returned ${formatBytes(req.responseSize)}`);
250
+ }
251
+ for (const req of errors.slice(0, 3)) {
252
+ output.push(`• ERROR: ${req.method} ${req.uri} — ${req.error}`);
253
+ }
254
+ }
255
+ return {
256
+ content: [
257
+ {
258
+ type: "text",
259
+ text: output.join("\n"),
260
+ },
261
+ ],
262
+ };
263
+ });
264
+ }
265
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/tools/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { PerformanceSession } from "../services/performance-session.js";
3
+ export declare function registerPerformanceSessionTools(server: McpServer, session: PerformanceSession): void;
4
+ //# sourceMappingURL=performance-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-session.d.ts","sourceRoot":"","sources":["../../src/tools/performance-session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAwB,MAAM,oCAAoC,CAAC;AAG9F,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,kBAAkB,QAmI5B"}
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+ import { formatSessionSummary } from "../services/performance-session.js";
3
+ import { PERFORMANCE_SESSIONS_DIR } from "../services/session-types.js";
4
+ export function registerPerformanceSessionTools(server, session) {
5
+ server.tool("collect_performance_session", `采集指定时长的 Flutter 性能数据并返回结构化 JSON,供 AI 结合源码给出优化方案。
6
+
7
+ 重要:durationSec 仅为「录制窗口」(默认还会 +2s 预热)。工具返回前还需收尾(Timeline/CPU/报告),Android 真机总耗时常为 录制+30s~+120s,期间会输出 [perf] 进度。
8
+
9
+ 推荐话术:用户说「连接 Flutter App,开始采集性能数据 30s」时:
10
+ 1. 先 discover_apps / connect(adb 转发口需用真实 VM URI,非 302 端口)
11
+ 2. 调用本工具并告知用户「录制 30s,请保持 App 前台;收尾可能再需 1~2 分钟」
12
+ 3. 工具返回后读取 filesToInspect,按 hintsForAnalysis 输出 P0/P1/P2
13
+
14
+ 采集项:帧率/jank、Widget 重建(含行号)、CPU Top 函数、网络;内存默认关闭(enableMemory=true 开启)。`, {
15
+ scenario: z
16
+ .string()
17
+ .default("manual-session")
18
+ .describe("场景标识,如 home-list-scroll"),
19
+ durationSec: z
20
+ .number()
21
+ .min(5)
22
+ .max(120)
23
+ .default(30)
24
+ .describe("录制窗口(秒);不含收尾分析时间。总耗时见返回 progressLog / wallClockSec"),
25
+ enableNetwork: z.boolean().default(true),
26
+ enableCpuProfile: z.boolean().default(true),
27
+ enableMemory: z
28
+ .boolean()
29
+ .default(false)
30
+ .describe("是否调用 getAllocationProfile(gc:true);默认 false 以缩短 Android 收尾"),
31
+ topN: z.number().min(5).max(50).default(15),
32
+ saveToFile: z
33
+ .boolean()
34
+ .default(false)
35
+ .describe(`是否保存 JSON/AI 报告到 Flutter 工程下的 ${PERFORMANCE_SESSIONS_DIR}/`),
36
+ outputDir: z
37
+ .string()
38
+ .optional()
39
+ .describe(`覆盖默认输出目录;未指定时使用 {projectRoot}/${PERFORMANCE_SESSIONS_DIR}`),
40
+ generateAiReport: z
41
+ .boolean()
42
+ .default(true)
43
+ .describe("是否生成 P0/P1/P2 规则引擎分析报告"),
44
+ projectRoot: z
45
+ .string()
46
+ .optional()
47
+ .describe("Flutter 工程根目录(读取源码 + 默认将报告保存到该目录下的 performance-sessions/)"),
48
+ }, async ({ scenario, durationSec, enableNetwork, enableCpuProfile, enableMemory, topN, saveToFile, outputDir, generateAiReport, projectRoot, }) => {
49
+ try {
50
+ const { result, savedPath } = await session.collect({
51
+ scenario,
52
+ durationSec,
53
+ enableNetwork,
54
+ enableCpuProfile,
55
+ enableMemory,
56
+ topN,
57
+ saveToFile,
58
+ outputDir,
59
+ generateAiReport,
60
+ projectRoot,
61
+ });
62
+ const summary = formatSessionSummary(result);
63
+ const json = JSON.stringify(result, null, 2);
64
+ const rec = result.recordingWindowSec ?? durationSec;
65
+ const wall = result.wallClockSec ?? result.durationSec;
66
+ const text = [
67
+ `✅ 性能采集完成(录制 ${rec}s,总耗时 ${wall}s,场景:${scenario})`,
68
+ "",
69
+ "── 采集进度 ──",
70
+ ...(result.progressLog?.map((p) => `- ${p}`) ?? ["- (无进度日志)"]),
71
+ "",
72
+ "── 中文摘要 ──",
73
+ summary,
74
+ ];
75
+ if (result.aiAnalysis) {
76
+ text.push("", "── AI 优化方案(规则引擎)──", result.aiAnalysis);
77
+ }
78
+ text.push("", "── PerformanceSessionResult JSON ──", json);
79
+ if (savedPath) {
80
+ text.push("", `已保存: ${savedPath}`);
81
+ if (result.aiAnalysis) {
82
+ text.push(`AI 分析: ${savedPath.replace(/\.json$/, ".ai.md")}`);
83
+ }
84
+ }
85
+ if (result.durationSec < 5) {
86
+ text.push("", "⚠️ 采集时长过短,数据可能不足。");
87
+ }
88
+ return {
89
+ content: [{ type: "text", text: text.join("\n") }],
90
+ };
91
+ }
92
+ catch (error) {
93
+ return {
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `采集失败: ${error instanceof Error ? error.message : String(error)}`,
98
+ },
99
+ ],
100
+ isError: true,
101
+ };
102
+ }
103
+ });
104
+ }
105
+ //# sourceMappingURL=performance-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-session.js","sourceRoot":"","sources":["../../src/tools/performance-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { FlutterVmServiceClient } from "../services/vm-service-client.js";
3
+ import { Profiler } from "../services/profiler.js";
4
+ export declare function registerProfilingTools(server: McpServer, client: FlutterVmServiceClient, profiler: Profiler): void;
5
+ //# sourceMappingURL=profiling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiling.d.ts","sourceRoot":"","sources":["../../src/tools/profiling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,EAAE,QAAQ,QAuKnB"}
@@ -0,0 +1,142 @@
1
+ export function registerProfilingTools(server, client, profiler) {
2
+ server.tool("start_profiling", "Start a performance profiling session. After starting, interact with the app (scroll, tap, navigate) to generate activity, then call stop_profiling to get the analysis. The app should be running in profile mode (`flutter run --profile`) for accurate results.", {}, async () => {
3
+ if (!client.connected) {
4
+ return {
5
+ content: [
6
+ {
7
+ type: "text",
8
+ text: "Not connected. Use the `connect` tool first.",
9
+ },
10
+ ],
11
+ isError: true,
12
+ };
13
+ }
14
+ if (profiler.isActive) {
15
+ return {
16
+ content: [
17
+ {
18
+ type: "text",
19
+ text: "A profiling session is already active. Call stop_profiling first.",
20
+ },
21
+ ],
22
+ isError: true,
23
+ };
24
+ }
25
+ try {
26
+ await profiler.start();
27
+ return {
28
+ content: [
29
+ {
30
+ type: "text",
31
+ text: "✅ Profiling started. Interact with the app now (scroll, tap, navigate), then call `stop_profiling` to get the analysis.",
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ catch (error) {
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: `Failed to start profiling: ${error instanceof Error ? error.message : String(error)}`,
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
47
+ });
48
+ server.tool("stop_profiling", "Stop the current profiling session and get a detailed performance analysis including frame timing, jank detection, CPU hotspots, build/layout/paint phase analysis, and actionable recommendations.", {}, async () => {
49
+ if (!client.connected) {
50
+ return {
51
+ content: [
52
+ {
53
+ type: "text",
54
+ text: "Not connected. Use the `connect` tool first.",
55
+ },
56
+ ],
57
+ isError: true,
58
+ };
59
+ }
60
+ if (!profiler.isActive) {
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: "No active profiling session. Call start_profiling first.",
66
+ },
67
+ ],
68
+ isError: true,
69
+ };
70
+ }
71
+ try {
72
+ const result = await profiler.stop();
73
+ const output = [
74
+ "═══════════════════════════════════════════════════════════",
75
+ " FLUTTER PERFORMANCE ANALYSIS REPORT",
76
+ "═══════════════════════════════════════════════════════════",
77
+ "",
78
+ "📊 SUMMARY",
79
+ "───────────────────────────────────────────────────────────",
80
+ ...result.summary,
81
+ "",
82
+ "📈 FRAME ANALYSIS",
83
+ "───────────────────────────────────────────────────────────",
84
+ `Total frames: ${result.frameAnalysis.totalFrames}`,
85
+ `Average frame time: ${result.frameAnalysis.averageFrameTimeMs.toFixed(2)}ms`,
86
+ `P90 frame time: ${result.frameAnalysis.p90FrameTimeMs.toFixed(2)}ms`,
87
+ `P99 frame time: ${result.frameAnalysis.p99FrameTimeMs.toFixed(2)}ms`,
88
+ `Max frame time: ${result.frameAnalysis.maxFrameTimeMs.toFixed(2)}ms`,
89
+ `Jank frames: ${result.frameAnalysis.jankFrames} (${result.frameAnalysis.jankPercentage.toFixed(1)}%)`,
90
+ `Target: ${result.frameAnalysis.targetFrameTimeMs.toFixed(1)}ms (${Math.round(1000 / result.frameAnalysis.targetFrameTimeMs)}fps)`,
91
+ "",
92
+ "🔧 PHASE BREAKDOWN",
93
+ "───────────────────────────────────────────────────────────",
94
+ `Build: avg ${result.buildPhaseAnalysis.avgBuildTimeMs.toFixed(2)}ms | max ${result.buildPhaseAnalysis.maxBuildTimeMs.toFixed(2)}ms | ${result.buildPhaseAnalysis.buildCount} calls`,
95
+ `Layout: avg ${result.layoutPhaseAnalysis.avgLayoutTimeMs.toFixed(2)}ms | max ${result.layoutPhaseAnalysis.maxLayoutTimeMs.toFixed(2)}ms | ${result.layoutPhaseAnalysis.layoutCount} calls`,
96
+ `Paint: avg ${result.paintPhaseAnalysis.avgPaintTimeMs.toFixed(2)}ms | max ${result.paintPhaseAnalysis.maxPaintTimeMs.toFixed(2)}ms | ${result.paintPhaseAnalysis.paintCount} calls`,
97
+ "",
98
+ ];
99
+ if (result.cpuHotspots.length > 0) {
100
+ output.push("🔥 CPU HOTSPOTS");
101
+ output.push("───────────────────────────────────────────────────────────");
102
+ for (const h of result.cpuHotspots.slice(0, 10)) {
103
+ const severityIcon = h.severity === "critical"
104
+ ? "🔴"
105
+ : h.severity === "high"
106
+ ? "🟠"
107
+ : h.severity === "medium"
108
+ ? "🟡"
109
+ : "🟢";
110
+ output.push(`${severityIcon} ${h.name} [${h.severity.toUpperCase()}]`);
111
+ output.push(` Total: ${h.totalDurationMs.toFixed(1)}ms | Avg: ${h.avgDurationMs.toFixed(1)}ms | Max: ${h.maxDurationMs.toFixed(1)}ms | Calls: ${h.callCount}`);
112
+ }
113
+ output.push("");
114
+ }
115
+ output.push("💡 RECOMMENDATIONS");
116
+ output.push("───────────────────────────────────────────────────────────");
117
+ for (const rec of result.recommendations) {
118
+ output.push(`• ${rec}`);
119
+ }
120
+ return {
121
+ content: [
122
+ {
123
+ type: "text",
124
+ text: output.join("\n"),
125
+ },
126
+ ],
127
+ };
128
+ }
129
+ catch (error) {
130
+ return {
131
+ content: [
132
+ {
133
+ type: "text",
134
+ text: `Failed to stop profiling: ${error instanceof Error ? error.message : String(error)}`,
135
+ },
136
+ ],
137
+ isError: true,
138
+ };
139
+ }
140
+ });
141
+ }
142
+ //# sourceMappingURL=profiling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiling.js","sourceRoot":"","sources":["../../src/tools/profiling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { FlutterVmServiceClient } from "../services/vm-service-client.js";
3
+ export declare function registerRebuildTrackerTools(server: McpServer, client: FlutterVmServiceClient): void;
4
+ //# sourceMappingURL=rebuild-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebuild-tracker.d.ts","sourceRoot":"","sources":["../../src/tools/rebuild-tracker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAU1E,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,sBAAsB,QAkS/B"}