spect8-mcp 0.1.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 +69 -0
- package/dist/auth.d.ts +7 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +60 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/collectors/claude_jsonl.d.ts +28 -0
- package/dist/collectors/claude_jsonl.d.ts.map +1 -0
- package/dist/collectors/claude_jsonl.js +130 -0
- package/dist/collectors/claude_jsonl.js.map +1 -0
- package/dist/collectors/cursor_sqlite.d.ts +37 -0
- package/dist/collectors/cursor_sqlite.d.ts.map +1 -0
- package/dist/collectors/cursor_sqlite.js +164 -0
- package/dist/collectors/cursor_sqlite.js.map +1 -0
- package/dist/collectors/fs_metrics.d.ts +7 -0
- package/dist/collectors/fs_metrics.d.ts.map +1 -0
- package/dist/collectors/fs_metrics.js +36 -0
- package/dist/collectors/fs_metrics.js.map +1 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +108 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/claude_post_tool.d.ts +11 -0
- package/dist/hooks/claude_post_tool.d.ts.map +1 -0
- package/dist/hooks/claude_post_tool.js +77 -0
- package/dist/hooks/claude_post_tool.js.map +1 -0
- package/dist/hooks/claude_session_start.d.ts +9 -0
- package/dist/hooks/claude_session_start.d.ts.map +1 -0
- package/dist/hooks/claude_session_start.js +18 -0
- package/dist/hooks/claude_session_start.js.map +1 -0
- package/dist/hooks/claude_stop.d.ts +10 -0
- package/dist/hooks/claude_stop.d.ts.map +1 -0
- package/dist/hooks/claude_stop.js +51 -0
- package/dist/hooks/claude_stop.js.map +1 -0
- package/dist/hooks/cli.d.ts +8 -0
- package/dist/hooks/cli.d.ts.map +1 -0
- package/dist/hooks/cli.js +26 -0
- package/dist/hooks/cli.js.map +1 -0
- package/dist/hooks/shared.d.ts +27 -0
- package/dist/hooks/shared.d.ts.map +1 -0
- package/dist/hooks/shared.js +132 -0
- package/dist/hooks/shared.js.map +1 -0
- package/dist/interceptors/context.d.ts +11 -0
- package/dist/interceptors/context.d.ts.map +1 -0
- package/dist/interceptors/context.js +13 -0
- package/dist/interceptors/context.js.map +1 -0
- package/dist/interceptors/file_ops.d.ts +5 -0
- package/dist/interceptors/file_ops.d.ts.map +1 -0
- package/dist/interceptors/file_ops.js +21 -0
- package/dist/interceptors/file_ops.js.map +1 -0
- package/dist/interceptors/git_diff.d.ts +4 -0
- package/dist/interceptors/git_diff.d.ts.map +1 -0
- package/dist/interceptors/git_diff.js +37 -0
- package/dist/interceptors/git_diff.js.map +1 -0
- package/dist/interceptors/run_command.d.ts +9 -0
- package/dist/interceptors/run_command.d.ts.map +1 -0
- package/dist/interceptors/run_command.js +10 -0
- package/dist/interceptors/run_command.js.map +1 -0
- package/dist/interceptors/search_files.d.ts +4 -0
- package/dist/interceptors/search_files.d.ts.map +1 -0
- package/dist/interceptors/search_files.js +9 -0
- package/dist/interceptors/search_files.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +40 -0
- package/dist/logger.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +218 -0
- package/dist/server.js.map +1 -0
- package/dist/session/developer_id.d.ts +6 -0
- package/dist/session/developer_id.d.ts.map +1 -0
- package/dist/session/developer_id.js +25 -0
- package/dist/session/developer_id.js.map +1 -0
- package/dist/session/session_id.d.ts +8 -0
- package/dist/session/session_id.d.ts.map +1 -0
- package/dist/session/session_id.js +32 -0
- package/dist/session/session_id.js.map +1 -0
- package/dist/tools/end_task.d.ts +14 -0
- package/dist/tools/end_task.d.ts.map +1 -0
- package/dist/tools/end_task.js +25 -0
- package/dist/tools/end_task.js.map +1 -0
- package/dist/tools/get_score.d.ts +29 -0
- package/dist/tools/get_score.d.ts.map +1 -0
- package/dist/tools/get_score.js +54 -0
- package/dist/tools/get_score.js.map +1 -0
- package/dist/tools/report_activity.d.ts +59 -0
- package/dist/tools/report_activity.d.ts.map +1 -0
- package/dist/tools/report_activity.js +44 -0
- package/dist/tools/report_activity.js.map +1 -0
- package/dist/tools/start_task.d.ts +28 -0
- package/dist/tools/start_task.d.ts.map +1 -0
- package/dist/tools/start_task.js +34 -0
- package/dist/tools/start_task.js.map +1 -0
- package/dist/transport/batcher.d.ts +40 -0
- package/dist/transport/batcher.d.ts.map +1 -0
- package/dist/transport/batcher.js +115 -0
- package/dist/transport/batcher.js.map +1 -0
- package/dist/transport/ingest_client.d.ts +20 -0
- package/dist/transport/ingest_client.d.ts.map +1 -0
- package/dist/transport/ingest_client.js +78 -0
- package/dist/transport/ingest_client.js.map +1 -0
- package/dist/transport/offline_queue.d.ts +30 -0
- package/dist/transport/offline_queue.d.ts.map +1 -0
- package/dist/transport/offline_queue.js +83 -0
- package/dist/transport/offline_queue.js.map +1 -0
- package/dist/transport/sign.d.ts +7 -0
- package/dist/transport/sign.d.ts.map +1 -0
- package/dist/transport/sign.js +14 -0
- package/dist/transport/sign.js.map +1 -0
- package/dist/types.d.ts +123 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +53 -0
- package/dist/types.js.map +1 -0
- package/examples/claude/hooks.json +37 -0
- package/examples/cursor/mcp.json +14 -0
- package/package.json +53 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { request } from "undici";
|
|
2
|
+
import { signRequest } from "./sign.js";
|
|
3
|
+
export class IngestClient {
|
|
4
|
+
baseUrl;
|
|
5
|
+
hmacKey;
|
|
6
|
+
developerId;
|
|
7
|
+
logger;
|
|
8
|
+
timeoutMs;
|
|
9
|
+
constructor(baseUrl, hmacKey, developerId, logger, timeoutMs = 10_000) {
|
|
10
|
+
this.baseUrl = baseUrl;
|
|
11
|
+
this.hmacKey = hmacKey;
|
|
12
|
+
this.developerId = developerId;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
this.timeoutMs = timeoutMs;
|
|
15
|
+
}
|
|
16
|
+
async postEvents(events) {
|
|
17
|
+
return this.post("/api/v1/events", events);
|
|
18
|
+
}
|
|
19
|
+
async postTokenEvents(events) {
|
|
20
|
+
return this.post("/api/v1/token_events", events);
|
|
21
|
+
}
|
|
22
|
+
async get(path) {
|
|
23
|
+
const url = `${this.baseUrl}${path}`;
|
|
24
|
+
try {
|
|
25
|
+
const { statusCode, body } = await request(url, {
|
|
26
|
+
method: "GET",
|
|
27
|
+
headers: {
|
|
28
|
+
"x-spect8-dev-id": this.developerId,
|
|
29
|
+
},
|
|
30
|
+
headersTimeout: this.timeoutMs,
|
|
31
|
+
bodyTimeout: this.timeoutMs,
|
|
32
|
+
});
|
|
33
|
+
const text = await body.text();
|
|
34
|
+
return {
|
|
35
|
+
ok: statusCode >= 200 && statusCode < 300,
|
|
36
|
+
status: statusCode,
|
|
37
|
+
body: text,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
this.logger.warn(`GET ${path} failed`, err.message);
|
|
42
|
+
return { ok: false, status: 0, body: err.message };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async post(path, payload) {
|
|
46
|
+
const url = `${this.baseUrl}${path}`;
|
|
47
|
+
const body = JSON.stringify(payload);
|
|
48
|
+
const ts = Date.now();
|
|
49
|
+
const signature = signRequest(this.hmacKey, ts, body);
|
|
50
|
+
try {
|
|
51
|
+
const { statusCode, body: resBody } = await request(url, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"content-type": "application/json",
|
|
55
|
+
"x-spect8-timestamp": String(ts),
|
|
56
|
+
"x-spect8-signature": signature,
|
|
57
|
+
"x-spect8-dev-id": this.developerId,
|
|
58
|
+
},
|
|
59
|
+
body,
|
|
60
|
+
headersTimeout: this.timeoutMs,
|
|
61
|
+
bodyTimeout: this.timeoutMs,
|
|
62
|
+
});
|
|
63
|
+
const text = await resBody.text();
|
|
64
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
65
|
+
this.logger.debug(`POST ${path} succeeded`);
|
|
66
|
+
return { ok: true, status: statusCode, body: text };
|
|
67
|
+
}
|
|
68
|
+
this.logger.warn(`POST ${path} returned ${statusCode}`, text.slice(0, 200));
|
|
69
|
+
return { ok: false, status: statusCode, body: text };
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
this.logger.debug(`POST ${path} exception: ${err.message}`);
|
|
73
|
+
this.logger.warn(`POST ${path} failed`, err.message);
|
|
74
|
+
return { ok: false, status: 0, body: err.message };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=ingest_client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest_client.js","sourceRoot":"","sources":["../../src/transport/ingest_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Logger } from "../logger.js";
|
|
2
|
+
export type QueueKind = "event" | "token_event";
|
|
3
|
+
export interface QueuedRow {
|
|
4
|
+
id: number;
|
|
5
|
+
kind: QueueKind;
|
|
6
|
+
payload: string;
|
|
7
|
+
created_at_ms: number;
|
|
8
|
+
attempts: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Durable, append-only FIFO backed by SQLite. Used when the ingest endpoint is
|
|
12
|
+
* unreachable so no captured event is lost across crashes or network outages.
|
|
13
|
+
*
|
|
14
|
+
* Rows are keyed by auto-incremented id to preserve ordering; `dequeue()`
|
|
15
|
+
* returns the oldest unsent rows and callers call `ack()` after a successful
|
|
16
|
+
* POST. On retry storms we also track `attempts` so pathological rows can be
|
|
17
|
+
* drained manually by an operator.
|
|
18
|
+
*/
|
|
19
|
+
export declare class OfflineQueue {
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private readonly db;
|
|
22
|
+
constructor(dbPath: string, logger: Logger);
|
|
23
|
+
enqueueMany(kind: QueueKind, payloads: string[]): void;
|
|
24
|
+
dequeue(kind: QueueKind, limit: number): QueuedRow[];
|
|
25
|
+
ack(ids: number[]): void;
|
|
26
|
+
bump(ids: number[]): void;
|
|
27
|
+
size(): number;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=offline_queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline_queue.d.ts","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAGK,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAmB3D,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAgBtD,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE;IASpD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAQxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAUzB,IAAI,IAAI,MAAM;IAOd,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { mkdirSync, existsSync } from "node:fs";
|
|
4
|
+
/**
|
|
5
|
+
* Durable, append-only FIFO backed by SQLite. Used when the ingest endpoint is
|
|
6
|
+
* unreachable so no captured event is lost across crashes or network outages.
|
|
7
|
+
*
|
|
8
|
+
* Rows are keyed by auto-incremented id to preserve ordering; `dequeue()`
|
|
9
|
+
* returns the oldest unsent rows and callers call `ack()` after a successful
|
|
10
|
+
* POST. On retry storms we also track `attempts` so pathological rows can be
|
|
11
|
+
* drained manually by an operator.
|
|
12
|
+
*/
|
|
13
|
+
export class OfflineQueue {
|
|
14
|
+
logger;
|
|
15
|
+
db;
|
|
16
|
+
constructor(dbPath, logger) {
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
if (!existsSync(dirname(dbPath))) {
|
|
19
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
this.db = new Database(dbPath);
|
|
22
|
+
this.db.pragma("journal_mode = WAL");
|
|
23
|
+
this.db.pragma("synchronous = NORMAL");
|
|
24
|
+
this.db.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS spect8_queue (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
kind TEXT NOT NULL,
|
|
28
|
+
payload TEXT NOT NULL,
|
|
29
|
+
created_at_ms INTEGER NOT NULL,
|
|
30
|
+
attempts INTEGER NOT NULL DEFAULT 0
|
|
31
|
+
);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS ix_spect8_queue_kind ON spect8_queue(kind, id);
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
enqueueMany(kind, payloads) {
|
|
36
|
+
if (payloads.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
const stmt = this.db.prepare("INSERT INTO spect8_queue(kind, payload, created_at_ms) VALUES (?, ?, ?)");
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const tx = this.db.transaction((rows) => {
|
|
41
|
+
for (const p of rows)
|
|
42
|
+
stmt.run(kind, p, now);
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
tx(payloads);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
this.logger.error("offline queue enqueue failed", err.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
dequeue(kind, limit) {
|
|
52
|
+
const rows = this.db
|
|
53
|
+
.prepare("SELECT id, kind, payload, created_at_ms, attempts FROM spect8_queue WHERE kind = ? ORDER BY id LIMIT ?")
|
|
54
|
+
.all(kind, limit);
|
|
55
|
+
return rows;
|
|
56
|
+
}
|
|
57
|
+
ack(ids) {
|
|
58
|
+
if (ids.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
61
|
+
this.db
|
|
62
|
+
.prepare(`DELETE FROM spect8_queue WHERE id IN (${placeholders})`)
|
|
63
|
+
.run(...ids);
|
|
64
|
+
}
|
|
65
|
+
bump(ids) {
|
|
66
|
+
if (ids.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
69
|
+
this.db
|
|
70
|
+
.prepare(`UPDATE spect8_queue SET attempts = attempts + 1 WHERE id IN (${placeholders})`)
|
|
71
|
+
.run(...ids);
|
|
72
|
+
}
|
|
73
|
+
size() {
|
|
74
|
+
const row = this.db
|
|
75
|
+
.prepare("SELECT COUNT(*) AS n FROM spect8_queue")
|
|
76
|
+
.get();
|
|
77
|
+
return row.n;
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
this.db.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=offline_queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline_queue.js","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the `X-Spect8-Signature` header value for a signed request. The
|
|
3
|
+
* backend verifies the signature as HMAC_SHA256(key, `${ts}.${body}`) so both
|
|
4
|
+
* ts and body must be identical byte-for-byte on both sides.
|
|
5
|
+
*/
|
|
6
|
+
export declare function signRequest(key: string, timestampMs: number, body: string | Uint8Array): string;
|
|
7
|
+
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../src/transport/sign.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,UAAU,GACxB,MAAM,CAMR"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the `X-Spect8-Signature` header value for a signed request. The
|
|
4
|
+
* backend verifies the signature as HMAC_SHA256(key, `${ts}.${body}`) so both
|
|
5
|
+
* ts and body must be identical byte-for-byte on both sides.
|
|
6
|
+
*/
|
|
7
|
+
export function signRequest(key, timestampMs, body) {
|
|
8
|
+
const mac = createHmac("sha256", key);
|
|
9
|
+
mac.update(String(timestampMs));
|
|
10
|
+
mac.update(".");
|
|
11
|
+
mac.update(body);
|
|
12
|
+
return "sha256=" + mac.digest("hex");
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=sign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../../src/transport/sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,WAAmB,EACnB,IAAyB;IAEzB,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACtC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAChC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const TOOL_NAMES: readonly ["read_file", "write_file", "run_command", "git_diff", "search_files", "prompt_start", "start_task", "end_task", "get_score"];
|
|
3
|
+
export type ToolName = (typeof TOOL_NAMES)[number];
|
|
4
|
+
export type IdeName = string;
|
|
5
|
+
export declare const ToolEventSchema: z.ZodObject<{
|
|
6
|
+
event_id: z.ZodString;
|
|
7
|
+
session_id: z.ZodString;
|
|
8
|
+
task_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
developer_id: z.ZodString;
|
|
10
|
+
tool_name: z.ZodEnum<["read_file", "write_file", "run_command", "git_diff", "search_files", "prompt_start", "start_task", "end_task", "get_score"]>;
|
|
11
|
+
timestamp_ms: z.ZodNumber;
|
|
12
|
+
file_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
13
|
+
file_size_bytes: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
14
|
+
lines_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
15
|
+
command: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
|
+
exit_code: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
17
|
+
command_duration_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
18
|
+
search_query: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
|
+
search_results_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
20
|
+
diff_lines_added: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
21
|
+
diff_lines_removed: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
22
|
+
diff_files_changed: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
23
|
+
task_label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
|
+
latency_since_prev_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
25
|
+
input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
26
|
+
output_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
27
|
+
cache_read_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
28
|
+
cache_creation_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
29
|
+
ide_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
model_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
request_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
}, "strip", z.ZodTypeAny, {
|
|
33
|
+
developer_id: string;
|
|
34
|
+
event_id: string;
|
|
35
|
+
session_id: string;
|
|
36
|
+
tool_name: "read_file" | "write_file" | "run_command" | "git_diff" | "search_files" | "prompt_start" | "start_task" | "end_task" | "get_score";
|
|
37
|
+
timestamp_ms: number;
|
|
38
|
+
ide_name?: string | null | undefined;
|
|
39
|
+
task_id?: string | null | undefined;
|
|
40
|
+
file_path?: string | null | undefined;
|
|
41
|
+
file_size_bytes?: number | null | undefined;
|
|
42
|
+
lines_count?: number | null | undefined;
|
|
43
|
+
command?: string | null | undefined;
|
|
44
|
+
exit_code?: number | null | undefined;
|
|
45
|
+
command_duration_ms?: number | null | undefined;
|
|
46
|
+
search_query?: string | null | undefined;
|
|
47
|
+
search_results_count?: number | null | undefined;
|
|
48
|
+
diff_lines_added?: number | null | undefined;
|
|
49
|
+
diff_lines_removed?: number | null | undefined;
|
|
50
|
+
diff_files_changed?: number | null | undefined;
|
|
51
|
+
task_label?: string | null | undefined;
|
|
52
|
+
latency_since_prev_ms?: number | null | undefined;
|
|
53
|
+
input_tokens?: number | null | undefined;
|
|
54
|
+
output_tokens?: number | null | undefined;
|
|
55
|
+
cache_read_input_tokens?: number | null | undefined;
|
|
56
|
+
cache_creation_input_tokens?: number | null | undefined;
|
|
57
|
+
model_name?: string | null | undefined;
|
|
58
|
+
request_id?: string | null | undefined;
|
|
59
|
+
}, {
|
|
60
|
+
developer_id: string;
|
|
61
|
+
event_id: string;
|
|
62
|
+
session_id: string;
|
|
63
|
+
tool_name: "read_file" | "write_file" | "run_command" | "git_diff" | "search_files" | "prompt_start" | "start_task" | "end_task" | "get_score";
|
|
64
|
+
timestamp_ms: number;
|
|
65
|
+
ide_name?: string | null | undefined;
|
|
66
|
+
task_id?: string | null | undefined;
|
|
67
|
+
file_path?: string | null | undefined;
|
|
68
|
+
file_size_bytes?: number | null | undefined;
|
|
69
|
+
lines_count?: number | null | undefined;
|
|
70
|
+
command?: string | null | undefined;
|
|
71
|
+
exit_code?: number | null | undefined;
|
|
72
|
+
command_duration_ms?: number | null | undefined;
|
|
73
|
+
search_query?: string | null | undefined;
|
|
74
|
+
search_results_count?: number | null | undefined;
|
|
75
|
+
diff_lines_added?: number | null | undefined;
|
|
76
|
+
diff_lines_removed?: number | null | undefined;
|
|
77
|
+
diff_files_changed?: number | null | undefined;
|
|
78
|
+
task_label?: string | null | undefined;
|
|
79
|
+
latency_since_prev_ms?: number | null | undefined;
|
|
80
|
+
input_tokens?: number | null | undefined;
|
|
81
|
+
output_tokens?: number | null | undefined;
|
|
82
|
+
cache_read_input_tokens?: number | null | undefined;
|
|
83
|
+
cache_creation_input_tokens?: number | null | undefined;
|
|
84
|
+
model_name?: string | null | undefined;
|
|
85
|
+
request_id?: string | null | undefined;
|
|
86
|
+
}>;
|
|
87
|
+
export type ToolEvent = z.infer<typeof ToolEventSchema>;
|
|
88
|
+
export declare const TokenEventSchema: z.ZodObject<{
|
|
89
|
+
request_id: z.ZodString;
|
|
90
|
+
session_id: z.ZodString;
|
|
91
|
+
developer_id: z.ZodString;
|
|
92
|
+
timestamp_ms: z.ZodNumber;
|
|
93
|
+
ide_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
94
|
+
model_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
95
|
+
input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
96
|
+
output_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
97
|
+
cache_read_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
98
|
+
cache_creation_input_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
99
|
+
}, "strip", z.ZodTypeAny, {
|
|
100
|
+
developer_id: string;
|
|
101
|
+
session_id: string;
|
|
102
|
+
timestamp_ms: number;
|
|
103
|
+
request_id: string;
|
|
104
|
+
ide_name?: string | null | undefined;
|
|
105
|
+
input_tokens?: number | null | undefined;
|
|
106
|
+
output_tokens?: number | null | undefined;
|
|
107
|
+
cache_read_input_tokens?: number | null | undefined;
|
|
108
|
+
cache_creation_input_tokens?: number | null | undefined;
|
|
109
|
+
model_name?: string | null | undefined;
|
|
110
|
+
}, {
|
|
111
|
+
developer_id: string;
|
|
112
|
+
session_id: string;
|
|
113
|
+
timestamp_ms: number;
|
|
114
|
+
request_id: string;
|
|
115
|
+
ide_name?: string | null | undefined;
|
|
116
|
+
input_tokens?: number | null | undefined;
|
|
117
|
+
output_tokens?: number | null | undefined;
|
|
118
|
+
cache_read_input_tokens?: number | null | undefined;
|
|
119
|
+
cache_creation_input_tokens?: number | null | undefined;
|
|
120
|
+
model_name?: string | null | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
export type TokenEvent = z.infer<typeof TokenEventSchema>;
|
|
123
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,UAAU,wIAUb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const TOOL_NAMES = [
|
|
3
|
+
"read_file",
|
|
4
|
+
"write_file",
|
|
5
|
+
"run_command",
|
|
6
|
+
"git_diff",
|
|
7
|
+
"search_files",
|
|
8
|
+
"prompt_start",
|
|
9
|
+
"start_task",
|
|
10
|
+
"end_task",
|
|
11
|
+
"get_score",
|
|
12
|
+
];
|
|
13
|
+
export const ToolEventSchema = z.object({
|
|
14
|
+
event_id: z.string(),
|
|
15
|
+
session_id: z.string(),
|
|
16
|
+
task_id: z.string().nullable().optional(),
|
|
17
|
+
developer_id: z.string(),
|
|
18
|
+
tool_name: z.enum(TOOL_NAMES),
|
|
19
|
+
timestamp_ms: z.number().int(),
|
|
20
|
+
file_path: z.string().nullable().optional(),
|
|
21
|
+
file_size_bytes: z.number().int().nullable().optional(),
|
|
22
|
+
lines_count: z.number().int().nullable().optional(),
|
|
23
|
+
command: z.string().nullable().optional(),
|
|
24
|
+
exit_code: z.number().int().nullable().optional(),
|
|
25
|
+
command_duration_ms: z.number().int().nullable().optional(),
|
|
26
|
+
search_query: z.string().nullable().optional(),
|
|
27
|
+
search_results_count: z.number().int().nullable().optional(),
|
|
28
|
+
diff_lines_added: z.number().int().nullable().optional(),
|
|
29
|
+
diff_lines_removed: z.number().int().nullable().optional(),
|
|
30
|
+
diff_files_changed: z.number().int().nullable().optional(),
|
|
31
|
+
task_label: z.string().nullable().optional(),
|
|
32
|
+
latency_since_prev_ms: z.number().int().nullable().optional(),
|
|
33
|
+
input_tokens: z.number().int().nullable().optional(),
|
|
34
|
+
output_tokens: z.number().int().nullable().optional(),
|
|
35
|
+
cache_read_input_tokens: z.number().int().nullable().optional(),
|
|
36
|
+
cache_creation_input_tokens: z.number().int().nullable().optional(),
|
|
37
|
+
ide_name: z.string().nullable().optional(),
|
|
38
|
+
model_name: z.string().nullable().optional(),
|
|
39
|
+
request_id: z.string().nullable().optional(),
|
|
40
|
+
});
|
|
41
|
+
export const TokenEventSchema = z.object({
|
|
42
|
+
request_id: z.string(),
|
|
43
|
+
session_id: z.string(),
|
|
44
|
+
developer_id: z.string(),
|
|
45
|
+
timestamp_ms: z.number().int(),
|
|
46
|
+
ide_name: z.string().nullable().optional(),
|
|
47
|
+
model_name: z.string().nullable().optional(),
|
|
48
|
+
input_tokens: z.number().int().nullable().optional(),
|
|
49
|
+
output_tokens: z.number().int().nullable().optional(),
|
|
50
|
+
cache_read_input_tokens: z.number().int().nullable().optional(),
|
|
51
|
+
cache_creation_input_tokens: z.number().int().nullable().optional(),
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "SPECT8_INGEST_URL=${SPECT8_INGEST_URL:-https://spect8-api.herokuapp.com} SPECT8_IDE_NAME=claude_code npx -y --package @spect8/mcp -- spect8-mcp-claude-hook session-start"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "*",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "SPECT8_INGEST_URL=${SPECT8_INGEST_URL:-https://spect8-api.herokuapp.com} SPECT8_IDE_NAME=claude_code npx -y --package @spect8/mcp -- spect8-mcp-claude-hook post-tool"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"Stop": [
|
|
26
|
+
{
|
|
27
|
+
"matcher": "*",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "SPECT8_INGEST_URL=${SPECT8_INGEST_URL:-https://spect8-api.herokuapp.com} SPECT8_IDE_NAME=claude_code npx -y --package @spect8/mcp -- spect8-mcp-claude-hook stop"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"spect8": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["-y", "@spect8/mcp"],
|
|
6
|
+
"env": {
|
|
7
|
+
"SPECT8_INGEST_URL": "https://spect8-api.herokuapp.com",
|
|
8
|
+
"SPECT8_INGEST_HMAC_KEY": "replace-with-shared-secret",
|
|
9
|
+
"SPECT8_DEVELOPER_ID": "replace-with-your-id",
|
|
10
|
+
"SPECT8_IDE_NAME": "cursor"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spect8-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spect8 local MCP server: captures tool events + token usage from Cursor and Claude Code, posts HMAC-signed batches to the Spect8 ingest API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"spect8": "dist/cli.js",
|
|
9
|
+
"spect8-mcp": "dist/server.js",
|
|
10
|
+
"spect8-mcp-claude-hook": "dist/hooks/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"examples",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"dev": "tsc -w -p tsconfig.json",
|
|
20
|
+
"start": "node dist/server.js",
|
|
21
|
+
"lint": "eslint src --ext .ts",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
+
"better-sqlite3": "^11.5.0",
|
|
29
|
+
"chokidar": "^3.6.0",
|
|
30
|
+
"undici": "^6.20.0",
|
|
31
|
+
"zod": "^3.23.8"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
37
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
38
|
+
"eslint": "^8.57.0",
|
|
39
|
+
"typescript": "^5.5.4",
|
|
40
|
+
"vitest": "^1.6.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"license": "UNLICENSED",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/your-org/spect8"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
}
|
|
53
|
+
}
|