@chronova/mcp-server 1.0.1 → 1.0.2

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 (45) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +57 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/lib/chronova-client.d.ts +7 -0
  6. package/dist/lib/chronova-client.d.ts.map +1 -0
  7. package/dist/lib/chronova-client.js +44 -0
  8. package/dist/lib/chronova-client.js.map +1 -0
  9. package/dist/lib/config.d.ts +15 -0
  10. package/dist/lib/config.d.ts.map +1 -0
  11. package/dist/lib/config.js +71 -0
  12. package/dist/lib/config.js.map +1 -0
  13. package/dist/lib/errors.d.ts +9 -0
  14. package/dist/lib/errors.d.ts.map +1 -0
  15. package/dist/lib/errors.js +53 -0
  16. package/dist/lib/errors.js.map +1 -0
  17. package/dist/lib/types.d.ts +113 -0
  18. package/dist/lib/types.d.ts.map +1 -0
  19. package/dist/lib/types.js +2 -0
  20. package/dist/lib/types.js.map +1 -0
  21. package/dist/server.d.ts +6 -0
  22. package/dist/server.d.ts.map +1 -0
  23. package/dist/server.js +88 -0
  24. package/dist/server.js.map +1 -0
  25. package/dist/stdio.d.ts +3 -0
  26. package/dist/stdio.d.ts.map +1 -0
  27. package/dist/stdio.js +30 -0
  28. package/dist/stdio.js.map +1 -0
  29. package/dist/tools/get-ai-insights.d.ts +4 -0
  30. package/dist/tools/get-ai-insights.d.ts.map +1 -0
  31. package/dist/tools/get-ai-insights.js +49 -0
  32. package/dist/tools/get-ai-insights.js.map +1 -0
  33. package/dist/tools/get-developer-context.d.ts +4 -0
  34. package/dist/tools/get-developer-context.d.ts.map +1 -0
  35. package/dist/tools/get-developer-context.js +36 -0
  36. package/dist/tools/get-developer-context.js.map +1 -0
  37. package/dist/tools/get-productivity-summary.d.ts +4 -0
  38. package/dist/tools/get-productivity-summary.d.ts.map +1 -0
  39. package/dist/tools/get-productivity-summary.js +57 -0
  40. package/dist/tools/get-productivity-summary.js.map +1 -0
  41. package/dist/tools/get-recent-activity.d.ts +4 -0
  42. package/dist/tools/get-recent-activity.d.ts.map +1 -0
  43. package/dist/tools/get-recent-activity.js +93 -0
  44. package/dist/tools/get-recent-activity.js.map +1 -0
  45. package/package.json +2 -1
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { startServer } from "./server.js";
3
+ const HELP_TEXT = `
4
+ Usage: @chronova/mcp-server [options]
5
+
6
+ Options:
7
+ --port <number> Port to listen on (default: 3001)
8
+ --api-url <url> Chronova API URL (default: https://chronova.dev/api/v1)
9
+ --help Show this help message
10
+
11
+ Configuration priority:
12
+ 1. Environment variables (CHRONOVA_API_KEY, CHRONOVA_API_URL)
13
+ 2. ~/.chronova.cfg file (api_key, api_url under [settings])
14
+ 3. ~/.wakatime.cfg file (api_key, api_url under [settings])
15
+ 4. Defaults
16
+
17
+ Environment variables:
18
+ CHRONOVA_API_KEY Your Chronova API key (required if no config file)
19
+ CHRONOVA_API_URL Chronova API URL (default: https://chronova.dev/api/v1)
20
+ PORT Server port (default: 3001)
21
+ `;
22
+ function parseArgs() {
23
+ const args = process.argv.slice(2);
24
+ let i = 0;
25
+ while (i < args.length) {
26
+ const arg = args[i];
27
+ if (arg === "--help") {
28
+ console.log(HELP_TEXT.trim());
29
+ process.exit(0);
30
+ }
31
+ if (arg === "--port") {
32
+ const value = args[++i];
33
+ if (!value || isNaN(Number(value))) {
34
+ console.error("Error: --port requires a number");
35
+ process.exit(1);
36
+ }
37
+ process.env.PORT = value;
38
+ }
39
+ else if (arg === "--api-url") {
40
+ const value = args[++i];
41
+ if (!value) {
42
+ console.error("Error: --api-url requires a URL");
43
+ process.exit(1);
44
+ }
45
+ process.env.CHRONOVA_API_URL = value;
46
+ }
47
+ else {
48
+ console.error(`Error: Unknown option '${arg}'`);
49
+ console.log(HELP_TEXT.trim());
50
+ process.exit(1);
51
+ }
52
+ i++;
53
+ }
54
+ }
55
+ parseArgs();
56
+ startServer();
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;CAkBjB,CAAC;AAEF,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC;QAC3B,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,GAAG,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,CAAC,EAAE,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,EAAE,CAAC;AACZ,WAAW,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare class ChronovaClient {
2
+ private baseUrl;
3
+ private apiKey;
4
+ constructor(baseUrl?: string, apiKey?: string);
5
+ get<T>(path: string, params?: Record<string, string>): Promise<T>;
6
+ }
7
+ //# sourceMappingURL=chronova-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronova-client.d.ts","sourceRoot":"","sources":["../../src/lib/chronova-client.ts"],"names":[],"mappings":"AAIA,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAGrB,OAAO,GAAE,MAAsE,EAC/E,MAAM,GAAE,MAA2C;IAQ/C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAkCxE"}
@@ -0,0 +1,44 @@
1
+ import { mapHttpStatusToError, mapNetworkError } from "./errors.js";
2
+ const DEFAULT_TIMEOUT_MS = 30_000;
3
+ export class ChronovaClient {
4
+ baseUrl;
5
+ apiKey;
6
+ constructor(baseUrl = process.env.CHRONOVA_API_URL ?? "https://chronova.dev/api/v1", apiKey = process.env.CHRONOVA_API_KEY ?? "") {
7
+ // Ensure trailing slash so new URL("users/current", baseUrl) resolves correctly
8
+ // Without it, new URL("path", "https://host/api/v1") yields "https://host/path"
9
+ this.baseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
10
+ this.apiKey = apiKey;
11
+ }
12
+ async get(path, params) {
13
+ const url = new URL(path, this.baseUrl);
14
+ if (params) {
15
+ for (const [key, value] of Object.entries(params)) {
16
+ if (value !== undefined && value !== "") {
17
+ url.searchParams.set(key, value);
18
+ }
19
+ }
20
+ }
21
+ const urlStr = url.toString();
22
+ try {
23
+ const response = await fetch(urlStr, {
24
+ method: "GET",
25
+ headers: {
26
+ Authorization: `Bearer ${this.apiKey}`,
27
+ Accept: "application/json",
28
+ },
29
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
30
+ });
31
+ if (!response.ok) {
32
+ throw mapHttpStatusToError(response, urlStr);
33
+ }
34
+ return (await response.json());
35
+ }
36
+ catch (error) {
37
+ if (error instanceof TypeError || (error instanceof Error && error.name === "AbortError")) {
38
+ throw mapNetworkError(error, urlStr);
39
+ }
40
+ throw error;
41
+ }
42
+ }
43
+ }
44
+ //# sourceMappingURL=chronova-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronova-client.js","sourceRoot":"","sources":["../../src/lib/chronova-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEpE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,OAAO,cAAc;IACjB,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB,YACE,UAAkB,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,6BAA6B,EAC/E,SAAiB,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE;QAEnD,gFAAgF;QAChF,gFAAgF;QAChF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;QAC/D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,IAAY,EAAE,MAA+B;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACxC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAE9B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;gBACnC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;oBACtC,MAAM,EAAE,kBAAkB;iBAC3B;gBACD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;aAChD,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC;YAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,SAAS,IAAI,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;gBAC1F,MAAM,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ export interface ChronovaConfig {
2
+ apiKey: string;
3
+ apiUrl: string;
4
+ port: number;
5
+ configSource: "env" | "chronova.cfg" | "wakatime.cfg" | "none";
6
+ }
7
+ export interface ResolveConfigOptions {
8
+ readFile?: (path: string) => Record<string, string> | null;
9
+ getHomeDir?: () => string;
10
+ env?: NodeJS.ProcessEnv;
11
+ }
12
+ export declare function parseIniFile(content: string): Record<string, string>;
13
+ export declare function readConfigFile(filePath: string): Record<string, string> | null;
14
+ export declare function resolveConfig(options?: ResolveConfigOptions): ChronovaConfig;
15
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,KAAK,GAAG,cAAc,GAAG,cAAc,GAAG,MAAM,CAAC;CAChE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC3D,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAKD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcpE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAO9E;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,cAAc,CA4C5E"}
@@ -0,0 +1,71 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const DEFAULT_API_URL = "https://chronova.dev/api/v1";
5
+ const DEFAULT_PORT = 3001;
6
+ export function parseIniFile(content) {
7
+ const result = {};
8
+ for (const line of content.split("\n")) {
9
+ const trimmed = line.trim();
10
+ if (trimmed === "" || trimmed.startsWith("[") || trimmed.startsWith("#") || trimmed.startsWith(";")) {
11
+ continue;
12
+ }
13
+ const eqIndex = trimmed.indexOf("=");
14
+ if (eqIndex === -1)
15
+ continue;
16
+ const key = trimmed.slice(0, eqIndex).trim();
17
+ const value = trimmed.slice(eqIndex + 1).trim();
18
+ result[key] = value;
19
+ }
20
+ return result;
21
+ }
22
+ export function readConfigFile(filePath) {
23
+ try {
24
+ const content = readFileSync(filePath, "utf-8");
25
+ return parseIniFile(content);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ export function resolveConfig(options) {
32
+ const readFile = options?.readFile ?? readConfigFile;
33
+ const getHome = options?.getHomeDir ?? homedir;
34
+ const env = options?.env ?? process.env;
35
+ const envApiKey = env.CHRONOVA_API_KEY;
36
+ const envApiUrl = env.CHRONOVA_API_URL;
37
+ const envPort = env.PORT;
38
+ if (envApiKey) {
39
+ return {
40
+ apiKey: envApiKey,
41
+ apiUrl: envApiUrl ?? DEFAULT_API_URL,
42
+ port: envPort ? Number(envPort) : DEFAULT_PORT,
43
+ configSource: "env",
44
+ };
45
+ }
46
+ const chronovaCfg = readFile(join(getHome(), ".chronova.cfg"));
47
+ if (chronovaCfg?.api_key) {
48
+ return {
49
+ apiKey: chronovaCfg.api_key,
50
+ apiUrl: envApiUrl ?? chronovaCfg.api_url ?? DEFAULT_API_URL,
51
+ port: envPort ? Number(envPort) : DEFAULT_PORT,
52
+ configSource: "chronova.cfg",
53
+ };
54
+ }
55
+ const wakatimeCfg = readFile(join(getHome(), ".wakatime.cfg"));
56
+ if (wakatimeCfg?.api_key) {
57
+ return {
58
+ apiKey: wakatimeCfg.api_key,
59
+ apiUrl: envApiUrl ?? wakatimeCfg.api_url ?? DEFAULT_API_URL,
60
+ port: envPort ? Number(envPort) : DEFAULT_PORT,
61
+ configSource: "wakatime.cfg",
62
+ };
63
+ }
64
+ return {
65
+ apiKey: "",
66
+ apiUrl: envApiUrl ?? DEFAULT_API_URL,
67
+ port: envPort ? Number(envPort) : DEFAULT_PORT,
68
+ configSource: "none",
69
+ };
70
+ }
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare class ChronovaApiError extends Error {
2
+ statusCode: number;
3
+ code: string;
4
+ retryAfter?: number;
5
+ constructor(message: string, statusCode: number, code: string, retryAfter?: number);
6
+ }
7
+ export declare function mapHttpStatusToError(response: Response, url: string): ChronovaApiError;
8
+ export declare function mapNetworkError(error: unknown, url: string): ChronovaApiError;
9
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;gBAER,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAOnF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CA8DtF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAM7E"}
@@ -0,0 +1,53 @@
1
+ export class ChronovaApiError extends Error {
2
+ statusCode;
3
+ code;
4
+ retryAfter;
5
+ constructor(message, statusCode, code, retryAfter) {
6
+ super(message);
7
+ this.name = "ChronovaApiError";
8
+ this.statusCode = statusCode;
9
+ this.code = code;
10
+ this.retryAfter = retryAfter;
11
+ }
12
+ }
13
+ export function mapHttpStatusToError(response, url) {
14
+ const status = response.status;
15
+ const statusText = response.statusText;
16
+ switch (status) {
17
+ case 401:
18
+ return new ChronovaApiError("Unauthorized: Invalid or expired API key. Check your CHRONOVA_API_KEY configuration.", 401, "UNAUTHORIZED");
19
+ case 429: {
20
+ let retryAfter;
21
+ const retryAfterHeader = response.headers.get("Retry-After");
22
+ if (retryAfterHeader) {
23
+ const parsed = Number(retryAfterHeader);
24
+ if (!Number.isNaN(parsed)) {
25
+ retryAfter = parsed;
26
+ }
27
+ }
28
+ if (retryAfter === undefined) {
29
+ const resetHeader = response.headers.get("X-RateLimit-Reset");
30
+ if (resetHeader) {
31
+ const resetEpoch = Number(resetHeader);
32
+ if (!Number.isNaN(resetEpoch) && resetEpoch > 0) {
33
+ const remaining = Math.max(0, Math.ceil(resetEpoch - Date.now() / 1000));
34
+ retryAfter = remaining;
35
+ }
36
+ }
37
+ }
38
+ const suffix = retryAfter !== undefined ? ` Retry after ${retryAfter} seconds.` : "";
39
+ return new ChronovaApiError(`Rate limited: Too many requests.${suffix}`, 429, "RATE_LIMITED", retryAfter);
40
+ }
41
+ case 404:
42
+ return new ChronovaApiError("Not found: The requested resource does not exist.", 404, "NOT_FOUND");
43
+ default:
44
+ if (status >= 500) {
45
+ return new ChronovaApiError(`Chronova server error: ${statusText}. Please try again later.`, status, "SERVER_ERROR");
46
+ }
47
+ return new ChronovaApiError(`Chronova API error: ${status} ${statusText}`, status, "API_ERROR");
48
+ }
49
+ }
50
+ export function mapNetworkError(error, url) {
51
+ return new ChronovaApiError(`Cannot connect to Chronova at ${url}. Check CHRONOVA_API_URL configuration.`, 0, "CONNECTION_ERROR");
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,UAAU,CAAS;IACnB,IAAI,CAAS;IACb,UAAU,CAAU;IAEpB,YAAY,OAAe,EAAE,UAAkB,EAAE,IAAY,EAAE,UAAmB;QAChF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAkB,EAAE,GAAW;IAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC/B,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IAEvC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,IAAI,gBAAgB,CACzB,sFAAsF,EACtF,GAAG,EACH,cAAc,CACf,CAAC;QAEJ,KAAK,GAAG,CAAC,CAAC,CAAC;YACT,IAAI,UAA8B,CAAC;YACnC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC7D,IAAI,gBAAgB,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,UAAU,GAAG,MAAM,CAAC;gBACtB,CAAC;YACH,CAAC;YACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBAC9D,IAAI,WAAW,EAAE,CAAC;oBAChB,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;oBACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;wBAChD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;wBACzE,UAAU,GAAG,SAAS,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,UAAU,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACrF,OAAO,IAAI,gBAAgB,CACzB,mCAAmC,MAAM,EAAE,EAC3C,GAAG,EACH,cAAc,EACd,UAAU,CACX,CAAC;QACJ,CAAC;QAED,KAAK,GAAG;YACN,OAAO,IAAI,gBAAgB,CACzB,mDAAmD,EACnD,GAAG,EACH,WAAW,CACZ,CAAC;QAEJ;YACE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;gBAClB,OAAO,IAAI,gBAAgB,CACzB,0BAA0B,UAAU,2BAA2B,EAC/D,MAAM,EACN,cAAc,CACf,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,gBAAgB,CACzB,uBAAuB,MAAM,IAAI,UAAU,EAAE,EAC7C,MAAM,EACN,WAAW,CACZ,CAAC;IACN,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,GAAW;IACzD,OAAO,IAAI,gBAAgB,CACzB,iCAAiC,GAAG,yCAAyC,EAC7E,CAAC,EACD,kBAAkB,CACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,113 @@
1
+ export interface ChronovaUser {
2
+ id: string;
3
+ username: string;
4
+ email: string;
5
+ avatar_url: string | null;
6
+ subscription: {
7
+ plan: string;
8
+ status: string;
9
+ };
10
+ github_connected: boolean;
11
+ organizations: Array<{
12
+ id: string;
13
+ name: string;
14
+ role: string;
15
+ }>;
16
+ created_at: string;
17
+ modified_at: string;
18
+ }
19
+ export interface ChronovaStatsRange {
20
+ range: string;
21
+ total_seconds: number;
22
+ languages: Array<{
23
+ name: string;
24
+ total_seconds: number;
25
+ percent: number;
26
+ }>;
27
+ projects: Array<{
28
+ name: string;
29
+ total_seconds: number;
30
+ percent: number;
31
+ }>;
32
+ editors: Array<{
33
+ name: string;
34
+ total_seconds: number;
35
+ percent: number;
36
+ }>;
37
+ operating_systems: Array<{
38
+ name: string;
39
+ total_seconds: number;
40
+ percent: number;
41
+ }>;
42
+ daily_stats: Array<{
43
+ date: string;
44
+ total_seconds: number;
45
+ }>;
46
+ hourly_stats: Array<{
47
+ hour: number;
48
+ total_seconds: number;
49
+ }>;
50
+ best_day: {
51
+ date: string;
52
+ total_seconds: number;
53
+ } | null;
54
+ start: string;
55
+ end: string;
56
+ }
57
+ export interface ChronovaHeartbeat {
58
+ id: string;
59
+ time: string;
60
+ type: string;
61
+ project: string | null;
62
+ language: string | null;
63
+ editor: string | null;
64
+ operating_system: string | null;
65
+ machine: string | null;
66
+ branch: string | null;
67
+ created_at: string;
68
+ }
69
+ export interface ChronovaHeartbeatResponse {
70
+ heartbeats: ChronovaHeartbeat[];
71
+ total: number;
72
+ page: number;
73
+ per_page: number;
74
+ total_pages: number;
75
+ }
76
+ export interface ChronovaAiAnalytics {
77
+ adoptionTimeline: Array<{
78
+ date: string;
79
+ aiSeconds: number;
80
+ manualSeconds: number;
81
+ }>;
82
+ contributionShare: {
83
+ aiPercent: number;
84
+ manualPercent: number;
85
+ aiHours: number;
86
+ manualHours: number;
87
+ };
88
+ comparison: {
89
+ withAi: {
90
+ totalSeconds: number;
91
+ avgDaily: number;
92
+ };
93
+ withoutAi: {
94
+ totalSeconds: number;
95
+ avgDaily: number;
96
+ };
97
+ };
98
+ languageMatrix: Array<{
99
+ language: string;
100
+ aiPercent: number;
101
+ manualPercent: number;
102
+ }>;
103
+ projectDependency: Array<{
104
+ project: string;
105
+ aiPercent: number;
106
+ manualPercent: number;
107
+ }>;
108
+ efficiencyTrend: Array<{
109
+ period: string;
110
+ productivity: number;
111
+ }>;
112
+ }
113
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import express from "express";
3
+ import { type ChronovaConfig } from "./lib/config.js";
4
+ export declare function createApp(config?: ChronovaConfig): express.Express;
5
+ export declare function startServer(): import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,OAAwC,MAAM,SAAS,CAAC;AAI/D,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAyBrE,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CA6DlE;AAED,wBAAgB,WAAW,8EA0B1B"}
package/dist/server.js ADDED
@@ -0,0 +1,88 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import cors from "cors";
3
+ import express from "express";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
+ import { ChronovaClient } from "./lib/chronova-client.js";
7
+ import { resolveConfig } from "./lib/config.js";
8
+ import { registerGetDeveloperContext } from "./tools/get-developer-context.js";
9
+ import { registerGetAiInsights } from "./tools/get-ai-insights.js";
10
+ import { registerGetProductivitySummary } from "./tools/get-productivity-summary.js";
11
+ import { registerGetRecentActivity } from "./tools/get-recent-activity.js";
12
+ const VERSION = "0.1.0";
13
+ function createMcpServer(config) {
14
+ const server = new McpServer({ name: "chronova-mcp", version: VERSION });
15
+ const chronova = new ChronovaClient(config.apiUrl, config.apiKey);
16
+ registerGetAiInsights(server, chronova);
17
+ registerGetDeveloperContext(server, chronova);
18
+ registerGetProductivitySummary(server, chronova);
19
+ registerGetRecentActivity(server, chronova);
20
+ return { server, chronova };
21
+ }
22
+ export function createApp(config) {
23
+ const resolvedConfig = config ?? resolveConfig();
24
+ const app = express();
25
+ app.use(cors());
26
+ app.use(express.json());
27
+ const sessions = new Map();
28
+ app.get("/health", (_req, res) => {
29
+ res.json({ status: "ok", version: VERSION });
30
+ });
31
+ async function handleMcpRequest(req, res) {
32
+ const sessionId = req.headers["mcp-session-id"];
33
+ if (sessionId) {
34
+ const session = sessions.get(sessionId);
35
+ if (!session) {
36
+ res.status(400).json({
37
+ jsonrpc: "2.0",
38
+ error: { code: -32600, message: "Invalid or expired session ID" },
39
+ id: null,
40
+ });
41
+ return;
42
+ }
43
+ await session.transport.handleRequest(req, res, req.body);
44
+ return;
45
+ }
46
+ const { server: mcpServer } = createMcpServer(resolvedConfig);
47
+ const transport = new StreamableHTTPServerTransport({
48
+ sessionIdGenerator: () => randomUUID(),
49
+ onsessioninitialized: (newSessionId) => {
50
+ sessions.set(newSessionId, { transport, server: mcpServer });
51
+ },
52
+ });
53
+ mcpServer.server.onclose = () => {
54
+ const sid = transport.sessionId;
55
+ if (sid && sessions.has(sid)) {
56
+ sessions.delete(sid);
57
+ }
58
+ };
59
+ await mcpServer.connect(transport);
60
+ await transport.handleRequest(req, res, req.body);
61
+ }
62
+ app.post("/mcp", handleMcpRequest);
63
+ app.get("/mcp", handleMcpRequest);
64
+ app.delete("/mcp", handleMcpRequest);
65
+ return app;
66
+ }
67
+ export function startServer() {
68
+ const config = resolveConfig();
69
+ if (!config.apiKey) {
70
+ console.warn("Warning: No API key found. Set CHRONOVA_API_KEY env var, or add api_key to ~/.chronova.cfg or ~/.wakatime.cfg. API requests will fail.");
71
+ }
72
+ else if (config.configSource !== "env") {
73
+ console.log(`Using API key from ${config.configSource}`);
74
+ }
75
+ const app = createApp(config);
76
+ const httpServer = app.listen(config.port, () => {
77
+ console.log(`Chronova MCP server listening on port ${config.port}`);
78
+ });
79
+ async function shutdown() {
80
+ console.log("Shutting down...");
81
+ httpServer.close();
82
+ process.exit(0);
83
+ }
84
+ process.on("SIGTERM", shutdown);
85
+ process.on("SIGINT", shutdown);
86
+ return httpServer;
87
+ }
88
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":""}
package/dist/stdio.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { ChronovaClient } from "./lib/chronova-client.js";
5
+ import { resolveConfig } from "./lib/config.js";
6
+ import { registerGetDeveloperContext } from "./tools/get-developer-context.js";
7
+ import { registerGetAiInsights } from "./tools/get-ai-insights.js";
8
+ import { registerGetProductivitySummary } from "./tools/get-productivity-summary.js";
9
+ import { registerGetRecentActivity } from "./tools/get-recent-activity.js";
10
+ const VERSION = "0.1.0";
11
+ async function main() {
12
+ const config = resolveConfig();
13
+ if (!config.apiKey) {
14
+ console.error("Error: No API key found. Set CHRONOVA_API_KEY env var, or add api_key to ~/.chronova.cfg or ~/.wakatime.cfg.");
15
+ process.exit(1);
16
+ }
17
+ const server = new McpServer({ name: "chronova-mcp", version: VERSION });
18
+ const chronova = new ChronovaClient(config.apiUrl, config.apiKey);
19
+ registerGetAiInsights(server, chronova);
20
+ registerGetDeveloperContext(server, chronova);
21
+ registerGetProductivitySummary(server, chronova);
22
+ registerGetRecentActivity(server, chronova);
23
+ const transport = new StdioServerTransport();
24
+ await server.connect(transport);
25
+ }
26
+ main().catch((error) => {
27
+ console.error("Fatal error:", error);
28
+ process.exit(1);
29
+ });
30
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CACX,8GAA8G,CAC/G,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAElE,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACxC,2BAA2B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9C,8BAA8B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjD,yBAAyB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE5C,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ChronovaClient } from "../lib/chronova-client.js";
3
+ export declare function registerGetAiInsights(server: McpServer, chronova: ChronovaClient): void;
4
+ //# sourceMappingURL=get-ai-insights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-ai-insights.d.ts","sourceRoot":"","sources":["../../src/tools/get-ai-insights.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,cAAc,GACvB,IAAI,CAyDN"}
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ import { ChronovaApiError } from "../lib/errors.js";
3
+ export function registerGetAiInsights(server, chronova) {
4
+ server.registerTool("get_ai_insights", {
5
+ description: "Get AI-assisted coding analytics including adoption timeline (AI vs manual coding over time), contribution share (percentage of AI vs manual work), human vs AI comparison by language, project-level AI dependency, and efficiency trends.",
6
+ inputSchema: z.object({
7
+ range: z
8
+ .enum([
9
+ "today",
10
+ "last_7_days",
11
+ "last_30_days",
12
+ "last_3_months",
13
+ "last_6_months",
14
+ "last_year",
15
+ "all_time",
16
+ ])
17
+ .or(z.string().regex(/^\d{4}-\d{2}-\d{2}_to_\d{4}-\d{2}-\d{2}$/))
18
+ .describe('Time range for analytics. Named ranges (today, last_7_days, etc.) or custom date range (YYYY-MM-DD_to_YYYY-MM-DD).'),
19
+ }),
20
+ annotations: { readOnlyHint: true },
21
+ }, async ({ range }) => {
22
+ try {
23
+ const response = await chronova.get("users/current/analytics/ai", { range });
24
+ return {
25
+ content: [
26
+ { type: "text", text: JSON.stringify(response.data, null, 2) },
27
+ ],
28
+ };
29
+ }
30
+ catch (error) {
31
+ if (error instanceof ChronovaApiError) {
32
+ return {
33
+ content: [{ type: "text", text: error.message }],
34
+ isError: true,
35
+ };
36
+ }
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`,
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
47
+ });
48
+ }
49
+ //# sourceMappingURL=get-ai-insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-ai-insights.js","sourceRoot":"","sources":["../../src/tools/get-ai-insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,QAAwB;IAExB,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,6OAA6O;QAC/O,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC;iBACL,IAAI,CAAC;gBACJ,OAAO;gBACP,aAAa;gBACb,cAAc;gBACd,eAAe;gBACf,eAAe;gBACf,WAAW;gBACX,UAAU;aACX,CAAC;iBACD,EAAE,CACD,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAC7D;iBACA,QAAQ,CACP,oHAAoH,CACrH;SACJ,CAAC;QACF,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,GAAG,CACjC,4BAA4B,EAC5B,EAAE,KAAK,EAAE,CACV,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACxE;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;gBACtC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;oBACzD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBACpF;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ChronovaClient } from "../lib/chronova-client.js";
3
+ export declare function registerGetDeveloperContext(server: McpServer, chronova: ChronovaClient): void;
4
+ //# sourceMappingURL=get-developer-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-developer-context.d.ts","sourceRoot":"","sources":["../../src/tools/get-developer-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,cAAc,GACvB,IAAI,CAsCN"}
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { ChronovaApiError } from "../lib/errors.js";
3
+ export function registerGetDeveloperContext(server, chronova) {
4
+ server.registerTool("get_developer_context", {
5
+ description: "Get the authenticated user's developer profile including coding statistics, subscription status, GitHub integration status, and organization memberships. No parameters required — uses the configured API key.",
6
+ inputSchema: z.object({}),
7
+ annotations: { readOnlyHint: true },
8
+ }, async () => {
9
+ try {
10
+ const response = await chronova.get("users/current");
11
+ return {
12
+ content: [
13
+ { type: "text", text: JSON.stringify(response.data, null, 2) },
14
+ ],
15
+ };
16
+ }
17
+ catch (error) {
18
+ if (error instanceof ChronovaApiError) {
19
+ return {
20
+ content: [{ type: "text", text: error.message }],
21
+ isError: true,
22
+ };
23
+ }
24
+ return {
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`,
29
+ },
30
+ ],
31
+ isError: true,
32
+ };
33
+ }
34
+ });
35
+ }
36
+ //# sourceMappingURL=get-developer-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-developer-context.js","sourceRoot":"","sources":["../../src/tools/get-developer-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,UAAU,2BAA2B,CACzC,MAAiB,EACjB,QAAwB;IAExB,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,WAAW,EACT,iNAAiN;QACnN,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,GAAG,CACjC,eAAe,CAChB,CAAC;YACF,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACxE;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;gBACtC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;oBACzD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBACpF;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ChronovaClient } from "../lib/chronova-client.js";
3
+ export declare function registerGetProductivitySummary(server: McpServer, chronova: ChronovaClient): void;
4
+ //# sourceMappingURL=get-productivity-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-productivity-summary.d.ts","sourceRoot":"","sources":["../../src/tools/get-productivity-summary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,cAAc,GACvB,IAAI,CAgEN"}
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ import { ChronovaApiError } from "../lib/errors.js";
3
+ export function registerGetProductivitySummary(server, chronova) {
4
+ server.registerTool("get_productivity_summary", {
5
+ description: "Get aggregated coding productivity statistics for a time range. Returns total coding time, language breakdown, editor breakdown, and project breakdown.",
6
+ inputSchema: z.object({
7
+ range: z
8
+ .enum([
9
+ "today",
10
+ "last_7_days",
11
+ "last_30_days",
12
+ "last_3_months",
13
+ "last_6_months",
14
+ "last_year",
15
+ "all_time",
16
+ ])
17
+ .describe('Time range for statistics. Named ranges (today, last_7_days, etc.) or custom formats (YYYY for year, YYYY-MM for month, YYYY-MM-DD_to_YYYY-MM-DD for date range).'),
18
+ project: z
19
+ .string()
20
+ .optional()
21
+ .describe("Filter results to a specific project name"),
22
+ }),
23
+ annotations: { readOnlyHint: true },
24
+ }, async ({ range, project }) => {
25
+ try {
26
+ const path = `users/current/stats/${range}`;
27
+ const params = {};
28
+ if (project) {
29
+ params.project = project;
30
+ }
31
+ const response = await chronova.get(path, Object.keys(params).length > 0 ? params : undefined);
32
+ return {
33
+ content: [
34
+ { type: "text", text: JSON.stringify(response.data, null, 2) },
35
+ ],
36
+ };
37
+ }
38
+ catch (error) {
39
+ if (error instanceof ChronovaApiError) {
40
+ return {
41
+ content: [{ type: "text", text: error.message }],
42
+ isError: true,
43
+ };
44
+ }
45
+ return {
46
+ content: [
47
+ {
48
+ type: "text",
49
+ text: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`,
50
+ },
51
+ ],
52
+ isError: true,
53
+ };
54
+ }
55
+ });
56
+ }
57
+ //# sourceMappingURL=get-productivity-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-productivity-summary.js","sourceRoot":"","sources":["../../src/tools/get-productivity-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,UAAU,8BAA8B,CAC5C,MAAiB,EACjB,QAAwB;IAExB,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;QACE,WAAW,EACT,yJAAyJ;QAC3J,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC;iBACL,IAAI,CAAC;gBACJ,OAAO;gBACP,aAAa;gBACb,cAAc;gBACd,eAAe;gBACf,eAAe;gBACf,WAAW;gBACX,UAAU;aACX,CAAC;iBACD,QAAQ,CACP,mKAAmK,CACpK;YACH,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,2CAA2C,CAAC;SACzD,CAAC;QACF,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,uBAAuB,KAAK,EAAE,CAAC;YAC5C,MAAM,MAAM,GAA2B,EAAE,CAAC;YAC1C,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YAC3B,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,GAAG,CACjC,IAAI,EACJ,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CACpD,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACxE;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;gBACtC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;oBACzD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,qBAAqB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBACpF;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ChronovaClient } from "../lib/chronova-client.js";
3
+ export declare function registerGetRecentActivity(server: McpServer, chronova: ChronovaClient): void;
4
+ //# sourceMappingURL=get-recent-activity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-recent-activity.d.ts","sourceRoot":"","sources":["../../src/tools/get-recent-activity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,cAAc,GACvB,IAAI,CA0FN"}
@@ -0,0 +1,93 @@
1
+ import { z } from "zod";
2
+ import { ChronovaApiError } from "../lib/errors.js";
3
+ export function registerGetRecentActivity(server, chronova) {
4
+ server.registerTool("get_recent_activity", {
5
+ description: "Get recent coding heartbeats (activity events). Returns paginated results — use 'page' and 'per_page' parameters to navigate through large result sets. The response includes 'total' count and pagination metadata.",
6
+ inputSchema: z.object({
7
+ date: z
8
+ .string()
9
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
10
+ .optional()
11
+ .describe("Filter by specific date (YYYY-MM-DD)"),
12
+ start: z
13
+ .string()
14
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
15
+ .optional()
16
+ .describe("Start date for range (YYYY-MM-DD)"),
17
+ end: z
18
+ .string()
19
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
20
+ .optional()
21
+ .describe("End date for range (YYYY-MM-DD)"),
22
+ project: z
23
+ .string()
24
+ .optional()
25
+ .describe("Filter by project name"),
26
+ language: z
27
+ .string()
28
+ .optional()
29
+ .describe("Filter by programming language"),
30
+ editor: z
31
+ .string()
32
+ .optional()
33
+ .describe("Filter by editor/IDE name"),
34
+ page: z
35
+ .number()
36
+ .int()
37
+ .positive()
38
+ .optional()
39
+ .describe("Page number for pagination (default: 1)"),
40
+ per_page: z
41
+ .number()
42
+ .int()
43
+ .positive()
44
+ .optional()
45
+ .describe("Results per page (default: 100, max: 100)"),
46
+ }),
47
+ annotations: { readOnlyHint: true },
48
+ }, async ({ date, start, end, project, language, editor, page, per_page }) => {
49
+ try {
50
+ const params = {};
51
+ if (date !== undefined)
52
+ params.date = date;
53
+ if (start !== undefined)
54
+ params.start = start;
55
+ if (end !== undefined)
56
+ params.end = end;
57
+ if (project !== undefined)
58
+ params.project = project;
59
+ if (language !== undefined)
60
+ params.language = language;
61
+ if (editor !== undefined)
62
+ params.editor = editor;
63
+ if (page !== undefined)
64
+ params.page = String(page);
65
+ if (per_page !== undefined)
66
+ params.per_page = String(per_page);
67
+ const response = await chronova.get("users/current/heartbeats", Object.keys(params).length > 0 ? params : undefined);
68
+ return {
69
+ content: [
70
+ { type: "text", text: JSON.stringify(response, null, 2) },
71
+ ],
72
+ };
73
+ }
74
+ catch (error) {
75
+ if (error instanceof ChronovaApiError) {
76
+ return {
77
+ content: [{ type: "text", text: error.message }],
78
+ isError: true,
79
+ };
80
+ }
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`,
86
+ },
87
+ ],
88
+ isError: true,
89
+ };
90
+ }
91
+ });
92
+ }
93
+ //# sourceMappingURL=get-recent-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-recent-activity.js","sourceRoot":"","sources":["../../src/tools/get-recent-activity.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chronova/mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -17,6 +17,7 @@
17
17
  "private": false,
18
18
  "scripts": {
19
19
  "build": "tsc",
20
+ "prepublishOnly": "npm run build",
20
21
  "start": "node dist/index.js",
21
22
  "dev": "tsc --watch & node --watch dist/index.js",
22
23
  "test": "vitest run",