@j0hanz/code-review-analyst-mcp 1.4.1 → 1.4.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.
@@ -1,5 +1,6 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { ZodRawShapeCompat } from '@modelcontextprotocol/sdk/server/zod-compat.js';
3
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
4
  import { z } from 'zod';
4
5
  import { type DiffSlot } from './diff-store.js';
5
6
  import { createErrorToolResponse } from './tool-response.js';
@@ -17,6 +18,22 @@ export interface PromptParts {
17
18
  export interface ToolExecutionContext {
18
19
  readonly diffSlot: DiffSlot | undefined;
19
20
  }
21
+ type ProgressToken = string | number;
22
+ interface ProgressNotificationParams {
23
+ progressToken: ProgressToken;
24
+ progress: number;
25
+ total?: number;
26
+ message?: string;
27
+ }
28
+ interface ProgressExtra {
29
+ _meta?: {
30
+ progressToken?: unknown;
31
+ };
32
+ sendNotification: (notification: {
33
+ method: 'notifications/progress';
34
+ params: ProgressNotificationParams;
35
+ }) => Promise<void>;
36
+ }
20
37
  export interface StructuredToolTaskConfig<TInput extends object = Record<string, unknown>, TResult extends object = Record<string, unknown>, TFinal extends TResult = TResult> {
21
38
  /** Tool name registered with the MCP server (e.g. 'analyze_pr_impact'). */
22
39
  name: string;
@@ -57,4 +74,9 @@ export interface StructuredToolTaskConfig<TInput extends object = Record<string,
57
74
  /** Builds the system instruction and user prompt from parsed tool input. */
58
75
  buildPrompt: (input: TInput, ctx: ToolExecutionContext) => PromptParts;
59
76
  }
77
+ export declare function wrapToolHandler<TInput, TResult extends CallToolResult>(options: {
78
+ toolName: string;
79
+ progressContext?: (input: TInput) => string;
80
+ }, handler: (input: TInput, extra: ProgressExtra) => Promise<TResult> | TResult): (input: TInput, extra: ProgressExtra) => Promise<TResult>;
60
81
  export declare function registerStructuredToolTask<TInput extends object, TResult extends object = Record<string, unknown>, TFinal extends TResult = TResult>(server: McpServer, config: StructuredToolTaskConfig<TInput, TResult, TFinal>): void;
82
+ export {};
@@ -218,6 +218,43 @@ function createGeminiLogger(server, taskId) {
218
218
  }
219
219
  };
220
220
  }
221
+ export function wrapToolHandler(options, handler) {
222
+ return async (input, extra) => {
223
+ const context = normalizeProgressContext(options.progressContext?.(input));
224
+ // Start progress (0/1)
225
+ await sendTaskProgress(extra, {
226
+ current: 0,
227
+ total: 1,
228
+ message: formatProgressStep(options.toolName, context, 'starting'),
229
+ });
230
+ try {
231
+ const result = await handler(input, extra);
232
+ // End progress (1/1)
233
+ const outcome = result.isError ? 'failed' : 'completed';
234
+ const success = !result.isError;
235
+ await sendTaskProgress(extra, {
236
+ current: 1,
237
+ total: 1,
238
+ message: formatProgressCompletion(options.toolName, context, outcome, success),
239
+ });
240
+ return result;
241
+ }
242
+ catch (error) {
243
+ // Progress is best-effort; must never mask the original error.
244
+ try {
245
+ await sendTaskProgress(extra, {
246
+ current: 1,
247
+ total: 1,
248
+ message: formatProgressCompletion(options.toolName, context, 'failed', false),
249
+ });
250
+ }
251
+ catch {
252
+ // Swallow progress delivery errors so the original error propagates.
253
+ }
254
+ throw error;
255
+ }
256
+ };
257
+ }
221
258
  export function registerStructuredToolTask(server, config) {
222
259
  const responseSchema = createGeminiResponseSchema({
223
260
  geminiSchema: config.geminiSchema,
@@ -3,6 +3,7 @@ import { z } from 'zod';
3
3
  import { cleanDiff, isEmptyDiff, NOISY_EXCLUDE_PATHSPECS, } from '../lib/diff-cleaner.js';
4
4
  import { computeDiffStatsFromFiles, parseDiffFiles, } from '../lib/diff-parser.js';
5
5
  import { DIFF_RESOURCE_URI, storeDiff } from '../lib/diff-store.js';
6
+ import { wrapToolHandler } from '../lib/tool-factory.js';
6
7
  import { createErrorToolResponse, createToolResponse, } from '../lib/tool-response.js';
7
8
  const GIT_TIMEOUT_MS = 30_000;
8
9
  const GIT_MAX_BUFFER = 10 * 1024 * 1024; // 10 MB
@@ -29,6 +30,9 @@ export function registerGenerateDiffTool(server) {
29
30
  .enum(['unstaged', 'staged'])
30
31
  .describe('"unstaged": working-tree changes not yet staged. "staged": changes added to the index with git add.'),
31
32
  },
33
+ }, wrapToolHandler({
34
+ toolName: 'generate_diff',
35
+ progressContext: (input) => input.mode,
32
36
  }, (input) => {
33
37
  const { mode } = input;
34
38
  const args = buildGitArgs(mode);
@@ -67,5 +71,5 @@ export function registerGenerateDiffTool(server) {
67
71
  message: summary,
68
72
  },
69
73
  }, summary);
70
- });
74
+ }));
71
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/code-review-analyst-mcp",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "mcpName": "io.github.j0hanz/code-review-analyst",
5
5
  "description": "Gemini-powered MCP server for code review analysis.",
6
6
  "type": "module",