@graphty/remote-logger 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +318 -10
- package/dist/client/RemoteLogClient.d.ts +2 -0
- package/dist/client/RemoteLogClient.d.ts.map +1 -1
- package/dist/client/RemoteLogClient.js +35 -4
- package/dist/client/RemoteLogClient.js.map +1 -1
- package/dist/client/types.d.ts +13 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +9 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +32 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -0
- package/dist/mcp/mcp-server.js +270 -0
- package/dist/mcp/mcp-server.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +14 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +14 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/logs-clear.d.ts +76 -0
- package/dist/mcp/tools/logs-clear.d.ts.map +1 -0
- package/dist/mcp/tools/logs-clear.js +58 -0
- package/dist/mcp/tools/logs-clear.js.map +1 -0
- package/dist/mcp/tools/logs-get-all.d.ts +60 -0
- package/dist/mcp/tools/logs-get-all.d.ts.map +1 -0
- package/dist/mcp/tools/logs-get-all.js +50 -0
- package/dist/mcp/tools/logs-get-all.js.map +1 -0
- package/dist/mcp/tools/logs-get-errors.d.ts +65 -0
- package/dist/mcp/tools/logs-get-errors.d.ts.map +1 -0
- package/dist/mcp/tools/logs-get-errors.js +46 -0
- package/dist/mcp/tools/logs-get-errors.js.map +1 -0
- package/dist/mcp/tools/logs-get-file-path.d.ts +75 -0
- package/dist/mcp/tools/logs-get-file-path.d.ts.map +1 -0
- package/dist/mcp/tools/logs-get-file-path.js +90 -0
- package/dist/mcp/tools/logs-get-file-path.js.map +1 -0
- package/dist/mcp/tools/logs-get-recent.d.ts +89 -0
- package/dist/mcp/tools/logs-get-recent.d.ts.map +1 -0
- package/dist/mcp/tools/logs-get-recent.js +74 -0
- package/dist/mcp/tools/logs-get-recent.js.map +1 -0
- package/dist/mcp/tools/logs-list-sessions.d.ts +64 -0
- package/dist/mcp/tools/logs-list-sessions.d.ts.map +1 -0
- package/dist/mcp/tools/logs-list-sessions.js +48 -0
- package/dist/mcp/tools/logs-list-sessions.js.map +1 -0
- package/dist/mcp/tools/logs-receive.d.ts +150 -0
- package/dist/mcp/tools/logs-receive.d.ts.map +1 -0
- package/dist/mcp/tools/logs-receive.js +68 -0
- package/dist/mcp/tools/logs-receive.js.map +1 -0
- package/dist/mcp/tools/logs-search.d.ts +91 -0
- package/dist/mcp/tools/logs-search.d.ts.map +1 -0
- package/dist/mcp/tools/logs-search.js +68 -0
- package/dist/mcp/tools/logs-search.js.map +1 -0
- package/dist/mcp/tools/logs-status.d.ts +45 -0
- package/dist/mcp/tools/logs-status.d.ts.map +1 -0
- package/dist/mcp/tools/logs-status.js +45 -0
- package/dist/mcp/tools/logs-status.js.map +1 -0
- package/dist/server/dual-server.d.ts +76 -0
- package/dist/server/dual-server.d.ts.map +1 -0
- package/dist/server/dual-server.js +214 -0
- package/dist/server/dual-server.js.map +1 -0
- package/dist/server/index.d.ts +5 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +5 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/jsonl-writer.d.ts +93 -0
- package/dist/server/jsonl-writer.d.ts.map +1 -0
- package/dist/server/jsonl-writer.js +205 -0
- package/dist/server/jsonl-writer.js.map +1 -0
- package/dist/server/log-server.d.ts +62 -11
- package/dist/server/log-server.d.ts.map +1 -1
- package/dist/server/log-server.js +237 -101
- package/dist/server/log-server.js.map +1 -1
- package/dist/server/log-storage.d.ts +301 -0
- package/dist/server/log-storage.d.ts.map +1 -0
- package/dist/server/log-storage.js +408 -0
- package/dist/server/log-storage.js.map +1 -0
- package/dist/server/marker-utils.d.ts +69 -0
- package/dist/server/marker-utils.d.ts.map +1 -0
- package/dist/server/marker-utils.js +118 -0
- package/dist/server/marker-utils.js.map +1 -0
- package/dist/vite/index.d.ts +8 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +8 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/plugin.d.ts +42 -0
- package/dist/vite/plugin.d.ts.map +1 -0
- package/dist/vite/plugin.js +46 -0
- package/dist/vite/plugin.js.map +1 -0
- package/package.json +12 -2
- package/src/client/RemoteLogClient.ts +52 -4
- package/src/client/types.ts +13 -0
- package/src/mcp/index.ts +25 -0
- package/src/mcp/mcp-server.ts +364 -0
- package/src/mcp/tools/index.ts +69 -0
- package/src/mcp/tools/logs-clear.ts +86 -0
- package/src/mcp/tools/logs-get-all.ts +78 -0
- package/src/mcp/tools/logs-get-errors.ts +71 -0
- package/src/mcp/tools/logs-get-file-path.ts +121 -0
- package/src/mcp/tools/logs-get-recent.ts +104 -0
- package/src/mcp/tools/logs-list-sessions.ts +71 -0
- package/src/mcp/tools/logs-receive.ts +96 -0
- package/src/mcp/tools/logs-search.ts +95 -0
- package/src/mcp/tools/logs-status.ts +69 -0
- package/src/server/dual-server.ts +308 -0
- package/src/server/index.ts +37 -0
- package/src/server/jsonl-writer.ts +277 -0
- package/src/server/log-server.ts +311 -119
- package/src/server/log-storage.ts +651 -0
- package/src/server/marker-utils.ts +144 -0
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin.ts +59 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for automatic project marker injection.
|
|
3
|
+
*
|
|
4
|
+
* This plugin injects global variables that the RemoteLogClient
|
|
5
|
+
* can use to automatically identify which project/worktree logs
|
|
6
|
+
* are coming from.
|
|
7
|
+
* @module vite/plugin
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Vite plugin configuration object.
|
|
11
|
+
* Matches Vite's Plugin type but without requiring the full vite dependency.
|
|
12
|
+
*/
|
|
13
|
+
interface VitePlugin {
|
|
14
|
+
name: string;
|
|
15
|
+
config: () => {
|
|
16
|
+
define: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Vite plugin that injects project marker globals.
|
|
21
|
+
*
|
|
22
|
+
* The plugin detects the current working directory and:
|
|
23
|
+
* 1. Extracts a project marker (worktree name or directory basename)
|
|
24
|
+
* 2. Injects `__REMOTE_LOG_PROJECT_MARKER__` with the marker
|
|
25
|
+
* 3. Injects `__REMOTE_LOG_WORKTREE_PATH__` with the full path
|
|
26
|
+
*
|
|
27
|
+
* These globals are automatically read by RemoteLogClient when no
|
|
28
|
+
* explicit projectMarker/worktreePath options are provided.
|
|
29
|
+
* @returns A Vite plugin configuration object
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // vite.config.ts
|
|
33
|
+
* import { remoteLoggerPlugin } from "@graphty/remote-logger/vite";
|
|
34
|
+
*
|
|
35
|
+
* export default defineConfig({
|
|
36
|
+
* plugins: [remoteLoggerPlugin()]
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function remoteLoggerPlugin(): VitePlugin;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;GAGG;AACH,UAAU,UAAU;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM;QACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;CACL;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,CAe/C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for automatic project marker injection.
|
|
3
|
+
*
|
|
4
|
+
* This plugin injects global variables that the RemoteLogClient
|
|
5
|
+
* can use to automatically identify which project/worktree logs
|
|
6
|
+
* are coming from.
|
|
7
|
+
* @module vite/plugin
|
|
8
|
+
*/
|
|
9
|
+
import { extractMarkerFromPath } from "../server/marker-utils.js";
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Vite plugin that injects project marker globals.
|
|
12
|
+
*
|
|
13
|
+
* The plugin detects the current working directory and:
|
|
14
|
+
* 1. Extracts a project marker (worktree name or directory basename)
|
|
15
|
+
* 2. Injects `__REMOTE_LOG_PROJECT_MARKER__` with the marker
|
|
16
|
+
* 3. Injects `__REMOTE_LOG_WORKTREE_PATH__` with the full path
|
|
17
|
+
*
|
|
18
|
+
* These globals are automatically read by RemoteLogClient when no
|
|
19
|
+
* explicit projectMarker/worktreePath options are provided.
|
|
20
|
+
* @returns A Vite plugin configuration object
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // vite.config.ts
|
|
24
|
+
* import { remoteLoggerPlugin } from "@graphty/remote-logger/vite";
|
|
25
|
+
*
|
|
26
|
+
* export default defineConfig({
|
|
27
|
+
* plugins: [remoteLoggerPlugin()]
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function remoteLoggerPlugin() {
|
|
32
|
+
return {
|
|
33
|
+
name: "remote-logger",
|
|
34
|
+
config() {
|
|
35
|
+
const cwd = process.cwd();
|
|
36
|
+
const marker = extractMarkerFromPath(cwd);
|
|
37
|
+
return {
|
|
38
|
+
define: {
|
|
39
|
+
__REMOTE_LOG_PROJECT_MARKER__: JSON.stringify(marker),
|
|
40
|
+
__REMOTE_LOG_WORKTREE_PATH__: JSON.stringify(cwd),
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAalE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB;IAC9B,OAAO;QACH,IAAI,EAAE,eAAe;QACrB,MAAM;YACF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;YAE1C,OAAO;gBACH,MAAM,EAAE;oBACJ,6BAA6B,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;oBACrD,4BAA4B,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;iBACpD;aACJ,CAAC;QACN,CAAC;KACJ,CAAC;AACN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphty/remote-logger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Remote logging client and server for browser debugging",
|
|
5
5
|
"author": "Adam Powers <apowers@ato.ms>",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,6 +21,14 @@
|
|
|
21
21
|
"./ui": {
|
|
22
22
|
"import": "./dist/ui/index.js",
|
|
23
23
|
"types": "./dist/ui/index.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./mcp": {
|
|
26
|
+
"import": "./dist/mcp/index.js",
|
|
27
|
+
"types": "./dist/mcp/index.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./vite": {
|
|
30
|
+
"import": "./dist/vite/index.js",
|
|
31
|
+
"types": "./dist/vite/index.d.ts"
|
|
24
32
|
}
|
|
25
33
|
},
|
|
26
34
|
"types": "dist/index.d.ts",
|
|
@@ -60,7 +68,9 @@
|
|
|
60
68
|
"node": ">=18.19.0"
|
|
61
69
|
},
|
|
62
70
|
"dependencies": {
|
|
63
|
-
"
|
|
71
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
72
|
+
"selfsigned": "^2.4.1",
|
|
73
|
+
"zod": "^3.25.0"
|
|
64
74
|
},
|
|
65
75
|
"devDependencies": {
|
|
66
76
|
"@types/node": "^20.0.0",
|
|
@@ -6,6 +6,33 @@
|
|
|
6
6
|
|
|
7
7
|
import type { LogEntry, RemoteLogClientOptions, ThrottlePattern } from "./types.js";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Global variables that may be injected by the Vite plugin.
|
|
11
|
+
* These provide automatic project marker detection.
|
|
12
|
+
*/
|
|
13
|
+
declare const __REMOTE_LOG_PROJECT_MARKER__: string | undefined;
|
|
14
|
+
declare const __REMOTE_LOG_WORKTREE_PATH__: string | undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Safely read a global variable that may or may not be defined.
|
|
18
|
+
* @param name - Name of the global variable
|
|
19
|
+
* @returns The value of the global variable, or undefined if not defined
|
|
20
|
+
*/
|
|
21
|
+
function getGlobalValue(name: "__REMOTE_LOG_PROJECT_MARKER__" | "__REMOTE_LOG_WORKTREE_PATH__"): string | undefined {
|
|
22
|
+
try {
|
|
23
|
+
if (name === "__REMOTE_LOG_PROJECT_MARKER__") {
|
|
24
|
+
return typeof __REMOTE_LOG_PROJECT_MARKER__ !== "undefined" ? __REMOTE_LOG_PROJECT_MARKER__ : undefined;
|
|
25
|
+
}
|
|
26
|
+
if (name === "__REMOTE_LOG_WORKTREE_PATH__") {
|
|
27
|
+
return typeof __REMOTE_LOG_WORKTREE_PATH__ !== "undefined" ? __REMOTE_LOG_WORKTREE_PATH__ : undefined;
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
// ReferenceError if the global is not defined at all
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
9
36
|
/** Default configuration values */
|
|
10
37
|
const DEFAULT_BATCH_INTERVAL_MS = 1000;
|
|
11
38
|
const DEFAULT_MAX_RETRIES = 3;
|
|
@@ -57,6 +84,8 @@ export class RemoteLogClient {
|
|
|
57
84
|
private readonly maxRetries: number;
|
|
58
85
|
private readonly retryDelayMs: number;
|
|
59
86
|
private readonly throttlePatterns: ThrottlePattern[];
|
|
87
|
+
private readonly projectMarker: string | undefined;
|
|
88
|
+
private readonly worktreePath: string | undefined;
|
|
60
89
|
|
|
61
90
|
private pendingLogs: LogEntry[] = [];
|
|
62
91
|
private batchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -77,6 +106,10 @@ export class RemoteLogClient {
|
|
|
77
106
|
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
78
107
|
this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
79
108
|
this.throttlePatterns = options.throttlePatterns ?? [];
|
|
109
|
+
|
|
110
|
+
// Priority: explicit option > global variable
|
|
111
|
+
this.projectMarker = options.projectMarker ?? getGlobalValue("__REMOTE_LOG_PROJECT_MARKER__");
|
|
112
|
+
this.worktreePath = options.worktreePath ?? getGlobalValue("__REMOTE_LOG_WORKTREE_PATH__");
|
|
80
113
|
}
|
|
81
114
|
|
|
82
115
|
/**
|
|
@@ -199,15 +232,30 @@ export class RemoteLogClient {
|
|
|
199
232
|
* @throws Error if the request fails
|
|
200
233
|
*/
|
|
201
234
|
private async sendRequest(logs: LogEntry[]): Promise<void> {
|
|
235
|
+
const requestBody: {
|
|
236
|
+
sessionId: string;
|
|
237
|
+
logs: LogEntry[];
|
|
238
|
+
projectMarker?: string;
|
|
239
|
+
worktreePath?: string;
|
|
240
|
+
} = {
|
|
241
|
+
sessionId: this.sessionId,
|
|
242
|
+
logs,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Only include these fields if they have values
|
|
246
|
+
if (this.projectMarker !== undefined) {
|
|
247
|
+
requestBody.projectMarker = this.projectMarker;
|
|
248
|
+
}
|
|
249
|
+
if (this.worktreePath !== undefined) {
|
|
250
|
+
requestBody.worktreePath = this.worktreePath;
|
|
251
|
+
}
|
|
252
|
+
|
|
202
253
|
const response = await fetch(`${this.serverUrl}/log`, {
|
|
203
254
|
method: "POST",
|
|
204
255
|
headers: {
|
|
205
256
|
"Content-Type": "application/json",
|
|
206
257
|
},
|
|
207
|
-
body: JSON.stringify(
|
|
208
|
-
sessionId: this.sessionId,
|
|
209
|
-
logs,
|
|
210
|
-
}),
|
|
258
|
+
body: JSON.stringify(requestBody),
|
|
211
259
|
});
|
|
212
260
|
|
|
213
261
|
if (!response.ok) {
|
package/src/client/types.ts
CHANGED
|
@@ -46,4 +46,17 @@ export interface RemoteLogClientOptions {
|
|
|
46
46
|
* Messages matching a pattern will only be sent once per the configured interval.
|
|
47
47
|
*/
|
|
48
48
|
throttlePatterns?: ThrottlePattern[];
|
|
49
|
+
/**
|
|
50
|
+
* Project marker to identify logs from this project.
|
|
51
|
+
* If not provided, the client will attempt to read from the
|
|
52
|
+
* __REMOTE_LOG_PROJECT_MARKER__ global variable (injected by Vite plugin).
|
|
53
|
+
*/
|
|
54
|
+
projectMarker?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Full path to the worktree or project directory.
|
|
57
|
+
* Used for debugging and log organization.
|
|
58
|
+
* If not provided, the client will attempt to read from the
|
|
59
|
+
* __REMOTE_LOG_WORKTREE_PATH__ global variable (injected by Vite plugin).
|
|
60
|
+
*/
|
|
61
|
+
worktreePath?: string;
|
|
49
62
|
}
|
package/src/mcp/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server module exports.
|
|
3
|
+
*
|
|
4
|
+
* Provides Model Context Protocol interface for log queries.
|
|
5
|
+
* @module mcp
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createMcpServer, getToolNames, startMcpServer } from "./mcp-server.js";
|
|
9
|
+
export {
|
|
10
|
+
logsGetRecentHandler,
|
|
11
|
+
type LogsGetRecentInput,
|
|
12
|
+
logsGetRecentInputSchema,
|
|
13
|
+
type LogsGetRecentOutput,
|
|
14
|
+
logsGetRecentTool,
|
|
15
|
+
logsListSessionsHandler,
|
|
16
|
+
type LogsListSessionsInput,
|
|
17
|
+
logsListSessionsInputSchema,
|
|
18
|
+
type LogsListSessionsOutput,
|
|
19
|
+
logsListSessionsTool,
|
|
20
|
+
logsStatusHandler,
|
|
21
|
+
type LogsStatusInput,
|
|
22
|
+
logsStatusInputSchema,
|
|
23
|
+
type LogsStatusOutput,
|
|
24
|
+
logsStatusTool,
|
|
25
|
+
} from "./tools/index.js";
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server for remote logging.
|
|
3
|
+
*
|
|
4
|
+
* Provides an MCP interface for Claude Code to query logs from browser
|
|
5
|
+
* applications. Uses STDIO transport for communication.
|
|
6
|
+
* @module mcp/mcp-server
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
|
|
12
|
+
import type { LogStorage } from "../server/log-storage.js";
|
|
13
|
+
import {
|
|
14
|
+
logsClearHandler,
|
|
15
|
+
type LogsClearInput,
|
|
16
|
+
logsClearInputSchema,
|
|
17
|
+
logsClearTool,
|
|
18
|
+
logsGetAllHandler,
|
|
19
|
+
type LogsGetAllInput,
|
|
20
|
+
logsGetAllInputSchema,
|
|
21
|
+
logsGetAllTool,
|
|
22
|
+
logsGetErrorsHandler,
|
|
23
|
+
type LogsGetErrorsInput,
|
|
24
|
+
logsGetErrorsInputSchema,
|
|
25
|
+
logsGetErrorsTool,
|
|
26
|
+
logsGetFilePathHandler,
|
|
27
|
+
type LogsGetFilePathInput,
|
|
28
|
+
logsGetFilePathInputSchema,
|
|
29
|
+
logsGetFilePathTool,
|
|
30
|
+
logsGetRecentHandler,
|
|
31
|
+
type LogsGetRecentInput,
|
|
32
|
+
logsGetRecentInputSchema,
|
|
33
|
+
logsGetRecentTool,
|
|
34
|
+
logsListSessionsHandler,
|
|
35
|
+
type LogsListSessionsInput,
|
|
36
|
+
logsListSessionsInputSchema,
|
|
37
|
+
logsListSessionsTool,
|
|
38
|
+
logsReceiveHandler,
|
|
39
|
+
type LogsReceiveInput,
|
|
40
|
+
logsReceiveInputSchema,
|
|
41
|
+
logsReceiveTool,
|
|
42
|
+
logsSearchHandler,
|
|
43
|
+
type LogsSearchInput,
|
|
44
|
+
logsSearchInputSchema,
|
|
45
|
+
logsSearchTool,
|
|
46
|
+
logsStatusHandler,
|
|
47
|
+
logsStatusTool,
|
|
48
|
+
} from "./tools/index.js";
|
|
49
|
+
|
|
50
|
+
// Track registered tool names for testing
|
|
51
|
+
let registeredTools: string[] = [];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the names of all registered tools.
|
|
55
|
+
* Useful for testing.
|
|
56
|
+
* @returns Array of registered tool names
|
|
57
|
+
*/
|
|
58
|
+
export function getToolNames(): string[] {
|
|
59
|
+
return [...registeredTools];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Server instructions for LLMs describing the overall purpose and usage.
|
|
64
|
+
*/
|
|
65
|
+
const SERVER_INSTRUCTIONS = `Remote Logger MCP Server - View console.log output from browser applications.
|
|
66
|
+
|
|
67
|
+
## What is Remote Logging?
|
|
68
|
+
|
|
69
|
+
Browser applications run in a sandbox - their console.log() output appears in browser DevTools but is NOT visible to CLI tools or this assistant. Remote logging bridges this gap by sending browser logs to a server where they can be queried.
|
|
70
|
+
|
|
71
|
+
## When to Use Remote Logging
|
|
72
|
+
|
|
73
|
+
Use this when debugging applications where developer tools aren't easily accessible:
|
|
74
|
+
- Storybook components (stories running in browser)
|
|
75
|
+
- Web applications during development
|
|
76
|
+
- Mobile web apps (phones, tablets) where DevTools requires USB debugging
|
|
77
|
+
- VR/AR applications where you can't see a console while wearing a headset
|
|
78
|
+
- Embedded devices, kiosks, or smart displays without keyboard access
|
|
79
|
+
- Any JavaScript running in a browser context
|
|
80
|
+
|
|
81
|
+
Remote logging is especially valuable for LLM assistants like this one - it enables reading, interpreting, and acting on application logs without requiring user interaction with browser DevTools.
|
|
82
|
+
|
|
83
|
+
## Architecture
|
|
84
|
+
|
|
85
|
+
Browser App → HTTP POST to /logs → Log Server (stores in memory + JSONL files) → MCP Tools (query logs)
|
|
86
|
+
|
|
87
|
+
## Setting Up a Browser App to Send Logs
|
|
88
|
+
|
|
89
|
+
First, get the server endpoint URL by calling logs_status - look for server.httpEndpoint in the response.
|
|
90
|
+
|
|
91
|
+
### Option 1: Using the RemoteLogClient SDK (Recommended)
|
|
92
|
+
|
|
93
|
+
Install: npm install @graphty/remote-logger
|
|
94
|
+
|
|
95
|
+
\`\`\`typescript
|
|
96
|
+
import { RemoteLogClient } from "@graphty/remote-logger";
|
|
97
|
+
|
|
98
|
+
const logger = new RemoteLogClient({
|
|
99
|
+
serverUrl: "http://localhost:9080/logs", // Get this from logs_status
|
|
100
|
+
projectMarker: "my-project", // Optional: for filtering
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Intercept all console.log/warn/error calls
|
|
104
|
+
logger.interceptConsole();
|
|
105
|
+
|
|
106
|
+
// Or log manually
|
|
107
|
+
logger.log("INFO", "Hello from browser!");
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
### Option 2: Raw fetch() calls
|
|
111
|
+
|
|
112
|
+
\`\`\`typescript
|
|
113
|
+
fetch("http://localhost:9080/logs", {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
sessionId: "unique-session-id",
|
|
118
|
+
logs: [
|
|
119
|
+
{ time: new Date().toISOString(), level: "INFO", message: "Hello!" }
|
|
120
|
+
]
|
|
121
|
+
})
|
|
122
|
+
});
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
## Querying Logs (MCP Tools)
|
|
126
|
+
|
|
127
|
+
Once browser logs are flowing to the server:
|
|
128
|
+
|
|
129
|
+
1. logs_status - Check server health, get endpoint URL, see retention settings
|
|
130
|
+
2. logs_get_recent - View recent logs (primary tool, sorted chronologically)
|
|
131
|
+
3. logs_get_errors - Quick filter for ERROR level only
|
|
132
|
+
4. logs_search - Find specific text (supports regex)
|
|
133
|
+
5. logs_list_sessions - See all browser sessions
|
|
134
|
+
6. logs_get_all - Get all logs grouped by session
|
|
135
|
+
7. logs_get_file_path - Get JSONL file path for Grep/Read tools
|
|
136
|
+
8. logs_clear - Delete logs (requires confirmation)
|
|
137
|
+
|
|
138
|
+
## Typical Debugging Workflow
|
|
139
|
+
|
|
140
|
+
1. Call logs_status to verify server is running and get the endpoint URL
|
|
141
|
+
2. Ensure the browser app is configured to send logs to that endpoint
|
|
142
|
+
3. Trigger the action in the browser you want to debug
|
|
143
|
+
4. Call logs_get_recent to see what happened
|
|
144
|
+
5. Use logs_search if looking for specific errors or messages`;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create an MCP server with logging tools.
|
|
148
|
+
*
|
|
149
|
+
* Registers all logging tools and returns the configured server.
|
|
150
|
+
* The server is not started; use `startMcpServer` to run it.
|
|
151
|
+
* @param storage - The LogStorage instance to use for log operations
|
|
152
|
+
* @returns Configured McpServer instance
|
|
153
|
+
*/
|
|
154
|
+
export function createMcpServer(storage: LogStorage): McpServer {
|
|
155
|
+
const server = new McpServer(
|
|
156
|
+
{
|
|
157
|
+
name: "remote-logger",
|
|
158
|
+
version: "1.0.0",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Reset registered tools
|
|
166
|
+
registeredTools = [];
|
|
167
|
+
|
|
168
|
+
// Register logs_get_recent tool
|
|
169
|
+
server.registerTool(
|
|
170
|
+
logsGetRecentTool.name,
|
|
171
|
+
{
|
|
172
|
+
description: logsGetRecentTool.description,
|
|
173
|
+
inputSchema: logsGetRecentInputSchema,
|
|
174
|
+
},
|
|
175
|
+
(args: LogsGetRecentInput) => {
|
|
176
|
+
return logsGetRecentHandler(storage, args).then((r) => ({
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text" as const,
|
|
180
|
+
text: JSON.stringify(r, null, 2),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
}));
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
registeredTools.push(logsGetRecentTool.name);
|
|
187
|
+
|
|
188
|
+
// Register logs_status tool
|
|
189
|
+
server.registerTool(
|
|
190
|
+
logsStatusTool.name,
|
|
191
|
+
{
|
|
192
|
+
description: logsStatusTool.description,
|
|
193
|
+
},
|
|
194
|
+
() => {
|
|
195
|
+
return logsStatusHandler(storage).then((r) => ({
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: "text" as const,
|
|
199
|
+
text: JSON.stringify(r, null, 2),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
}));
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
registeredTools.push(logsStatusTool.name);
|
|
206
|
+
|
|
207
|
+
// Register logs_list_sessions tool
|
|
208
|
+
server.registerTool(
|
|
209
|
+
logsListSessionsTool.name,
|
|
210
|
+
{
|
|
211
|
+
description: logsListSessionsTool.description,
|
|
212
|
+
inputSchema: logsListSessionsInputSchema,
|
|
213
|
+
},
|
|
214
|
+
(args: LogsListSessionsInput) => {
|
|
215
|
+
return logsListSessionsHandler(storage, args).then((r) => ({
|
|
216
|
+
content: [
|
|
217
|
+
{
|
|
218
|
+
type: "text" as const,
|
|
219
|
+
text: JSON.stringify(r, null, 2),
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
}));
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
registeredTools.push(logsListSessionsTool.name);
|
|
226
|
+
|
|
227
|
+
// Register logs_receive tool
|
|
228
|
+
server.registerTool(
|
|
229
|
+
logsReceiveTool.name,
|
|
230
|
+
{
|
|
231
|
+
description: logsReceiveTool.description,
|
|
232
|
+
inputSchema: logsReceiveInputSchema,
|
|
233
|
+
},
|
|
234
|
+
(args: LogsReceiveInput) => {
|
|
235
|
+
return logsReceiveHandler(storage, args).then((r) => ({
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text" as const,
|
|
239
|
+
text: JSON.stringify(r, null, 2),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
}));
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
registeredTools.push(logsReceiveTool.name);
|
|
246
|
+
|
|
247
|
+
// Register logs_get_all tool
|
|
248
|
+
server.registerTool(
|
|
249
|
+
logsGetAllTool.name,
|
|
250
|
+
{
|
|
251
|
+
description: logsGetAllTool.description,
|
|
252
|
+
inputSchema: logsGetAllInputSchema,
|
|
253
|
+
},
|
|
254
|
+
(args: LogsGetAllInput) => {
|
|
255
|
+
return logsGetAllHandler(storage, args).then((r) => ({
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: "text" as const,
|
|
259
|
+
text: JSON.stringify(r, null, 2),
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
}));
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
registeredTools.push(logsGetAllTool.name);
|
|
266
|
+
|
|
267
|
+
// Register logs_get_errors tool
|
|
268
|
+
server.registerTool(
|
|
269
|
+
logsGetErrorsTool.name,
|
|
270
|
+
{
|
|
271
|
+
description: logsGetErrorsTool.description,
|
|
272
|
+
inputSchema: logsGetErrorsInputSchema,
|
|
273
|
+
},
|
|
274
|
+
(args: LogsGetErrorsInput) => {
|
|
275
|
+
return logsGetErrorsHandler(storage, args).then((r) => ({
|
|
276
|
+
content: [
|
|
277
|
+
{
|
|
278
|
+
type: "text" as const,
|
|
279
|
+
text: JSON.stringify(r, null, 2),
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
}));
|
|
283
|
+
},
|
|
284
|
+
);
|
|
285
|
+
registeredTools.push(logsGetErrorsTool.name);
|
|
286
|
+
|
|
287
|
+
// Register logs_clear tool
|
|
288
|
+
server.registerTool(
|
|
289
|
+
logsClearTool.name,
|
|
290
|
+
{
|
|
291
|
+
description: logsClearTool.description,
|
|
292
|
+
inputSchema: logsClearInputSchema,
|
|
293
|
+
},
|
|
294
|
+
(args: LogsClearInput) => {
|
|
295
|
+
return logsClearHandler(storage, args).then((r) => ({
|
|
296
|
+
content: [
|
|
297
|
+
{
|
|
298
|
+
type: "text" as const,
|
|
299
|
+
text: JSON.stringify(r, null, 2),
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
}));
|
|
303
|
+
},
|
|
304
|
+
);
|
|
305
|
+
registeredTools.push(logsClearTool.name);
|
|
306
|
+
|
|
307
|
+
// Register logs_search tool
|
|
308
|
+
server.registerTool(
|
|
309
|
+
logsSearchTool.name,
|
|
310
|
+
{
|
|
311
|
+
description: logsSearchTool.description,
|
|
312
|
+
inputSchema: logsSearchInputSchema,
|
|
313
|
+
},
|
|
314
|
+
(args: LogsSearchInput) => {
|
|
315
|
+
return logsSearchHandler(storage, args).then((r) => ({
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: "text" as const,
|
|
319
|
+
text: JSON.stringify(r, null, 2),
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
}));
|
|
323
|
+
},
|
|
324
|
+
);
|
|
325
|
+
registeredTools.push(logsSearchTool.name);
|
|
326
|
+
|
|
327
|
+
// Register logs_get_file_path tool
|
|
328
|
+
server.registerTool(
|
|
329
|
+
logsGetFilePathTool.name,
|
|
330
|
+
{
|
|
331
|
+
description: logsGetFilePathTool.description,
|
|
332
|
+
inputSchema: logsGetFilePathInputSchema,
|
|
333
|
+
},
|
|
334
|
+
(args: LogsGetFilePathInput) => {
|
|
335
|
+
return logsGetFilePathHandler(storage, args).then((r) => ({
|
|
336
|
+
content: [
|
|
337
|
+
{
|
|
338
|
+
type: "text" as const,
|
|
339
|
+
text: JSON.stringify(r, null, 2),
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
}));
|
|
343
|
+
},
|
|
344
|
+
);
|
|
345
|
+
registeredTools.push(logsGetFilePathTool.name);
|
|
346
|
+
|
|
347
|
+
return server;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Start the MCP server with STDIO transport.
|
|
352
|
+
*
|
|
353
|
+
* This function runs the MCP server and blocks until the connection is closed.
|
|
354
|
+
* @param storage - The LogStorage instance to use for log operations
|
|
355
|
+
*/
|
|
356
|
+
export async function startMcpServer(storage: LogStorage): Promise<void> {
|
|
357
|
+
const server = createMcpServer(storage);
|
|
358
|
+
const transport = new StdioServerTransport();
|
|
359
|
+
|
|
360
|
+
await server.connect(transport);
|
|
361
|
+
|
|
362
|
+
// Keep the process alive
|
|
363
|
+
// The connection will handle cleanup when stdin closes
|
|
364
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool exports and registration.
|
|
3
|
+
* @module mcp/tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
logsClearHandler,
|
|
8
|
+
type LogsClearInput,
|
|
9
|
+
logsClearInputSchema,
|
|
10
|
+
type LogsClearOutput,
|
|
11
|
+
logsClearTool,
|
|
12
|
+
} from "./logs-clear.js";
|
|
13
|
+
export {
|
|
14
|
+
logsGetAllHandler,
|
|
15
|
+
type LogsGetAllInput,
|
|
16
|
+
logsGetAllInputSchema,
|
|
17
|
+
type LogsGetAllOutput,
|
|
18
|
+
logsGetAllTool,
|
|
19
|
+
} from "./logs-get-all.js";
|
|
20
|
+
export {
|
|
21
|
+
logsGetErrorsHandler,
|
|
22
|
+
type LogsGetErrorsInput,
|
|
23
|
+
logsGetErrorsInputSchema,
|
|
24
|
+
type LogsGetErrorsOutput,
|
|
25
|
+
logsGetErrorsTool,
|
|
26
|
+
} from "./logs-get-errors.js";
|
|
27
|
+
export {
|
|
28
|
+
getLogFilePath,
|
|
29
|
+
logsGetFilePathHandler,
|
|
30
|
+
type LogsGetFilePathInput,
|
|
31
|
+
logsGetFilePathInputSchema,
|
|
32
|
+
type LogsGetFilePathOutput,
|
|
33
|
+
logsGetFilePathTool,
|
|
34
|
+
} from "./logs-get-file-path.js";
|
|
35
|
+
export {
|
|
36
|
+
logsGetRecentHandler,
|
|
37
|
+
type LogsGetRecentInput,
|
|
38
|
+
logsGetRecentInputSchema,
|
|
39
|
+
type LogsGetRecentOutput,
|
|
40
|
+
logsGetRecentTool,
|
|
41
|
+
} from "./logs-get-recent.js";
|
|
42
|
+
export {
|
|
43
|
+
logsListSessionsHandler,
|
|
44
|
+
type LogsListSessionsInput,
|
|
45
|
+
logsListSessionsInputSchema,
|
|
46
|
+
type LogsListSessionsOutput,
|
|
47
|
+
logsListSessionsTool,
|
|
48
|
+
} from "./logs-list-sessions.js";
|
|
49
|
+
export {
|
|
50
|
+
logsReceiveHandler,
|
|
51
|
+
type LogsReceiveInput,
|
|
52
|
+
logsReceiveInputSchema,
|
|
53
|
+
type LogsReceiveOutput,
|
|
54
|
+
logsReceiveTool,
|
|
55
|
+
} from "./logs-receive.js";
|
|
56
|
+
export {
|
|
57
|
+
logsSearchHandler,
|
|
58
|
+
type LogsSearchInput,
|
|
59
|
+
logsSearchInputSchema,
|
|
60
|
+
type LogsSearchOutput,
|
|
61
|
+
logsSearchTool,
|
|
62
|
+
} from "./logs-search.js";
|
|
63
|
+
export {
|
|
64
|
+
logsStatusHandler,
|
|
65
|
+
type LogsStatusInput,
|
|
66
|
+
logsStatusInputSchema,
|
|
67
|
+
type LogsStatusOutput,
|
|
68
|
+
logsStatusTool,
|
|
69
|
+
} from "./logs-status.js";
|