ucu-mcp 0.2.0 → 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.
- package/CHANGELOG.md +31 -53
- package/README.md +90 -4
- package/dist/src/mcp/server.js +11 -6
- package/dist/src/mcp/tools.d.ts +6 -1
- package/dist/src/mcp/tools.js +219 -46
- package/dist/src/platform/base.d.ts +26 -1
- package/dist/src/platform/linux.d.ts +4 -2
- package/dist/src/platform/linux.js +51 -0
- package/dist/src/platform/macos.d.ts +6 -2
- package/dist/src/platform/macos.js +160 -16
- package/dist/src/platform/windows.d.ts +4 -2
- package/dist/src/platform/windows.js +33 -0
- package/dist/src/safety/guard.d.ts +8 -1
- package/dist/src/safety/guard.js +43 -4
- package/dist/src/util/errors.d.ts +26 -1
- package/dist/src/util/errors.js +43 -11
- package/dist/src/util/metrics.d.ts +37 -0
- package/dist/src/util/metrics.js +97 -0
- package/native/cgevent/cgevent-helper +0 -0
- package/native/ocr/ocr-helper +0 -0
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-call latency ring buffer for performance observability.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the last 1000 durationMs samples per tool name and exposes
|
|
5
|
+
* p50/p95/max/mean stats through the `doctor` tool.
|
|
6
|
+
*
|
|
7
|
+
* Singleton instance: import `metrics` and call `record()` on every
|
|
8
|
+
* completed tool call. Tests that need isolation can construct their own
|
|
9
|
+
* Metrics instance.
|
|
10
|
+
*/
|
|
11
|
+
export interface MetricStats {
|
|
12
|
+
count: number;
|
|
13
|
+
p50: number;
|
|
14
|
+
p95: number;
|
|
15
|
+
max: number;
|
|
16
|
+
mean: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class Metrics {
|
|
19
|
+
private buffers;
|
|
20
|
+
private order;
|
|
21
|
+
private writeIndex;
|
|
22
|
+
private totalWrites;
|
|
23
|
+
/** Record a durationMs sample for the named tool. */
|
|
24
|
+
record(toolName: string, durationMs: number): void;
|
|
25
|
+
/** Get stats for one tool, or aggregate across all tools. */
|
|
26
|
+
stats(toolName?: string): MetricStats;
|
|
27
|
+
/** Stats for every tracked tool. */
|
|
28
|
+
byTool(): Record<string, MetricStats>;
|
|
29
|
+
/** Clear all recorded samples. Mostly for tests. */
|
|
30
|
+
reset(): void;
|
|
31
|
+
private liveSamples;
|
|
32
|
+
private computeStats;
|
|
33
|
+
/** Nearest-rank percentile on a pre-sorted ascending array. */
|
|
34
|
+
private percentile;
|
|
35
|
+
}
|
|
36
|
+
/** Singleton shared across the process. */
|
|
37
|
+
export declare const metrics: Metrics;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-call latency ring buffer for performance observability.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the last 1000 durationMs samples per tool name and exposes
|
|
5
|
+
* p50/p95/max/mean stats through the `doctor` tool.
|
|
6
|
+
*
|
|
7
|
+
* Singleton instance: import `metrics` and call `record()` on every
|
|
8
|
+
* completed tool call. Tests that need isolation can construct their own
|
|
9
|
+
* Metrics instance.
|
|
10
|
+
*/
|
|
11
|
+
const RING_SIZE = 1000;
|
|
12
|
+
export class Metrics {
|
|
13
|
+
buffers = new Map();
|
|
14
|
+
order = [];
|
|
15
|
+
writeIndex = new Map();
|
|
16
|
+
totalWrites = new Map();
|
|
17
|
+
/** Record a durationMs sample for the named tool. */
|
|
18
|
+
record(toolName, durationMs) {
|
|
19
|
+
let buf = this.buffers.get(toolName);
|
|
20
|
+
if (!buf) {
|
|
21
|
+
buf = new Array(RING_SIZE).fill(0);
|
|
22
|
+
this.buffers.set(toolName, buf);
|
|
23
|
+
this.order.push(toolName);
|
|
24
|
+
this.writeIndex.set(toolName, 0);
|
|
25
|
+
this.totalWrites.set(toolName, 0);
|
|
26
|
+
}
|
|
27
|
+
const idx = this.writeIndex.get(toolName);
|
|
28
|
+
buf[idx] = durationMs;
|
|
29
|
+
this.writeIndex.set(toolName, (idx + 1) % RING_SIZE);
|
|
30
|
+
this.totalWrites.set(toolName, (this.totalWrites.get(toolName) ?? 0) + 1);
|
|
31
|
+
}
|
|
32
|
+
/** Get stats for one tool, or aggregate across all tools. */
|
|
33
|
+
stats(toolName) {
|
|
34
|
+
if (toolName !== undefined) {
|
|
35
|
+
return this.computeStats(this.liveSamples(toolName));
|
|
36
|
+
}
|
|
37
|
+
const all = [];
|
|
38
|
+
for (const name of this.order) {
|
|
39
|
+
all.push(...this.liveSamples(name));
|
|
40
|
+
}
|
|
41
|
+
return this.computeStats(all);
|
|
42
|
+
}
|
|
43
|
+
/** Stats for every tracked tool. */
|
|
44
|
+
byTool() {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const name of this.order) {
|
|
47
|
+
const samples = this.liveSamples(name);
|
|
48
|
+
if (samples.length > 0) {
|
|
49
|
+
out[name] = this.computeStats(samples);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
/** Clear all recorded samples. Mostly for tests. */
|
|
55
|
+
reset() {
|
|
56
|
+
this.buffers.clear();
|
|
57
|
+
this.order.length = 0;
|
|
58
|
+
this.writeIndex.clear();
|
|
59
|
+
this.totalWrites.clear();
|
|
60
|
+
}
|
|
61
|
+
liveSamples(toolName) {
|
|
62
|
+
const buf = this.buffers.get(toolName);
|
|
63
|
+
if (!buf)
|
|
64
|
+
return [];
|
|
65
|
+
const total = this.totalWrites.get(toolName) ?? 0;
|
|
66
|
+
if (total < RING_SIZE) {
|
|
67
|
+
return buf.slice(0, total);
|
|
68
|
+
}
|
|
69
|
+
// Ring is full — all RING_SIZE slots contain the most recent samples.
|
|
70
|
+
return buf.slice();
|
|
71
|
+
}
|
|
72
|
+
computeStats(samples) {
|
|
73
|
+
if (samples.length === 0) {
|
|
74
|
+
return { count: 0, p50: 0, p95: 0, max: 0, mean: 0 };
|
|
75
|
+
}
|
|
76
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
77
|
+
const sum = sorted.reduce((acc, v) => acc + v, 0);
|
|
78
|
+
const round = (n) => Math.round(n * 10) / 10;
|
|
79
|
+
return {
|
|
80
|
+
count: sorted.length,
|
|
81
|
+
p50: round(this.percentile(sorted, 0.5)),
|
|
82
|
+
p95: round(this.percentile(sorted, 0.95)),
|
|
83
|
+
max: round(sorted[sorted.length - 1]),
|
|
84
|
+
mean: round(sum / sorted.length),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Nearest-rank percentile on a pre-sorted ascending array. */
|
|
88
|
+
percentile(sorted, p) {
|
|
89
|
+
if (sorted.length === 1)
|
|
90
|
+
return sorted[0];
|
|
91
|
+
const rank = Math.ceil(p * sorted.length);
|
|
92
|
+
const idx = Math.max(0, Math.min(rank - 1, sorted.length - 1));
|
|
93
|
+
return sorted[idx];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Singleton shared across the process. */
|
|
97
|
+
export const metrics = new Metrics();
|
|
Binary file
|
package/native/ocr/ocr-helper
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucu-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP server for Universal Computer Use — desktop automation for AI agents via Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
],
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/
|
|
41
|
+
"url": "git+https://github.com/kaguyaluna2333/ucu-mcp-backup.git"
|
|
42
42
|
},
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"dependencies": {
|