@opencraw/mcp 0.1.3 → 0.1.4

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 CHANGED
@@ -109,6 +109,21 @@ call, for checking what already exists before writing a new recipe.
109
109
 
110
110
  Returns `{ outputs: [{ path, id }], inputs: [{ path, id, output, mode }], others: string[] }`.
111
111
 
112
+ ### `diff`
113
+
114
+ Compares two runs' JSON Lines files by record key: what was added, what was removed, and which fields changed,
115
+ before and after. For "what changed in this price list since last week" without re-reading it all.
116
+
117
+ | Input | Meaning |
118
+ |---|---|
119
+ | `previous`, `current` | The two runs' files (a `run`'s `out`). |
120
+ | `key?` | The fields that identify a record (the output recipe's key fields). Default: each line's `_key`, which a run with `append` writes. |
121
+ | `ignore?` | Fields not compared, such as a `scrapedAt` the crawl stamps. |
122
+
123
+ Returns `{ added, removed, changed, unchanged, shrunk?, summary, changes }`: `summary` is the report a person
124
+ reads, `changes` the first 200 changes (`removed`, `changed` with `fields: [{ field, before, after }]`, `added`),
125
+ and `shrunk` is set when the current run holds under half the records the previous one did.
126
+
112
127
  ## Building
113
128
 
114
129
  `nx build @opencraw/mcp`, `nx test @opencraw/mcp` (unit tests per tool), `nx run @opencraw/mcp:e2e`
package/dist/index.esm.js CHANGED
@@ -1,8 +1,60 @@
1
1
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { readRecipeFiles, probeUrl, loadForRun, loadPlugins, resolveAccess } from '@opencraw/cli';
3
+ import { readRecordsFile, diffRecords, memorySink, jsonLinesSink, createCrawler, RecipeSet, RecipeValidationError, RecipeBindingError, parseOutputRecipe, parseInputRecipe, bindRecipeSet } from '@opencraw/core';
4
+ import { diffReport, readRecipeFiles, probeUrl, loadForRun, loadPlugins, resolveAccess } from '@opencraw/cli';
4
5
  import { z } from 'zod';
5
- import { memorySink, jsonLinesSink, createCrawler, RecipeSet, RecipeValidationError, RecipeBindingError, parseOutputRecipe, parseInputRecipe, bindRecipeSet } from '@opencraw/core';
6
+
7
+ /** Changes returned at most; the counts cover all of them. */
8
+ const CHANGE_LIMIT = 200;
9
+ /** Input schema for the `diff` tool. */
10
+ const diffInputShape = {
11
+ previous: z.string().describe('The earlier run\'s JSON Lines file (a run\'s "out").'),
12
+ current: z.string().describe('The later run\'s JSON Lines file.'),
13
+ key: z.array(z.string()).optional().describe('The fields that identify a record (the output recipe\'s key fields). Default: each line\'s _key, which an appending run writes.'),
14
+ ignore: z.array(z.string()).optional().describe('Fields left out of the comparison, such as a scrapedAt the crawl stamps.')
15
+ };
16
+ /**
17
+ * The `diff` tool: compares two runs' records by key, so an agent watching a
18
+ * source can say what changed since last time instead of re-reading it all.
19
+ *
20
+ * @param args - The tool's parsed input.
21
+ * @returns The MCP tool result.
22
+ */
23
+ async function diffTool(args) {
24
+ try {
25
+ const [previous, current] = await Promise.all([readRecordsFile(args.previous), readRecordsFile(args.current)]);
26
+ const diff = diffRecords(previous, current, {
27
+ key: args.key,
28
+ ignore: args.ignore
29
+ });
30
+ const result = {
31
+ added: diff.added,
32
+ removed: diff.removed,
33
+ changed: diff.changed,
34
+ unchanged: diff.unchanged,
35
+ ...(diff.shrunk !== undefined && {
36
+ shrunk: diff.shrunk
37
+ }),
38
+ summary: diffReport(diff),
39
+ changes: diff.changes.slice(0, CHANGE_LIMIT)
40
+ };
41
+ return {
42
+ content: [{
43
+ type: 'text',
44
+ text: JSON.stringify(result)
45
+ }],
46
+ structuredContent: result
47
+ };
48
+ } catch (error) {
49
+ return {
50
+ content: [{
51
+ type: 'text',
52
+ text: error instanceof Error ? error.message : String(error)
53
+ }],
54
+ isError: true
55
+ };
56
+ }
57
+ }
6
58
 
7
59
  /** Input schema for the `list_recipes` tool. */
8
60
  const listRecipesInputShape = {
@@ -342,7 +394,7 @@ const SERVER_INFO = {
342
394
  };
343
395
  /**
344
396
  * Builds the MCP server: one tool per crawl primitive (`probe`, `validate`,
345
- * `run`, `list_recipes`), each a thin wrapper over `@opencraw/core` and
397
+ * `run`, `list_recipes`, `diff`), each a thin wrapper over `@opencraw/core` and
346
398
  * `@opencraw/cli`'s structured helpers. No transport is attached; `main`
347
399
  * connects it over stdio.
348
400
  *
@@ -366,6 +418,10 @@ function createServer() {
366
418
  description: 'List the recipe files in a directory, split by kind, with their ids. Check before writing a new recipe, or to see what an existing output recipe covers.',
367
419
  inputSchema: listRecipesInputShape
368
420
  }, args => listRecipesTool(args));
421
+ server.registerTool('diff', {
422
+ description: 'Compare two runs\' JSON Lines files by record key: what was added, removed, and changed (each field before and after). Use to report what changed in a source since the last crawl.',
423
+ inputSchema: diffInputShape
424
+ }, args => diffTool(args));
369
425
  return server;
370
426
  }
371
427
 
@@ -379,5 +435,5 @@ async function main() {
379
435
  await server.connect(new StdioServerTransport());
380
436
  }
381
437
 
382
- export { createServer, listRecipesInputShape, listRecipesTool, main, probeInputShape, probeTool, runInputShape, runTool, validateInputShape, validateTool };
438
+ export { createServer, diffInputShape, diffTool, listRecipesInputShape, listRecipesTool, main, probeInputShape, probeTool, runInputShape, runTool, validateInputShape, validateTool };
383
439
  //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1,42 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { RecordChange } from '@opencraw/core';
3
+ import { z } from 'zod';
4
+ /** Input schema for the `diff` tool. */
5
+ export declare const diffInputShape: {
6
+ previous: z.ZodString;
7
+ current: z.ZodString;
8
+ key: z.ZodOptional<z.ZodArray<z.ZodString>>;
9
+ ignore: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
+ };
11
+ interface DiffArgs {
12
+ previous: string;
13
+ current: string;
14
+ key?: string[];
15
+ ignore?: string[];
16
+ }
17
+ /** What the `diff` tool returns. */
18
+ export interface DiffResult {
19
+ added: number;
20
+ removed: number;
21
+ changed: number;
22
+ unchanged: number;
23
+ /** Set when the current run lost most of the previous run's records. */
24
+ shrunk?: {
25
+ previous: number;
26
+ current: number;
27
+ };
28
+ /** The report a person reads, one line per change. */
29
+ summary: string[];
30
+ /** The changes, up to 200: removed, changed (with each field's before and after), added. */
31
+ changes: RecordChange[];
32
+ }
33
+ /**
34
+ * The `diff` tool: compares two runs' records by key, so an agent watching a
35
+ * source can say what changed since last time instead of re-reading it all.
36
+ *
37
+ * @param args - The tool's parsed input.
38
+ * @returns The MCP tool result.
39
+ */
40
+ export declare function diffTool(args: DiffArgs): Promise<CallToolResult>;
41
+ export {};
42
+ //# sourceMappingURL=diff-tool.handler.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { diffTool, diffInputShape } from './diff-tool.handler.js';
2
+ export type { DiffResult } from './diff-tool.handler.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -2,6 +2,8 @@ export { main } from './main.js';
2
2
  export { createServer } from './server/index.js';
3
3
  export { probeTool, probeInputShape } from './probe-tool/index.js';
4
4
  export { validateTool, validateInputShape } from './validate-tool/index.js';
5
+ export { diffTool, diffInputShape } from './diff-tool/index.js';
6
+ export type { DiffResult } from './diff-tool/index.js';
5
7
  export type { ValidateResult } from './validate-tool/index.js';
6
8
  export { runTool, runInputShape } from './run-tool/index.js';
7
9
  export type { RunResult } from './run-tool/index.js';
@@ -1,7 +1,7 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  /**
3
3
  * Builds the MCP server: one tool per crawl primitive (`probe`, `validate`,
4
- * `run`, `list_recipes`), each a thin wrapper over `@opencraw/core` and
4
+ * `run`, `list_recipes`, `diff`), each a thin wrapper over `@opencraw/core` and
5
5
  * `@opencraw/cli`'s structured helpers. No transport is attached; `main`
6
6
  * connects it over stdio.
7
7
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencraw/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.esm.js",
6
6
  "module": "./dist/index.esm.js",
@@ -24,8 +24,8 @@
24
24
  "!**/*.js.map"
25
25
  ],
26
26
  "dependencies": {
27
- "@opencraw/core": "^0.1.3",
28
- "@opencraw/cli": "^0.1.3",
27
+ "@opencraw/core": "^0.1.4",
28
+ "@opencraw/cli": "^0.1.4",
29
29
  "@modelcontextprotocol/sdk": "^1.30.1",
30
30
  "playwright": "^1.63.0",
31
31
  "zod": "^4.6.5"