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.
- package/README.md +265 -0
- package/dist/packages/arkanalyzer-mcp/src/index.d.ts +5 -0
- package/dist/packages/arkanalyzer-mcp/src/index.js +220 -0
- package/dist/packages/arkanalyzer-mcp/src/index.js.map +1 -0
- package/dist/packages/arkcheck-mcp/src/index.d.ts +5 -0
- package/dist/packages/arkcheck-mcp/src/index.js +160 -0
- package/dist/packages/arkcheck-mcp/src/index.js.map +1 -0
- package/dist/packages/arkcheck-mcp/src/rules.d.ts +15 -0
- package/dist/packages/arkcheck-mcp/src/rules.js +90 -0
- package/dist/packages/arkcheck-mcp/src/rules.js.map +1 -0
- package/dist/packages/dev-mcp/src/hvigor-bridge.d.ts +31 -0
- package/dist/packages/dev-mcp/src/hvigor-bridge.js +179 -0
- package/dist/packages/dev-mcp/src/hvigor-bridge.js.map +1 -0
- package/dist/packages/dev-mcp/src/index.d.ts +5 -0
- package/dist/packages/dev-mcp/src/index.js +256 -0
- package/dist/packages/dev-mcp/src/index.js.map +1 -0
- package/dist/packages/dev-mcp/src/log-stream.d.ts +56 -0
- package/dist/packages/dev-mcp/src/log-stream.js +147 -0
- package/dist/packages/dev-mcp/src/log-stream.js.map +1 -0
- package/dist/packages/harmony-knowledge-mcp/src/index.d.ts +5 -0
- package/dist/packages/harmony-knowledge-mcp/src/index.js +164 -0
- package/dist/packages/harmony-knowledge-mcp/src/index.js.map +1 -0
- package/dist/packages/hdc-mcp/src/hdc-runner.d.ts +32 -0
- package/dist/packages/hdc-mcp/src/hdc-runner.js +60 -0
- package/dist/packages/hdc-mcp/src/hdc-runner.js.map +1 -0
- package/dist/packages/hdc-mcp/src/index.d.ts +5 -0
- package/dist/packages/hdc-mcp/src/index.js +155 -0
- package/dist/packages/hdc-mcp/src/index.js.map +1 -0
- package/dist/packages/hvigor-mcp/src/index.d.ts +5 -0
- package/dist/packages/hvigor-mcp/src/index.js +300 -0
- package/dist/packages/hvigor-mcp/src/index.js.map +1 -0
- package/dist/packages/hvigor-mcp/src/log-parser.d.ts +28 -0
- package/dist/packages/hvigor-mcp/src/log-parser.js +65 -0
- package/dist/packages/hvigor-mcp/src/log-parser.js.map +1 -0
- package/dist/packages/mcp-common/src/entry.d.ts +1 -0
- package/dist/packages/mcp-common/src/entry.js +22 -0
- package/dist/packages/mcp-common/src/entry.js.map +1 -0
- package/dist/packages/mcp-common/src/errors.d.ts +13 -0
- package/dist/packages/mcp-common/src/errors.js +23 -0
- package/dist/packages/mcp-common/src/errors.js.map +1 -0
- package/dist/packages/mcp-common/src/index.d.ts +6 -0
- package/dist/packages/mcp-common/src/index.js +7 -0
- package/dist/packages/mcp-common/src/index.js.map +1 -0
- package/dist/packages/mcp-common/src/logger.d.ts +11 -0
- package/dist/packages/mcp-common/src/logger.js +32 -0
- package/dist/packages/mcp-common/src/logger.js.map +1 -0
- package/dist/packages/mcp-common/src/mcp-helpers.d.ts +41 -0
- package/dist/packages/mcp-common/src/mcp-helpers.js +94 -0
- package/dist/packages/mcp-common/src/mcp-helpers.js.map +1 -0
- package/dist/packages/mcp-common/src/safe-exec.d.ts +23 -0
- package/dist/packages/mcp-common/src/safe-exec.js +74 -0
- package/dist/packages/mcp-common/src/safe-exec.js.map +1 -0
- package/dist/packages/mcp-common/src/sandbox.d.ts +31 -0
- package/dist/packages/mcp-common/src/sandbox.js +96 -0
- package/dist/packages/mcp-common/src/sandbox.js.map +1 -0
- package/dist/packages/profiler-mcp/src/index.d.ts +5 -0
- package/dist/packages/profiler-mcp/src/index.js +148 -0
- package/dist/packages/profiler-mcp/src/index.js.map +1 -0
- package/dist/packages/profiler-mcp/src/trace-analyzer.d.ts +38 -0
- package/dist/packages/profiler-mcp/src/trace-analyzer.js +95 -0
- package/dist/packages/profiler-mcp/src/trace-analyzer.js.map +1 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.js +61 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/server/handlers.d.ts +11 -0
- package/dist/src/server/handlers.js +33 -0
- package/dist/src/server/handlers.js.map +1 -0
- package/dist/src/server/tools.d.ts +23 -0
- package/dist/src/server/tools.js +57 -0
- package/dist/src/server/tools.js.map +1 -0
- package/package.json +54 -0
- 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,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,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
|