harmony-build 0.2.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.
Files changed (72) hide show
  1. package/README.md +265 -0
  2. package/dist/packages/arkanalyzer-mcp/src/index.d.ts +5 -0
  3. package/dist/packages/arkanalyzer-mcp/src/index.js +220 -0
  4. package/dist/packages/arkanalyzer-mcp/src/index.js.map +1 -0
  5. package/dist/packages/arkcheck-mcp/src/index.d.ts +5 -0
  6. package/dist/packages/arkcheck-mcp/src/index.js +160 -0
  7. package/dist/packages/arkcheck-mcp/src/index.js.map +1 -0
  8. package/dist/packages/arkcheck-mcp/src/rules.d.ts +15 -0
  9. package/dist/packages/arkcheck-mcp/src/rules.js +90 -0
  10. package/dist/packages/arkcheck-mcp/src/rules.js.map +1 -0
  11. package/dist/packages/dev-mcp/src/hvigor-bridge.d.ts +31 -0
  12. package/dist/packages/dev-mcp/src/hvigor-bridge.js +179 -0
  13. package/dist/packages/dev-mcp/src/hvigor-bridge.js.map +1 -0
  14. package/dist/packages/dev-mcp/src/index.d.ts +5 -0
  15. package/dist/packages/dev-mcp/src/index.js +256 -0
  16. package/dist/packages/dev-mcp/src/index.js.map +1 -0
  17. package/dist/packages/dev-mcp/src/log-stream.d.ts +56 -0
  18. package/dist/packages/dev-mcp/src/log-stream.js +147 -0
  19. package/dist/packages/dev-mcp/src/log-stream.js.map +1 -0
  20. package/dist/packages/harmony-knowledge-mcp/src/index.d.ts +5 -0
  21. package/dist/packages/harmony-knowledge-mcp/src/index.js +164 -0
  22. package/dist/packages/harmony-knowledge-mcp/src/index.js.map +1 -0
  23. package/dist/packages/hdc-mcp/src/hdc-runner.d.ts +32 -0
  24. package/dist/packages/hdc-mcp/src/hdc-runner.js +60 -0
  25. package/dist/packages/hdc-mcp/src/hdc-runner.js.map +1 -0
  26. package/dist/packages/hdc-mcp/src/index.d.ts +5 -0
  27. package/dist/packages/hdc-mcp/src/index.js +155 -0
  28. package/dist/packages/hdc-mcp/src/index.js.map +1 -0
  29. package/dist/packages/hvigor-mcp/src/index.d.ts +5 -0
  30. package/dist/packages/hvigor-mcp/src/index.js +300 -0
  31. package/dist/packages/hvigor-mcp/src/index.js.map +1 -0
  32. package/dist/packages/hvigor-mcp/src/log-parser.d.ts +28 -0
  33. package/dist/packages/hvigor-mcp/src/log-parser.js +65 -0
  34. package/dist/packages/hvigor-mcp/src/log-parser.js.map +1 -0
  35. package/dist/packages/mcp-common/src/entry.d.ts +1 -0
  36. package/dist/packages/mcp-common/src/entry.js +22 -0
  37. package/dist/packages/mcp-common/src/entry.js.map +1 -0
  38. package/dist/packages/mcp-common/src/errors.d.ts +13 -0
  39. package/dist/packages/mcp-common/src/errors.js +23 -0
  40. package/dist/packages/mcp-common/src/errors.js.map +1 -0
  41. package/dist/packages/mcp-common/src/index.d.ts +6 -0
  42. package/dist/packages/mcp-common/src/index.js +7 -0
  43. package/dist/packages/mcp-common/src/index.js.map +1 -0
  44. package/dist/packages/mcp-common/src/logger.d.ts +11 -0
  45. package/dist/packages/mcp-common/src/logger.js +32 -0
  46. package/dist/packages/mcp-common/src/logger.js.map +1 -0
  47. package/dist/packages/mcp-common/src/mcp-helpers.d.ts +41 -0
  48. package/dist/packages/mcp-common/src/mcp-helpers.js +94 -0
  49. package/dist/packages/mcp-common/src/mcp-helpers.js.map +1 -0
  50. package/dist/packages/mcp-common/src/safe-exec.d.ts +23 -0
  51. package/dist/packages/mcp-common/src/safe-exec.js +74 -0
  52. package/dist/packages/mcp-common/src/safe-exec.js.map +1 -0
  53. package/dist/packages/mcp-common/src/sandbox.d.ts +31 -0
  54. package/dist/packages/mcp-common/src/sandbox.js +96 -0
  55. package/dist/packages/mcp-common/src/sandbox.js.map +1 -0
  56. package/dist/packages/profiler-mcp/src/index.d.ts +5 -0
  57. package/dist/packages/profiler-mcp/src/index.js +148 -0
  58. package/dist/packages/profiler-mcp/src/index.js.map +1 -0
  59. package/dist/packages/profiler-mcp/src/trace-analyzer.d.ts +38 -0
  60. package/dist/packages/profiler-mcp/src/trace-analyzer.js +95 -0
  61. package/dist/packages/profiler-mcp/src/trace-analyzer.js.map +1 -0
  62. package/dist/src/index.d.ts +17 -0
  63. package/dist/src/index.js +61 -0
  64. package/dist/src/index.js.map +1 -0
  65. package/dist/src/server/handlers.d.ts +11 -0
  66. package/dist/src/server/handlers.js +33 -0
  67. package/dist/src/server/handlers.js.map +1 -0
  68. package/dist/src/server/tools.d.ts +23 -0
  69. package/dist/src/server/tools.js +57 -0
  70. package/dist/src/server/tools.js.map +1 -0
  71. package/package.json +54 -0
  72. package//345/217/221/345/270/203/346/214/207/345/215/227.md +45 -0
