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.
Files changed (120) hide show
  1. package/README.md +69 -0
  2. package/dist/auth.d.ts +7 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +60 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +103 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/collectors/claude_jsonl.d.ts +28 -0
  11. package/dist/collectors/claude_jsonl.d.ts.map +1 -0
  12. package/dist/collectors/claude_jsonl.js +130 -0
  13. package/dist/collectors/claude_jsonl.js.map +1 -0
  14. package/dist/collectors/cursor_sqlite.d.ts +37 -0
  15. package/dist/collectors/cursor_sqlite.d.ts.map +1 -0
  16. package/dist/collectors/cursor_sqlite.js +164 -0
  17. package/dist/collectors/cursor_sqlite.js.map +1 -0
  18. package/dist/collectors/fs_metrics.d.ts +7 -0
  19. package/dist/collectors/fs_metrics.d.ts.map +1 -0
  20. package/dist/collectors/fs_metrics.js +36 -0
  21. package/dist/collectors/fs_metrics.js.map +1 -0
  22. package/dist/config.d.ts +63 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +108 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/hooks/claude_post_tool.d.ts +11 -0
  27. package/dist/hooks/claude_post_tool.d.ts.map +1 -0
  28. package/dist/hooks/claude_post_tool.js +77 -0
  29. package/dist/hooks/claude_post_tool.js.map +1 -0
  30. package/dist/hooks/claude_session_start.d.ts +9 -0
  31. package/dist/hooks/claude_session_start.d.ts.map +1 -0
  32. package/dist/hooks/claude_session_start.js +18 -0
  33. package/dist/hooks/claude_session_start.js.map +1 -0
  34. package/dist/hooks/claude_stop.d.ts +10 -0
  35. package/dist/hooks/claude_stop.d.ts.map +1 -0
  36. package/dist/hooks/claude_stop.js +51 -0
  37. package/dist/hooks/claude_stop.js.map +1 -0
  38. package/dist/hooks/cli.d.ts +8 -0
  39. package/dist/hooks/cli.d.ts.map +1 -0
  40. package/dist/hooks/cli.js +26 -0
  41. package/dist/hooks/cli.js.map +1 -0
  42. package/dist/hooks/shared.d.ts +27 -0
  43. package/dist/hooks/shared.d.ts.map +1 -0
  44. package/dist/hooks/shared.js +132 -0
  45. package/dist/hooks/shared.js.map +1 -0
  46. package/dist/interceptors/context.d.ts +11 -0
  47. package/dist/interceptors/context.d.ts.map +1 -0
  48. package/dist/interceptors/context.js +13 -0
  49. package/dist/interceptors/context.js.map +1 -0
  50. package/dist/interceptors/file_ops.d.ts +5 -0
  51. package/dist/interceptors/file_ops.d.ts.map +1 -0
  52. package/dist/interceptors/file_ops.js +21 -0
  53. package/dist/interceptors/file_ops.js.map +1 -0
  54. package/dist/interceptors/git_diff.d.ts +4 -0
  55. package/dist/interceptors/git_diff.d.ts.map +1 -0
  56. package/dist/interceptors/git_diff.js +37 -0
  57. package/dist/interceptors/git_diff.js.map +1 -0
  58. package/dist/interceptors/run_command.d.ts +9 -0
  59. package/dist/interceptors/run_command.d.ts.map +1 -0
  60. package/dist/interceptors/run_command.js +10 -0
  61. package/dist/interceptors/run_command.js.map +1 -0
  62. package/dist/interceptors/search_files.d.ts +4 -0
  63. package/dist/interceptors/search_files.d.ts.map +1 -0
  64. package/dist/interceptors/search_files.js +9 -0
  65. package/dist/interceptors/search_files.js.map +1 -0
  66. package/dist/logger.d.ts +11 -0
  67. package/dist/logger.d.ts.map +1 -0
  68. package/dist/logger.js +40 -0
  69. package/dist/logger.js.map +1 -0
  70. package/dist/server.d.ts +12 -0
  71. package/dist/server.d.ts.map +1 -0
  72. package/dist/server.js +218 -0
  73. package/dist/server.js.map +1 -0
  74. package/dist/session/developer_id.d.ts +6 -0
  75. package/dist/session/developer_id.d.ts.map +1 -0
  76. package/dist/session/developer_id.js +25 -0
  77. package/dist/session/developer_id.js.map +1 -0
  78. package/dist/session/session_id.d.ts +8 -0
  79. package/dist/session/session_id.d.ts.map +1 -0
  80. package/dist/session/session_id.js +32 -0
  81. package/dist/session/session_id.js.map +1 -0
  82. package/dist/tools/end_task.d.ts +14 -0
  83. package/dist/tools/end_task.d.ts.map +1 -0
  84. package/dist/tools/end_task.js +25 -0
  85. package/dist/tools/end_task.js.map +1 -0
  86. package/dist/tools/get_score.d.ts +29 -0
  87. package/dist/tools/get_score.d.ts.map +1 -0
  88. package/dist/tools/get_score.js +54 -0
  89. package/dist/tools/get_score.js.map +1 -0
  90. package/dist/tools/report_activity.d.ts +59 -0
  91. package/dist/tools/report_activity.d.ts.map +1 -0
  92. package/dist/tools/report_activity.js +44 -0
  93. package/dist/tools/report_activity.js.map +1 -0
  94. package/dist/tools/start_task.d.ts +28 -0
  95. package/dist/tools/start_task.d.ts.map +1 -0
  96. package/dist/tools/start_task.js +34 -0
  97. package/dist/tools/start_task.js.map +1 -0
  98. package/dist/transport/batcher.d.ts +40 -0
  99. package/dist/transport/batcher.d.ts.map +1 -0
  100. package/dist/transport/batcher.js +115 -0
  101. package/dist/transport/batcher.js.map +1 -0
  102. package/dist/transport/ingest_client.d.ts +20 -0
  103. package/dist/transport/ingest_client.d.ts.map +1 -0
  104. package/dist/transport/ingest_client.js +78 -0
  105. package/dist/transport/ingest_client.js.map +1 -0
  106. package/dist/transport/offline_queue.d.ts +30 -0
  107. package/dist/transport/offline_queue.d.ts.map +1 -0
  108. package/dist/transport/offline_queue.js +83 -0
  109. package/dist/transport/offline_queue.js.map +1 -0
  110. package/dist/transport/sign.d.ts +7 -0
  111. package/dist/transport/sign.d.ts.map +1 -0
  112. package/dist/transport/sign.js +14 -0
  113. package/dist/transport/sign.js.map +1 -0
  114. package/dist/types.d.ts +123 -0
  115. package/dist/types.d.ts.map +1 -0
  116. package/dist/types.js +53 -0
  117. package/dist/types.js.map +1 -0
  118. package/examples/claude/hooks.json +37 -0
  119. package/examples/cursor/mcp.json +14 -0
  120. 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"}
@@ -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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,UAAU;IACV,cAAc;IACd,cAAc;IACd,YAAY;IACZ,UAAU;IACV,WAAW;CACH,CAAC;AAMX,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACzC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;IAC7B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAE9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC3C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACzC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC3D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5D,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxD,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1D,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAE7D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACrD,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC/D,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACrD,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC/D,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACpE,CAAC,CAAC"}
@@ -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
+ }