@craft-ts/log-mcp 0.7.0-beta.15
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 +41 -0
- package/dist/levels.d.ts +2 -0
- package/dist/levels.js +2 -0
- package/dist/levels.js.map +1 -0
- package/dist/log-reader.d.ts +59 -0
- package/dist/log-reader.js +115 -0
- package/dist/log-reader.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +17 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.js +68 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Logs MCP server
|
|
2
|
+
|
|
3
|
+
Exposes the JSONL files written by [`@craft-ts/log-server`](../../apps/log-server/README.md)
|
|
4
|
+
to an AI model over MCP stdio. It only reads the files — it never talks to the
|
|
5
|
+
browser or to the ingestion server.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm run logs:mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Register it with Claude Code:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
claude mcp add craft-ts-logs -- node /absolute/path/to/craft-ts/packages/log-mcp/dist/main.js
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run `npm run build --workspace @craft-ts/log-mcp` once beforehand, and set
|
|
18
|
+
`LOG_SERVER_DIR` in the MCP entry if the log server does not write to
|
|
19
|
+
`<cwd>/.logs`.
|
|
20
|
+
|
|
21
|
+
## Tools
|
|
22
|
+
|
|
23
|
+
| Tool | Description |
|
|
24
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
25
|
+
| `logs.stats` | Totals per level, per emitting host tag, known client ids, covered time range and backing files. Best first call. |
|
|
26
|
+
| `logs.search` | Filters on `text`, `level`, `from`, `correlationId`, `clientId`, `since`, `until`, `limit`. Newest first, ANDed together. |
|
|
27
|
+
| `logs.tail` | The `count` most recent entries, oldest first. |
|
|
28
|
+
| `logs.clear` | Delete every log file, including rotated ones, for a clean reproduction. |
|
|
29
|
+
|
|
30
|
+
Because the entries come from the craft `Console.*` boundary, the useful filters
|
|
31
|
+
are craft-shaped:
|
|
32
|
+
|
|
33
|
+
- `from` matches any tag in the host ancestry, e.g. `from: "UserCard"` matches
|
|
34
|
+
an entry emitted with `from: ["App", "UserCard"]`.
|
|
35
|
+
- `correlationId` matches a substring of the serialized craft correlation
|
|
36
|
+
metadata, so one id finds every log of a single correlated flow.
|
|
37
|
+
- `text` searches the message *and* the serialized arguments.
|
|
38
|
+
|
|
39
|
+
Rotated files are read oldest first, then the active file, so ordering is stable
|
|
40
|
+
across a rotation. A truncated trailing line — the server writing while the
|
|
41
|
+
reader reads — is skipped instead of failing the read.
|
package/dist/levels.d.ts
ADDED
package/dist/levels.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levels.js","sourceRoot":"","sources":["../src/levels.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type StoredLogEntry = {
|
|
2
|
+
readonly level: string;
|
|
3
|
+
readonly message: string;
|
|
4
|
+
readonly args?: readonly unknown[];
|
|
5
|
+
readonly from?: readonly string[];
|
|
6
|
+
readonly tags?: readonly unknown[];
|
|
7
|
+
readonly trace?: string;
|
|
8
|
+
readonly correlationId?: unknown;
|
|
9
|
+
readonly timestamp: string;
|
|
10
|
+
readonly receivedAt: string;
|
|
11
|
+
readonly route?: string;
|
|
12
|
+
readonly browser?: unknown;
|
|
13
|
+
readonly clientId?: string;
|
|
14
|
+
readonly seq: number;
|
|
15
|
+
};
|
|
16
|
+
export type LogQuery = {
|
|
17
|
+
readonly level?: readonly string[];
|
|
18
|
+
/** Case-insensitive substring match on the message. */
|
|
19
|
+
readonly text?: string;
|
|
20
|
+
/** Matches when any host tag in the ancestry equals this value. */
|
|
21
|
+
readonly from?: string;
|
|
22
|
+
readonly correlationId?: string;
|
|
23
|
+
readonly clientId?: string;
|
|
24
|
+
/** Inclusive lower bound on `receivedAt`. */
|
|
25
|
+
readonly since?: string;
|
|
26
|
+
/** Inclusive upper bound on `receivedAt`. */
|
|
27
|
+
readonly until?: string;
|
|
28
|
+
readonly limit?: number;
|
|
29
|
+
};
|
|
30
|
+
export type LogStats = {
|
|
31
|
+
readonly total: number;
|
|
32
|
+
readonly byLevel: Record<string, number>;
|
|
33
|
+
readonly byFrom: Record<string, number>;
|
|
34
|
+
readonly clients: readonly string[];
|
|
35
|
+
readonly firstAt?: string;
|
|
36
|
+
readonly lastAt?: string;
|
|
37
|
+
};
|
|
38
|
+
export declare class LogReader {
|
|
39
|
+
readonly directory: string;
|
|
40
|
+
readonly fileName: string;
|
|
41
|
+
readonly maxFiles: number;
|
|
42
|
+
constructor(options: {
|
|
43
|
+
directory: string;
|
|
44
|
+
fileName?: string;
|
|
45
|
+
maxFiles?: number;
|
|
46
|
+
});
|
|
47
|
+
get filePath(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Oldest first: rotated files (highest index is oldest) then the active file.
|
|
50
|
+
*/
|
|
51
|
+
files(): readonly string[];
|
|
52
|
+
readAll(): readonly StoredLogEntry[];
|
|
53
|
+
/** Newest first, capped by `limit`. */
|
|
54
|
+
search(query?: LogQuery): readonly StoredLogEntry[];
|
|
55
|
+
/** The `count` most recent entries, oldest first so they read like a tail. */
|
|
56
|
+
tail(count?: number): readonly StoredLogEntry[];
|
|
57
|
+
stats(): LogStats;
|
|
58
|
+
clear(): number;
|
|
59
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const DEFAULT_FILE_NAME = 'app.jsonl';
|
|
4
|
+
const DEFAULT_LIMIT = 50;
|
|
5
|
+
export class LogReader {
|
|
6
|
+
directory;
|
|
7
|
+
fileName;
|
|
8
|
+
maxFiles;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.directory = options.directory;
|
|
11
|
+
this.fileName = options.fileName ?? DEFAULT_FILE_NAME;
|
|
12
|
+
this.maxFiles = options.maxFiles ?? 5;
|
|
13
|
+
}
|
|
14
|
+
get filePath() {
|
|
15
|
+
return join(this.directory, this.fileName);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Oldest first: rotated files (highest index is oldest) then the active file.
|
|
19
|
+
*/
|
|
20
|
+
files() {
|
|
21
|
+
const paths = [];
|
|
22
|
+
for (let index = this.maxFiles; index >= 1; index--) {
|
|
23
|
+
const path = join(this.directory, `${this.fileName}.${index}`);
|
|
24
|
+
if (existsSync(path))
|
|
25
|
+
paths.push(path);
|
|
26
|
+
}
|
|
27
|
+
if (existsSync(this.filePath))
|
|
28
|
+
paths.push(this.filePath);
|
|
29
|
+
return paths;
|
|
30
|
+
}
|
|
31
|
+
readAll() {
|
|
32
|
+
return this.files().flatMap((path) => parseFile(path));
|
|
33
|
+
}
|
|
34
|
+
/** Newest first, capped by `limit`. */
|
|
35
|
+
search(query = {}) {
|
|
36
|
+
const limit = query.limit ?? DEFAULT_LIMIT;
|
|
37
|
+
const matched = this.readAll().filter((entry) => matches(entry, query));
|
|
38
|
+
return matched.slice(-limit).reverse();
|
|
39
|
+
}
|
|
40
|
+
/** The `count` most recent entries, oldest first so they read like a tail. */
|
|
41
|
+
tail(count = 20) {
|
|
42
|
+
const all = this.readAll();
|
|
43
|
+
return all.slice(Math.max(0, all.length - count));
|
|
44
|
+
}
|
|
45
|
+
stats() {
|
|
46
|
+
const entries = this.readAll();
|
|
47
|
+
const byLevel = {};
|
|
48
|
+
const byFrom = {};
|
|
49
|
+
const clients = new Set();
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
byLevel[entry.level] = (byLevel[entry.level] ?? 0) + 1;
|
|
52
|
+
const host = entry.from?.[entry.from.length - 1];
|
|
53
|
+
if (host)
|
|
54
|
+
byFrom[host] = (byFrom[host] ?? 0) + 1;
|
|
55
|
+
if (entry.clientId)
|
|
56
|
+
clients.add(entry.clientId);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
total: entries.length,
|
|
60
|
+
byLevel,
|
|
61
|
+
byFrom,
|
|
62
|
+
clients: [...clients],
|
|
63
|
+
firstAt: entries[0]?.receivedAt,
|
|
64
|
+
lastAt: entries[entries.length - 1]?.receivedAt,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
clear() {
|
|
68
|
+
const paths = this.files();
|
|
69
|
+
for (const path of paths)
|
|
70
|
+
rmSync(path, { force: true });
|
|
71
|
+
return paths.length;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function parseFile(path) {
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
77
|
+
if (line.length === 0)
|
|
78
|
+
continue;
|
|
79
|
+
try {
|
|
80
|
+
entries.push(JSON.parse(line));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// A truncated trailing line can happen if the server is writing right
|
|
84
|
+
// now; skip it rather than failing the whole read.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return entries;
|
|
88
|
+
}
|
|
89
|
+
function matches(entry, query) {
|
|
90
|
+
if (query.level && query.level.length > 0 && !query.level.includes(entry.level)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (query.text) {
|
|
94
|
+
const needle = query.text.toLowerCase();
|
|
95
|
+
const haystack = `${entry.message} ${JSON.stringify(entry.args ?? [])}`;
|
|
96
|
+
if (!haystack.toLowerCase().includes(needle))
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (query.from && !(entry.from ?? []).includes(query.from))
|
|
100
|
+
return false;
|
|
101
|
+
if (query.clientId && entry.clientId !== query.clientId)
|
|
102
|
+
return false;
|
|
103
|
+
if (query.correlationId) {
|
|
104
|
+
const serialized = JSON.stringify(entry.correlationId ?? null);
|
|
105
|
+
if (!serialized.includes(query.correlationId))
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const receivedAt = Date.parse(entry.receivedAt);
|
|
109
|
+
if (query.since && receivedAt < Date.parse(query.since))
|
|
110
|
+
return false;
|
|
111
|
+
if (query.until && receivedAt > Date.parse(query.until))
|
|
112
|
+
return false;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=log-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-reader.js","sourceRoot":"","sources":["../src/log-reader.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { LogReader } from './log-reader.js';
|
|
5
|
+
import { createLogMcpServer } from './mcp-server.js';
|
|
6
|
+
// Must match the log server: same directory, same rotation depth.
|
|
7
|
+
const directory = resolve(process.env['LOG_SERVER_DIR'] ?? resolve(process.cwd(), '.logs'));
|
|
8
|
+
const maxFiles = Number(process.env['LOG_SERVER_MAX_FILES'] ?? '5');
|
|
9
|
+
const reader = new LogReader({ directory, maxFiles });
|
|
10
|
+
const server = createLogMcpServer(reader);
|
|
11
|
+
await server.connect(new StdioServerTransport());
|
|
12
|
+
const shutdown = async () => {
|
|
13
|
+
await server.close();
|
|
14
|
+
};
|
|
15
|
+
process.once('SIGINT', () => void shutdown());
|
|
16
|
+
process.once('SIGTERM', () => void shutdown());
|
|
17
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,kEAAkE;AAClE,MAAM,SAAS,GAAG,OAAO,CACvB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CACjE,CAAC;AACF,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,GAAG,CAAC,CAAC;AAEpE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;AACtD,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAE1C,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AAEjD,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;IACzC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC;AAEF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;AAC9C,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { LOG_LEVELS } from './levels.js';
|
|
4
|
+
export function createLogMcpServer(reader) {
|
|
5
|
+
const server = new McpServer({ name: 'craft-ts-logs', version: '0.1.0' });
|
|
6
|
+
server.registerTool('logs.search', {
|
|
7
|
+
description: 'Search the application logs written by the craft-ts log server. Filters combine with AND. Results are newest first. Logs come from the craft Console.* boundary, so `from` is the host tag ancestry (for example App > UserCard) and `correlationId` carries the craft correlation metadata.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
text: z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Case-insensitive substring of the message or arguments'),
|
|
14
|
+
level: z
|
|
15
|
+
.array(z.enum(LOG_LEVELS))
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Keep only these levels'),
|
|
18
|
+
from: z
|
|
19
|
+
.string()
|
|
20
|
+
.min(1)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Keep entries whose host tag ancestry contains this tag'),
|
|
23
|
+
correlationId: z.string().min(1).optional(),
|
|
24
|
+
clientId: z
|
|
25
|
+
.string()
|
|
26
|
+
.min(1)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Browser tab identifier, see logs.stats'),
|
|
29
|
+
since: z
|
|
30
|
+
.string()
|
|
31
|
+
.min(1)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('ISO date lower bound on the server receive time'),
|
|
34
|
+
until: z
|
|
35
|
+
.string()
|
|
36
|
+
.min(1)
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('ISO date upper bound on the server receive time'),
|
|
39
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
40
|
+
},
|
|
41
|
+
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
42
|
+
}, async (params) => toolResult(reader.search(params)));
|
|
43
|
+
server.registerTool('logs.tail', {
|
|
44
|
+
description: 'Return the most recent log entries, oldest first, like `tail -n`.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
count: z.number().int().positive().max(500).optional(),
|
|
47
|
+
},
|
|
48
|
+
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
49
|
+
}, async ({ count }) => toolResult(reader.tail(count ?? 20)));
|
|
50
|
+
server.registerTool('logs.stats', {
|
|
51
|
+
description: 'Summarise the stored logs: totals per level, per emitting host tag, connected client ids and the covered time range. Use it before logs.search to know what is available.',
|
|
52
|
+
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
53
|
+
}, async () => toolResult({ ...reader.stats(), files: reader.files() }));
|
|
54
|
+
server.registerTool('logs.clear', {
|
|
55
|
+
description: 'Delete every stored log file, including rotated ones. Use it to start a clean reproduction.',
|
|
56
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
57
|
+
}, async () => toolResult({ removedFiles: reader.clear() }));
|
|
58
|
+
return server;
|
|
59
|
+
}
|
|
60
|
+
function toolResult(result) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{ type: 'text', text: JSON.stringify(result, null, 2) },
|
|
64
|
+
],
|
|
65
|
+
structuredContent: { result },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@craft-ts/log-mcp",
|
|
3
|
+
"version": "0.7.0-beta.15",
|
|
4
|
+
"description": "MCP reader for CraftTS local JSONL logs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md",
|
|
9
|
+
"!dist/**/*.spec.js",
|
|
10
|
+
"!dist/**/*.spec.d.ts",
|
|
11
|
+
"!dist/**/*.spec.js.map"
|
|
12
|
+
],
|
|
13
|
+
"bin": {
|
|
14
|
+
"craft-ts-log-mcp": "dist/main.js"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"start": "npm run build && node dist/main.js",
|
|
19
|
+
"test": "vitest run --config vitest.config.mts"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
23
|
+
"zod": "4.3.6"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "20.19.9",
|
|
27
|
+
"typescript": "6.0.3",
|
|
28
|
+
"vitest": "^4.0.8"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
}
|
|
33
|
+
}
|