@@ -0,0 +1,56 @@
1
+ import { Logger } from "@harmony-mcp/common";
2
+ export interface LogLine {
3
+ seq: number;
4
+ ts: string;
5
+ level: string;
6
+ tag?: string;
7
+ pid?: number;
8
+ text: string;
9
+ }
10
+ export interface LogTaskMeta {
11
+ task_id: string;
12
+ started_at: string;
13
+ pid_filter?: number;
14
+ tag_filter?: string;
15
+ level_filter?: string;
16
+ device?: string;
17
+ bundle_name?: string;
18
+ }
19
+ export interface LogTaskState extends LogTaskMeta {
20
+ status: "running" | "stopped" | "error";
21
+ total_lines: number;
22
+ errors: number;
23
+ warnings: number;
24
+ buffer_size: number;
25
+ last_error?: string;
26
+ /** 检测到的崩溃事件(最多 10 个) */
27
+ crashes: Array<{
28
+ ts: string;
29
+ signal?: string;
30
+ preview: string;
31
+ }>;
32
+ }
33
+ export interface StartLogStreamOptions {
34
+ hdcBin: string;
35
+ target?: string;
36
+ pid?: number;
37
+ tag?: string;
38
+ level?: "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL";
39
+ bundle_name?: string;
40
+ /** 环形缓冲容量;超出旧的覆盖;默认 5000 行 */
41
+ bufferCapacity?: number;
42
+ logger?: Logger;
43
+ }
44
+ export declare class LogStreamManager {
45
+ private tasks;
46
+ private idCounter;
47
+ start(opts: StartLogStreamOptions): LogTaskMeta;
48
+ read(taskId: string, sinceSeq?: number, max?: number): {
49
+ lines: LogLine[];
50
+ state: LogTaskState;
51
+ };
52
+ stop(taskId: string): LogTaskState;
53
+ list(): LogTaskState[];
54
+ /** 进程退出时调用,避免泄漏。 */
55
+ shutdown(): void;
56
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * 长寿命日志流管理:把 `hdc hilog` 进程长期挂着,
3
+ * 在 server 内存里维护环形缓冲,外部通过 task_id + since 增量拉取。
4
+ *
5
+ * 这是把 MCP "请求-响应" 模式改造成"对话里实时看日志"的关键模块。
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ const HILOG_PARSE = /^(\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d+)\s+(\d+)\s+(\d+)\s+([DIWEF])\s+([\w-]+)?\s*:\s*(.*)$/;
9
+ const CRASH_HINTS = /\b(FaultLogger|OH_CRASH|libc fatal|signal\s+\d+\s+\(SIG)/i;
10
+ export class LogStreamManager {
11
+ tasks = new Map();
12
+ idCounter = 0;
13
+ start(opts) {
14
+ const taskId = `log-${Date.now().toString(36)}-${this.idCounter++}`;
15
+ const args = [];
16
+ if (opts.target)
17
+ args.push("-t", opts.target);
18
+ args.push("shell", "hilog");
19
+ if (opts.level)
20
+ args.push("-L", levelShort(opts.level));
21
+ if (opts.pid)
22
+ args.push("-P", String(opts.pid));
23
+ if (opts.tag)
24
+ args.push("-T", opts.tag);
25
+ // 一直流式输出
26
+ args.push("-x");
27
+ const child = spawn(opts.hdcBin, args, { stdio: ["ignore", "pipe", "pipe"] });
28
+ const meta = {
29
+ task_id: taskId,
30
+ started_at: new Date().toISOString(),
31
+ pid_filter: opts.pid,
32
+ tag_filter: opts.tag,
33
+ level_filter: opts.level,
34
+ device: opts.target,
35
+ bundle_name: opts.bundle_name,
36
+ };
37
+ const task = {
38
+ meta,
39
+ child,
40
+ buffer: [],
41
+ capacity: opts.bufferCapacity ?? 5000,
42
+ nextSeq: 1,
43
+ status: "running",
44
+ errors: 0,
45
+ warnings: 0,
46
+ crashes: [],
47
+ };
48
+ let stdoutCarry = "";
49
+ child.stdout?.on("data", (chunk) => {
50
+ stdoutCarry += chunk.toString("utf8");
51
+ let idx;
52
+ while ((idx = stdoutCarry.indexOf("\n")) !== -1) {
53
+ const raw = stdoutCarry.slice(0, idx).trimEnd();
54
+ stdoutCarry = stdoutCarry.slice(idx + 1);
55
+ if (!raw)
56
+ continue;
57
+ const line = parseHilog(raw, task.nextSeq++);
58
+ if (!line)
59
+ continue;
60
+ appendLine(task, line);
61
+ }
62
+ });
63
+ child.stderr?.on("data", (chunk) => {
64
+ task.lastError = chunk.toString("utf8").slice(0, 500);
65
+ });
66
+ child.on("exit", (code) => {
67
+ task.status = code === 0 ? "stopped" : "error";
68
+ if (code !== 0 && code !== null) {
69
+ task.lastError = `hdc hilog exited with code ${code}: ${task.lastError ?? ""}`;
70
+ }
71
+ opts.logger?.info("log stream ended", { task: taskId, code });
72
+ });
73
+ child.on("error", (err) => {
74
+ task.status = "error";
75
+ task.lastError = err.message;
76
+ });
77
+ this.tasks.set(taskId, task);
78
+ return meta;
79
+ }
80
+ read(taskId, sinceSeq = 0, max = 200) {
81
+ const task = this.tasks.get(taskId);
82
+ if (!task)
83
+ throw new Error(`unknown task: ${taskId}`);
84
+ const slice = task.buffer.filter((l) => l.seq > sinceSeq).slice(0, max);
85
+ return { lines: slice, state: snapshot(task) };
86
+ }
87
+ stop(taskId) {
88
+ const task = this.tasks.get(taskId);
89
+ if (!task)
90
+ throw new Error(`unknown task: ${taskId}`);
91
+ if (task.child.exitCode === null)
92
+ task.child.kill("SIGTERM");
93
+ task.status = "stopped";
94
+ return snapshot(task);
95
+ }
96
+ list() {
97
+ return [...this.tasks.values()].map(snapshot);
98
+ }
99
+ /** 进程退出时调用,避免泄漏。 */
100
+ shutdown() {
101
+ for (const t of this.tasks.values())
102
+ if (t.child.exitCode === null)
103
+ t.child.kill("SIGTERM");
104
+ this.tasks.clear();
105
+ }
106
+ }
107
+ function appendLine(task, line) {
108
+ task.buffer.push(line);
109
+ if (task.buffer.length > task.capacity)
110
+ task.buffer.splice(0, task.buffer.length - task.capacity);
111
+ if (line.level === "E" || line.level === "F")
112
+ task.errors++;
113
+ else if (line.level === "W")
114
+ task.warnings++;
115
+ if (CRASH_HINTS.test(line.text) && task.crashes.length < 10) {
116
+ task.crashes.push({ ts: line.ts, preview: line.text.slice(0, 240) });
117
+ }
118
+ }
119
+ function parseHilog(raw, seq) {
120
+ const m = HILOG_PARSE.exec(raw);
121
+ if (!m)
122
+ return { seq, ts: "", level: "I", text: raw };
123
+ return {
124
+ seq,
125
+ ts: m[1],
126
+ pid: Number(m[2]),
127
+ level: m[4],
128
+ tag: m[5],
129
+ text: m[6],
130
+ };
131
+ }
132
+ function levelShort(l) {
133
+ return { DEBUG: "D", INFO: "I", WARN: "W", ERROR: "E", FATAL: "F" }[l] ?? "I";
134
+ }
135
+ function snapshot(t) {
136
+ return {
137
+ ...t.meta,
138
+ status: t.status,
139
+ total_lines: t.nextSeq - 1,
140
+ errors: t.errors,
141
+ warnings: t.warnings,
142
+ buffer_size: t.buffer.length,
143
+ last_error: t.lastError,
144
+ crashes: t.crashes,
145
+ };
146
+ }
147
+ //# sourceMappingURL=log-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-stream.js","sourceRoot":"","sources":["../../../../packages/dev-mcp/src/log-stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { z } from "zod";
3
+ import { ToolDef } from "@harmony-mcp/common";
4
+ export declare const NAMESPACE = "knowledge";
5
+ export declare const tools: ToolDef<z.ZodObject<z.ZodRawShape>>[];
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * harmony-knowledge-mcp: 把鸿蒙开发知识库(公司性能 SOP、最佳实践、提案)暴露为:
4
+ * - Resources:harmony-kb://docs/<rel-path>
5
+ * - Tools:search_kb(关键字检索),read_doc(按路径读取)
6
+ *
7
+ * 知识库根目录通过 HARMONY_KB_ROOTS 环境变量配置(多个用 PATH 分隔符隔开)。
8
+ * 默认:仓库的 docs/ 和 proposals/。
9
+ */
10
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
13
+ import { z } from "zod";
14
+ import fs from "node:fs/promises";
15
+ import path from "node:path";
16
+ import { createLogger, HarmonyMcpError, isMainModule, registerTools, } from "@harmony-mcp/common";
17
+ const logger = createLogger("harmony-knowledge-mcp");
18
+ const kbRoots = (process.env.HARMONY_KB_ROOTS ?? defaultRoots())
19
+ .split(path.delimiter)
20
+ .filter(Boolean)
21
+ .map((p) => path.resolve(p));
22
+ function defaultRoots() {
23
+ // 相对当前 cwd 找仓库内的 docs/ 与 proposals/
24
+ return [path.resolve("docs"), path.resolve("proposals")].join(path.delimiter);
25
+ }
26
+ async function walkMd(root) {
27
+ const out = [];
28
+ try {
29
+ await fs.access(root);
30
+ }
31
+ catch {
32
+ return out;
33
+ }
34
+ async function rec(d) {
35
+ const entries = await fs.readdir(d, { withFileTypes: true });
36
+ for (const e of entries) {
37
+ if (e.name.startsWith(".") || e.name === "node_modules")
38
+ continue;
39
+ const full = path.join(d, e.name);
40
+ if (e.isDirectory())
41
+ await rec(full);
42
+ else if (full.endsWith(".md") || full.endsWith(".sh") || full.endsWith(".txt")) {
43
+ out.push(full);
44
+ }
45
+ }
46
+ }
47
+ await rec(root);
48
+ return out;
49
+ }
50
+ function toUri(abs) {
51
+ // 用第一个匹配的 root 计算相对路径
52
+ for (const root of kbRoots) {
53
+ if (abs === root || abs.startsWith(root + path.sep)) {
54
+ const rel = path.relative(root, abs);
55
+ const rootName = path.basename(root);
56
+ return `harmony-kb://${rootName}/${rel.split(path.sep).join("/")}`;
57
+ }
58
+ }
59
+ return `harmony-kb://orphan/${path.basename(abs)}`;
60
+ }
61
+ function fromUri(uri) {
62
+ const m = /^harmony-kb:\/\/([^/]+)\/(.+)$/.exec(uri);
63
+ if (!m)
64
+ throw new HarmonyMcpError("INVALID_URI", `bad uri: ${uri}`);
65
+ const [, rootName, rel] = m;
66
+ const root = kbRoots.find((r) => path.basename(r) === rootName);
67
+ if (!root)
68
+ throw new HarmonyMcpError("UNKNOWN_ROOT", `unknown kb root: ${rootName}`);
69
+ const abs = path.resolve(root, rel);
70
+ if (!(abs === root || abs.startsWith(root + path.sep))) {
71
+ throw new HarmonyMcpError("URI_ESCAPES_ROOT", `uri escapes kb root: ${uri}`);
72
+ }
73
+ return abs;
74
+ }
75
+ const SearchKbSchema = z.object({
76
+ query: z.string().describe("要检索的关键字(多个空格分隔,全部出现才算命中)"),
77
+ max_results: z.number().int().positive().max(50).default(10),
78
+ });
79
+ const ReadDocSchema = z.object({
80
+ uri: z.string().describe("harmony-kb:// 协议的资源 URI"),
81
+ });
82
+ export const NAMESPACE = "knowledge";
83
+ export const tools = [
84
+ {
85
+ name: "search_kb",
86
+ description: "在鸿蒙知识库(docs/proposals/SOP)里按关键字检索片段",
87
+ schema: SearchKbSchema,
88
+ handler: async (a) => {
89
+ const terms = a.query.split(/\s+/).filter(Boolean).map((t) => t.toLowerCase());
90
+ const hits = [];
91
+ for (const root of kbRoots) {
92
+ const files = await walkMd(root);
93
+ for (const f of files) {
94
+ const text = await fs.readFile(f, "utf8");
95
+ const lower = text.toLowerCase();
96
+ if (!terms.every((t) => lower.includes(t)))
97
+ continue;
98
+ const lines = text.split(/\r?\n/);
99
+ for (let i = 0; i < lines.length; i++) {
100
+ const ll = lines[i].toLowerCase();
101
+ const hitCount = terms.reduce((s, t) => s + (ll.includes(t) ? 1 : 0), 0);
102
+ if (hitCount === 0)
103
+ continue;
104
+ hits.push({
105
+ uri: toUri(f),
106
+ line: i + 1,
107
+ preview: lines[i].trim().slice(0, 200),
108
+ score: hitCount,
109
+ });
110
+ }
111
+ }
112
+ }
113
+ hits.sort((a, b) => b.score - a.score);
114
+ return { query: a.query, hits: hits.slice(0, a.max_results), total: hits.length };
115
+ },
116
+ },
117
+ {
118
+ name: "read_doc",
119
+ description: "按 harmony-kb:// URI 读取完整文档内容",
120
+ schema: ReadDocSchema,
121
+ handler: async (a) => {
122
+ const abs = fromUri(a.uri);
123
+ const text = await fs.readFile(abs, "utf8");
124
+ return { uri: a.uri, bytes: text.length, content: text };
125
+ },
126
+ },
127
+ ];
128
+ async function main() {
129
+ const server = new Server({ name: "harmony-knowledge-mcp", version: "0.1.0" }, { capabilities: { tools: {}, resources: {} } });
130
+ // Resources:列出所有文档
131
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
132
+ const all = [];
133
+ for (const root of kbRoots) {
134
+ const files = await walkMd(root);
135
+ for (const f of files) {
136
+ all.push({
137
+ uri: toUri(f),
138
+ name: path.relative(root, f),
139
+ mimeType: f.endsWith(".md") ? "text/markdown" : "text/plain",
140
+ });
141
+ }
142
+ }
143
+ return { resources: all };
144
+ });
145
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
146
+ const uri = req.params.uri;
147
+ const abs = fromUri(uri);
148
+ const text = await fs.readFile(abs, "utf8");
149
+ const mimeType = abs.endsWith(".md") ? "text/markdown" : "text/plain";
150
+ return {
151
+ contents: [{ uri, mimeType, text }],
152
+ };
153
+ });
154
+ registerTools(server, { ListToolsRequestSchema, CallToolRequestSchema }, tools);
155
+ await server.connect(new StdioServerTransport());
156
+ logger.info("harmony-knowledge-mcp started", { kbRoots });
157
+ }
158
+ if (isMainModule(import.meta.url)) {
159
+ main().catch((err) => {
160
+ logger.error("fatal", { err: String(err) });
161
+ process.exit(1);
162
+ });
163
+ }
164
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/harmony-knowledge-mcp/src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * hdc 调用层:所有对 hdc CLI 的调用都通过这里走,统一处理 hdc 路径、超时、错误。
3
+ */
4
+ import { RunResult, Logger } from "@harmony-mcp/common";
5
+ export interface HdcRunnerOptions {
6
+ /** hdc 可执行文件路径;默认从环境变量 HDC_BIN 取,否则 'hdc' */
7
+ hdcBin?: string;
8
+ /** 默认目标设备序列号;可被单次调用覆盖 */
9
+ defaultTarget?: string;
10
+ /** 命令默认超时 */
11
+ timeoutMs?: number;
12
+ logger: Logger;
13
+ }
14
+ export declare class HdcRunner {
15
+ private readonly hdcBin;
16
+ private readonly defaultTarget?;
17
+ private readonly timeoutMs;
18
+ private readonly logger;
19
+ constructor(opts: HdcRunnerOptions);
20
+ /** 运行原始 hdc 命令;自动注入 -t <target>(如果传了 target)。 */
21
+ raw(args: string[], target?: string, timeoutMs?: number): Promise<RunResult>;
22
+ /** `hdc list targets` 解析为字符串数组 */
23
+ listTargets(): Promise<string[]>;
24
+ /** `hdc install <hap>` */
25
+ installHap(hapPath: string, target?: string): Promise<RunResult>;
26
+ /** `hdc uninstall <bundle>` */
27
+ uninstall(bundleName: string, target?: string): Promise<RunResult>;
28
+ /** `hdc shell <...>` —— 注意:禁止任意 shell,调用方需做白名单 */
29
+ shell(args: string[], target?: string, timeoutMs?: number): Promise<RunResult>;
30
+ /** `hdc file recv <remote> <local>` */
31
+ fileRecv(remote: string, local: string, target?: string): Promise<RunResult>;
32
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * hdc 调用层:所有对 hdc CLI 的调用都通过这里走,统一处理 hdc 路径、超时、错误。
3
+ */
4
+ import { runCommand, HarmonyMcpError } from "@harmony-mcp/common";
5
+ export class HdcRunner {
6
+ hdcBin;
7
+ defaultTarget;
8
+ timeoutMs;
9
+ logger;
10
+ constructor(opts) {
11
+ this.hdcBin = opts.hdcBin ?? process.env.HDC_BIN ?? "hdc";
12
+ this.defaultTarget = opts.defaultTarget ?? process.env.HDC_TARGET;
13
+ this.timeoutMs = opts.timeoutMs ?? 60_000;
14
+ this.logger = opts.logger;
15
+ }
16
+ /** 运行原始 hdc 命令;自动注入 -t <target>(如果传了 target)。 */
17
+ async raw(args, target, timeoutMs) {
18
+ const finalTarget = target ?? this.defaultTarget;
19
+ const finalArgs = finalTarget ? ["-t", finalTarget, ...args] : args;
20
+ this.logger.debug("hdc exec", { args: finalArgs });
21
+ const result = await runCommand(this.hdcBin, finalArgs, {
22
+ timeoutMs: timeoutMs ?? this.timeoutMs,
23
+ });
24
+ if (result.exitCode !== 0) {
25
+ this.logger.warn("hdc non-zero exit", {
26
+ exitCode: result.exitCode,
27
+ stderr: result.stderr.slice(0, 500),
28
+ });
29
+ }
30
+ return result;
31
+ }
32
+ /** `hdc list targets` 解析为字符串数组 */
33
+ async listTargets() {
34
+ const res = await this.raw(["list", "targets"]);
35
+ if (res.exitCode !== 0) {
36
+ throw new HarmonyMcpError("HDC_LIST_FAILED", res.stderr || "hdc list targets failed");
37
+ }
38
+ return res.stdout
39
+ .split(/\r?\n/)
40
+ .map((l) => l.trim())
41
+ .filter((l) => l && l !== "[Empty]");
42
+ }
43
+ /** `hdc install <hap>` */
44
+ async installHap(hapPath, target) {
45
+ return this.raw(["install", hapPath], target, 180_000);
46
+ }
47
+ /** `hdc uninstall <bundle>` */
48
+ async uninstall(bundleName, target) {
49
+ return this.raw(["uninstall", bundleName], target);
50
+ }
51
+ /** `hdc shell <...>` —— 注意:禁止任意 shell,调用方需做白名单 */
52
+ async shell(args, target, timeoutMs) {
53
+ return this.raw(["shell", ...args], target, timeoutMs);
54
+ }
55
+ /** `hdc file recv <remote> <local>` */
56
+ async fileRecv(remote, local, target) {
57
+ return this.raw(["file", "recv", remote, local], target, 180_000);
58
+ }
59
+ }
60
+ //# sourceMappingURL=hdc-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hdc-runner.js","sourceRoot":"","sources":["../../../../packages/hdc-mcp/src/hdc-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { z } from "zod";
3
+ import { ToolDef } from "@harmony-mcp/common";
4
+ export declare const NAMESPACE = "hdc";
5
+ export declare const tools: ToolDef<z.ZodObject<z.ZodRawShape>>[];
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hdc-mcp: 鸿蒙 hdc 命令行的 MCP Server 化。
4
+ *
5
+ * 提供的工具:
6
+ * - list_devices 列出当前连接的设备
7
+ * - install_hap 安装 HAP 包
8
+ * - uninstall 按 bundleName 卸载
9
+ * - start_ability 启动指定 Ability
10
+ * - stop_ability 按 bundleName 停止
11
+ * - hilog_capture 抓取 hilog(按 level/tag 过滤)
12
+ * - screencap 截屏并把图片拉回本地
13
+ */
14
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
17
+ import { z } from "zod";
18
+ import path from "node:path";
19
+ import os from "node:os";
20
+ import fs from "node:fs/promises";
21
+ import { createLogger, HarmonyMcpError, isMainModule, registerTools } from "@harmony-mcp/common";
22
+ import { HdcRunner } from "./hdc-runner.js";
23
+ const logger = createLogger("hdc-mcp");
24
+ const hdc = new HdcRunner({ logger });
25
+ // ---------- Schemas ----------
26
+ const TargetField = z.string().min(1).optional().describe("设备序列号;不填使用默认设备");
27
+ const ListDevicesSchema = z.object({});
28
+ const InstallHapSchema = z.object({
29
+ hap_path: z.string().describe("要安装的 .hap 文件本地绝对路径"),
30
+ target: TargetField,
31
+ });
32
+ const UninstallSchema = z.object({
33
+ bundle_name: z.string().describe("应用 bundleName,例如 com.example.demo"),
34
+ target: TargetField,
35
+ });
36
+ const StartAbilitySchema = z.object({
37
+ bundle_name: z.string().describe("应用 bundleName"),
38
+ ability_name: z.string().describe("Ability 名,例如 EntryAbility"),
39
+ target: TargetField,
40
+ });
41
+ const StopAbilitySchema = z.object({
42
+ bundle_name: z.string().describe("应用 bundleName"),
43
+ target: TargetField,
44
+ });
45
+ const HilogCaptureSchema = z.object({
46
+ duration_seconds: z.number().int().positive().max(120).default(5)
47
+ .describe("抓取时长(秒),上限 120"),
48
+ level: z.enum(["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]).optional()
49
+ .describe("最低日志等级"),
50
+ tag: z.string().optional().describe("按 tag 过滤"),
51
+ pid: z.number().int().optional().describe("按进程 PID 过滤"),
52
+ target: TargetField,
53
+ });
54
+ const ScreencapSchema = z.object({
55
+ local_path: z.string().optional()
56
+ .describe("本地保存路径,省略则保存到系统临时目录"),
57
+ target: TargetField,
58
+ });
59
+ // ---------- Tools ----------
60
+ export const NAMESPACE = "hdc";
61
+ export const tools = [
62
+ {
63
+ name: "list_devices",
64
+ description: "列出当前 hdc 检测到的所有设备/模拟器",
65
+ schema: ListDevicesSchema,
66
+ handler: async () => ({ devices: await hdc.listTargets() }),
67
+ },
68
+ {
69
+ name: "install_hap",
70
+ description: "安装 HAP 包到指定设备",
71
+ schema: InstallHapSchema,
72
+ handler: async (a) => {
73
+ const r = await hdc.installHap(a.hap_path, a.target);
74
+ return { ok: r.exitCode === 0, exit_code: r.exitCode, stdout: r.stdout, stderr: r.stderr };
75
+ },
76
+ },
77
+ {
78
+ name: "uninstall",
79
+ description: "按 bundleName 卸载应用",
80
+ schema: UninstallSchema,
81
+ handler: async (a) => {
82
+ const r = await hdc.uninstall(a.bundle_name, a.target);
83
+ return { ok: r.exitCode === 0, exit_code: r.exitCode, stdout: r.stdout, stderr: r.stderr };
84
+ },
85
+ },
86
+ {
87
+ name: "start_ability",
88
+ description: "启动指定 Ability(aa start -b <bundle> -a <ability>)",
89
+ schema: StartAbilitySchema,
90
+ handler: async (a) => {
91
+ const r = await hdc.shell(["aa", "start", "-b", a.bundle_name, "-a", a.ability_name], a.target);
92
+ return { ok: r.exitCode === 0, stdout: r.stdout, stderr: r.stderr };
93
+ },
94
+ },
95
+ {
96
+ name: "stop_ability",
97
+ description: "停止指定 bundleName 的进程(aa force-stop)",
98
+ schema: StopAbilitySchema,
99
+ handler: async (a) => {
100
+ const r = await hdc.shell(["aa", "force-stop", a.bundle_name], a.target);
101
+ return { ok: r.exitCode === 0, stdout: r.stdout, stderr: r.stderr };
102
+ },
103
+ },
104
+ {
105
+ name: "hilog_capture",
106
+ description: "抓取 hilog(可按 level/tag/pid 过滤),返回原始日志文本",
107
+ schema: HilogCaptureSchema,
108
+ handler: async (a) => {
109
+ const args = ["hilog"];
110
+ if (a.level)
111
+ args.push("-L", a.level);
112
+ if (a.tag)
113
+ args.push("-T", a.tag);
114
+ if (a.pid !== undefined)
115
+ args.push("-P", String(a.pid));
116
+ args.push("-x");
117
+ const r = await hdc.shell(args, a.target, (a.duration_seconds + 5) * 1000);
118
+ return { duration_seconds: a.duration_seconds, log: r.stdout, truncated: r.truncated };
119
+ },
120
+ },
121
+ {
122
+ name: "screencap",
123
+ description: "截屏并保存到本地",
124
+ schema: ScreencapSchema,
125
+ handler: async (a) => {
126
+ const localPath = a.local_path ?? path.join(os.tmpdir(), `harmony-screencap-${Date.now()}.png`);
127
+ const remoteTmp = `/data/local/tmp/mcp-screencap-${Date.now()}.png`;
128
+ const cap = await hdc.shell(["snapshot_display", "-f", remoteTmp], a.target, 15_000);
129
+ if (cap.exitCode !== 0) {
130
+ throw new HarmonyMcpError("SCREENCAP_FAILED", cap.stderr || "snapshot_display failed");
131
+ }
132
+ const recv = await hdc.fileRecv(remoteTmp, localPath, a.target);
133
+ if (recv.exitCode !== 0) {
134
+ throw new HarmonyMcpError("FILE_RECV_FAILED", recv.stderr || "file recv failed");
135
+ }
136
+ const stat = await fs.stat(localPath);
137
+ return { local_path: localPath, bytes: stat.size };
138
+ },
139
+ },
140
+ ];
141
+ // ---------- Boot ----------
142
+ async function main() {
143
+ const server = new Server({ name: "harmony-hdc-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
144
+ registerTools(server, { ListToolsRequestSchema, CallToolRequestSchema }, tools);
145
+ const transport = new StdioServerTransport();
146
+ await server.connect(transport);
147
+ logger.info("hdc-mcp server started on stdio");
148
+ }
149
+ if (isMainModule(import.meta.url)) {
150
+ main().catch((err) => {
151
+ logger.error("fatal", { err: String(err) });
152
+ process.exit(1);
153
+ });
154
+ }
155
+ //# sourceMappingURL=index.js.map