@j0hanz/code-review-analyst-mcp 1.2.1 → 1.4.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.
Files changed (55) hide show
  1. package/dist/index.js +12 -3
  2. package/dist/lib/context-budget.d.ts +2 -2
  3. package/dist/lib/context-budget.js +12 -6
  4. package/dist/lib/diff-budget.js +6 -2
  5. package/dist/lib/diff-cleaner.d.ts +12 -0
  6. package/dist/lib/diff-cleaner.js +51 -0
  7. package/dist/lib/diff-parser.js +31 -36
  8. package/dist/lib/diff-store.d.ts +22 -0
  9. package/dist/lib/diff-store.js +28 -0
  10. package/dist/lib/env-config.d.ts +1 -0
  11. package/dist/lib/env-config.js +9 -3
  12. package/dist/lib/errors.d.ts +1 -0
  13. package/dist/lib/errors.js +5 -5
  14. package/dist/lib/gemini-schema.js +2 -1
  15. package/dist/lib/gemini.js +135 -67
  16. package/dist/lib/model-config.d.ts +14 -2
  17. package/dist/lib/model-config.js +30 -6
  18. package/dist/lib/tool-contracts.d.ts +222 -0
  19. package/dist/lib/tool-contracts.js +289 -0
  20. package/dist/lib/tool-factory.d.ts +5 -1
  21. package/dist/lib/tool-factory.js +48 -54
  22. package/dist/lib/tool-response.js +10 -12
  23. package/dist/lib/types.d.ts +3 -3
  24. package/dist/prompts/index.js +47 -41
  25. package/dist/resources/index.d.ts +1 -1
  26. package/dist/resources/index.js +99 -18
  27. package/dist/resources/instructions.d.ts +1 -0
  28. package/dist/resources/instructions.js +69 -0
  29. package/dist/resources/server-config.d.ts +1 -0
  30. package/dist/resources/server-config.js +71 -0
  31. package/dist/resources/tool-catalog.d.ts +1 -0
  32. package/dist/resources/tool-catalog.js +39 -0
  33. package/dist/resources/tool-info.d.ts +5 -0
  34. package/dist/resources/tool-info.js +122 -0
  35. package/dist/resources/workflows.d.ts +1 -0
  36. package/dist/resources/workflows.js +72 -0
  37. package/dist/schemas/inputs.d.ts +6 -5
  38. package/dist/schemas/inputs.js +17 -29
  39. package/dist/schemas/outputs.d.ts +17 -1
  40. package/dist/schemas/outputs.js +84 -52
  41. package/dist/server.js +28 -27
  42. package/dist/tools/analyze-complexity.d.ts +2 -0
  43. package/dist/tools/analyze-complexity.js +51 -0
  44. package/dist/tools/analyze-pr-impact.js +32 -20
  45. package/dist/tools/detect-api-breaking.d.ts +2 -0
  46. package/dist/tools/detect-api-breaking.js +48 -0
  47. package/dist/tools/generate-diff.d.ts +2 -0
  48. package/dist/tools/generate-diff.js +71 -0
  49. package/dist/tools/generate-review-summary.js +34 -29
  50. package/dist/tools/generate-test-plan.js +38 -28
  51. package/dist/tools/index.js +11 -2
  52. package/dist/tools/inspect-code-quality.js +47 -36
  53. package/dist/tools/suggest-search-replace.js +34 -20
  54. package/package.json +1 -2
  55. package/dist/instructions.md +0 -149
@@ -1,18 +1,13 @@
1
1
  import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
2
2
  import { z } from 'zod';
3
+ import { getToolContract, getToolContractNames, } from '../lib/tool-contracts.js';
3
4
  const HELP_PROMPT_NAME = 'get-help';
4
5
  const HELP_PROMPT_TITLE = 'Get Help';
5
- const HELP_PROMPT_DESCRIPTION = 'Return the server usage instructions.';
6
+ const HELP_PROMPT_DESCRIPTION = 'Server instructions.';
6
7
  const REVIEW_GUIDE_PROMPT_NAME = 'review-guide';
7
8
  const REVIEW_GUIDE_PROMPT_TITLE = 'Review Guide';
8
- const REVIEW_GUIDE_PROMPT_DESCRIPTION = 'Guided workflow instructions for a specific code review tool and focus area.';
9
- const TOOLS = [
10
- 'analyze_pr_impact',
11
- 'generate_review_summary',
12
- 'inspect_code_quality',
13
- 'suggest_search_replace',
14
- 'generate_test_plan',
15
- ];
9
+ const REVIEW_GUIDE_PROMPT_DESCRIPTION = 'Workflow guide for tool/focus area.';
10
+ const TOOLS = getToolContractNames();
16
11
  const FOCUS_AREAS = [
17
12
  'security',
18
13
  'correctness',
@@ -20,26 +15,14 @@ const FOCUS_AREAS = [
20
15
  'regressions',
21
16
  'tests',
22
17
  ];
23
- const TOOL_GUIDES = {
24
- analyze_pr_impact: 'Call `analyze_pr_impact` with `diff` and `repository`. ' +
25
- 'Get severity rating and categorization.',
26
- generate_review_summary: 'Call `generate_review_summary` for a concise digest and merge recommendation.',
27
- inspect_code_quality: 'Call `inspect_code_quality` for deep review with optional file context. ' +
28
- 'Uses thinking model for complex reasoning.',
29
- suggest_search_replace: 'Call `suggest_search_replace` to generate verbatim search/replace fixes.',
30
- generate_test_plan: 'Call `generate_test_plan` to create a verification strategy.',
31
- };
18
+ const TOOL_DESCRIPTION_TEXT = 'Select tool for review guide.';
19
+ const FOCUS_DESCRIPTION_TEXT = 'Select focus area.';
32
20
  const FOCUS_AREA_GUIDES = {
33
- security: 'Audit for injection vulnerabilities, insecure data handling, broken authentication, ' +
34
- 'cryptographic failures, and OWASP Top 10 issues.',
35
- correctness: 'Check for logic errors, edge case mishandling, incorrect algorithm implementations, ' +
36
- 'and API contract violations.',
37
- performance: 'Identify algorithmic complexity issues, unnecessary allocations, blocking I/O, ' +
38
- 'and database query inefficiencies.',
39
- regressions: 'Look for changes that could break existing behavior, removed guards, altered return types, ' +
40
- 'or contract changes in public APIs.',
41
- tests: 'Assess test coverage gaps, missing edge case tests, flaky test patterns, ' +
42
- 'and untested error paths.',
21
+ security: 'Focus: Injection, auth, crypto, OWASP.',
22
+ correctness: 'Focus: Logic, edge cases, algorithms, contracts.',
23
+ performance: 'Focus: Complexity, allocations, I/O, queries.',
24
+ regressions: 'Focus: Behavior changes, guards, types, breaks.',
25
+ tests: 'Focus: Coverage, edge cases, flakes, error paths.',
43
26
  };
44
27
  function completeByPrefix(values, prefix) {
45
28
  const matches = [];
@@ -55,15 +38,23 @@ function getGuide(guides, value, fallback) {
55
38
  return guide ?? fallback(value);
56
39
  }
57
40
  function getToolGuide(tool) {
58
- return getGuide(TOOL_GUIDES, tool, (toolName) => `Use \`${toolName}\` to analyze your code changes.`);
41
+ const contract = getToolContract(tool);
42
+ if (!contract) {
43
+ return `Use \`${tool}\` to analyze your code changes.`;
44
+ }
45
+ const { thinkingBudget } = contract;
46
+ const modelLine = thinkingBudget !== undefined
47
+ ? `Model: ${contract.model} (thinking budget ${thinkingBudget}, output cap ${contract.maxOutputTokens}).`
48
+ : `Model: ${contract.model} (output cap ${contract.maxOutputTokens}).`;
49
+ return `Tool: ${contract.name}\n${modelLine}\nOutput: ${contract.outputShape}\nUse: ${contract.purpose}`;
59
50
  }
60
51
  function getFocusAreaGuide(focusArea) {
61
52
  return getGuide(FOCUS_AREA_GUIDES, focusArea, (area) => `Focus on ${area} concerns.`);
62
53
  }
63
- export function registerAllPrompts(server, instructions) {
54
+ function registerHelpPrompt(server, instructions) {
64
55
  server.registerPrompt(HELP_PROMPT_NAME, {
65
56
  title: HELP_PROMPT_TITLE,
66
- description: 'Return the server usage instructions.',
57
+ description: HELP_PROMPT_DESCRIPTION,
67
58
  }, () => ({
68
59
  description: HELP_PROMPT_DESCRIPTION,
69
60
  messages: [
@@ -76,16 +67,30 @@ export function registerAllPrompts(server, instructions) {
76
67
  },
77
68
  ],
78
69
  }));
70
+ }
71
+ function buildReviewGuideText(tool, focusArea) {
72
+ return (`# Code Review Guide\n\n` +
73
+ `## Tool: \`${tool}\`\n${getToolGuide(tool)}\n\n` +
74
+ `## Focus Area: ${focusArea}\n${getFocusAreaGuide(focusArea)}\n\n` +
75
+ `## Example: Finding → Patch\n\n` +
76
+ `Given a finding from \`inspect_code_quality\`:\n` +
77
+ `- **title:** "Uncaught promise rejection in retry loop"\n` +
78
+ `- **details:** "The catch block swallows errors without logging."\n\n` +
79
+ `Call \`suggest_search_replace\` with those values. It returns:\n` +
80
+ '```\n' +
81
+ `blocks[0].search: " } catch {\\n }"\n` +
82
+ `blocks[0].replace: " } catch (err) {\\n logger.error(err);\\n }"\n` +
83
+ '```\n\n' +
84
+ `Validate that \`blocks[].search\` matches file content verbatim before applying.\n\n` +
85
+ `> Tip: Run \`get-help\` for full server documentation.`);
86
+ }
87
+ function registerReviewGuidePrompt(server) {
79
88
  server.registerPrompt(REVIEW_GUIDE_PROMPT_NAME, {
80
89
  title: REVIEW_GUIDE_PROMPT_TITLE,
81
90
  description: REVIEW_GUIDE_PROMPT_DESCRIPTION,
82
91
  argsSchema: {
83
- tool: completable(z
84
- .string()
85
- .describe('Which review tool to use: analyze_pr_impact, generate_review_summary, etc.'), (value) => completeByPrefix(TOOLS, value)),
86
- focusArea: completable(z
87
- .string()
88
- .describe('Focus area: security, correctness, performance, regressions, or tests'), (value) => completeByPrefix(FOCUS_AREAS, value)),
92
+ tool: completable(z.string().describe(TOOL_DESCRIPTION_TEXT), (value) => completeByPrefix(TOOLS, value)),
93
+ focusArea: completable(z.string().describe(FOCUS_DESCRIPTION_TEXT), (value) => completeByPrefix(FOCUS_AREAS, value)),
89
94
  },
90
95
  }, ({ tool, focusArea }) => ({
91
96
  description: `Code review guide: ${tool} / ${focusArea}`,
@@ -94,12 +99,13 @@ export function registerAllPrompts(server, instructions) {
94
99
  role: 'user',
95
100
  content: {
96
101
  type: 'text',
97
- text: `# Code Review Guide\n\n` +
98
- `## Tool: \`${tool}\`\n${getToolGuide(tool)}\n\n` +
99
- `## Focus Area: ${focusArea}\n${getFocusAreaGuide(focusArea)}\n\n` +
100
- `> Tip: Run \`get-help\` for full server documentation.`,
102
+ text: buildReviewGuideText(tool, focusArea),
101
103
  },
102
104
  },
103
105
  ],
104
106
  }));
105
107
  }
108
+ export function registerAllPrompts(server, instructions) {
109
+ registerHelpPrompt(server, instructions);
110
+ registerReviewGuidePrompt(server);
111
+ }
@@ -1,2 +1,2 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  export declare function registerAllResources(server: McpServer, instructions: string): void;
@@ -1,23 +1,104 @@
1
- const RESOURCE_ID = 'server-instructions';
2
- const RESOURCE_URI = 'internal://instructions';
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { DIFF_RESOURCE_URI, getDiff } from '../lib/diff-store.js';
3
+ import { buildServerConfig } from './server-config.js';
4
+ import { buildToolCatalog } from './tool-catalog.js';
5
+ import { getToolInfo, getToolInfoNames } from './tool-info.js';
6
+ import { buildWorkflowGuide } from './workflows.js';
3
7
  const RESOURCE_MIME_TYPE = 'text/markdown';
4
- const RESOURCE_METADATA = {
5
- title: 'Server Instructions',
6
- description: 'Guidance for using the MCP tools effectively.',
7
- mimeType: RESOURCE_MIME_TYPE,
8
- annotations: {
9
- audience: ['assistant'],
8
+ const RESOURCE_AUDIENCE = ['assistant'];
9
+ function createMarkdownContent(uri, text) {
10
+ return { uri: uri.href, mimeType: RESOURCE_MIME_TYPE, text };
11
+ }
12
+ const STATIC_RESOURCES = [
13
+ {
14
+ id: 'server-instructions',
15
+ uri: 'internal://instructions',
16
+ title: 'Server Instructions',
17
+ description: 'Complete server usage instructions.',
10
18
  priority: 0.8,
19
+ content: () => '', // placeholder — resolved at registration time
20
+ },
21
+ {
22
+ id: 'tool-catalog',
23
+ uri: 'internal://tool-catalog',
24
+ title: 'Tool Catalog',
25
+ description: 'Tool reference: models, params, outputs, data flow.',
26
+ priority: 1.0,
27
+ content: buildToolCatalog,
28
+ },
29
+ {
30
+ id: 'workflows',
31
+ uri: 'internal://workflows',
32
+ title: 'Workflow Reference',
33
+ description: 'Recommended workflows and tool sequences.',
34
+ priority: 0.9,
35
+ content: buildWorkflowGuide,
36
+ },
37
+ {
38
+ id: 'server-config',
39
+ uri: 'internal://server-config',
40
+ title: 'Server Configuration',
41
+ description: 'Runtime configuration and limits.',
42
+ priority: 0.7,
43
+ content: buildServerConfig,
11
44
  },
12
- };
45
+ ];
46
+ function registerStaticResource(server, def, contentOverride) {
47
+ const content = contentOverride ?? def.content();
48
+ server.registerResource(def.id, def.uri, {
49
+ title: def.title,
50
+ description: def.description,
51
+ mimeType: RESOURCE_MIME_TYPE,
52
+ annotations: {
53
+ audience: RESOURCE_AUDIENCE,
54
+ priority: def.priority,
55
+ },
56
+ }, (uri) => ({ contents: [createMarkdownContent(uri, content)] }));
57
+ }
58
+ function registerToolInfoResources(server) {
59
+ const toolNames = getToolInfoNames();
60
+ server.registerResource('tool-info', new ResourceTemplate('internal://tool-info/{toolName}', {
61
+ list: undefined,
62
+ complete: {
63
+ toolName: (value) => toolNames.filter((name) => name.startsWith(value)),
64
+ },
65
+ }), {
66
+ title: 'Tool Info',
67
+ description: 'Per-tool reference: model, params, output, gotchas.',
68
+ mimeType: RESOURCE_MIME_TYPE,
69
+ annotations: {
70
+ audience: RESOURCE_AUDIENCE,
71
+ priority: 0.6,
72
+ },
73
+ }, (uri, { toolName }) => {
74
+ const name = typeof toolName === 'string' ? toolName : '';
75
+ const info = getToolInfo(name);
76
+ const text = info ?? `Unknown tool: ${name}`;
77
+ return { contents: [createMarkdownContent(uri, text)] };
78
+ });
79
+ }
80
+ function registerDiffResource(server) {
81
+ server.registerResource('diff-current', new ResourceTemplate(DIFF_RESOURCE_URI, { list: undefined }), {
82
+ title: 'Current Diff',
83
+ description: 'The most recently generated diff, cached by generate_diff. Read by all review tools automatically.',
84
+ mimeType: 'text/x-patch',
85
+ annotations: {
86
+ audience: ['assistant'],
87
+ priority: 1.0,
88
+ },
89
+ }, (uri) => {
90
+ const slot = getDiff();
91
+ const text = slot
92
+ ? `# Diff — ${slot.mode} — ${slot.generatedAt}\n# ${slot.stats.files} file(s), +${slot.stats.added} -${slot.stats.deleted}\n\n${slot.diff}`
93
+ : '# No diff cached. Call generate_diff first.';
94
+ return { contents: [{ uri: uri.href, mimeType: 'text/x-patch', text }] };
95
+ });
96
+ }
13
97
  export function registerAllResources(server, instructions) {
14
- server.registerResource(RESOURCE_ID, RESOURCE_URI, RESOURCE_METADATA, (uri) => ({
15
- contents: [
16
- {
17
- uri: uri.href,
18
- mimeType: RESOURCE_MIME_TYPE,
19
- text: instructions,
20
- },
21
- ],
22
- }));
98
+ for (const def of STATIC_RESOURCES) {
99
+ const override = def.id === 'server-instructions' ? instructions : undefined;
100
+ registerStaticResource(server, def, override);
101
+ }
102
+ registerToolInfoResources(server);
103
+ registerDiffResource(server);
23
104
  }
@@ -0,0 +1 @@
1
+ export declare function buildServerInstructions(): string;
@@ -0,0 +1,69 @@
1
+ import { getToolContracts } from '../lib/tool-contracts.js';
2
+ import { getSharedConstraints } from './tool-info.js';
3
+ const PROMPT_LIST = [
4
+ '- `get-help`: Returns these server instructions.',
5
+ '- `review-guide`: Workflow guide for a selected tool and focus area.',
6
+ ];
7
+ const RESOURCE_LIST = [
8
+ '- `internal://instructions`: This document.',
9
+ '- `internal://tool-catalog`: Tool matrix and cross-tool data flow.',
10
+ '- `internal://workflows`: Recommended multi-step tool workflows.',
11
+ '- `internal://server-config`: Runtime limits and model configuration.',
12
+ '- `internal://tool-info/{toolName}`: Per-tool contract details.',
13
+ '- `diff://current`: Cached diff from the most recent generate_diff run.',
14
+ ];
15
+ function formatParameterLine(parameter) {
16
+ const required = parameter.required ? 'required' : 'optional';
17
+ return `- \`${parameter.name}\` (${parameter.type}, ${required}; ${parameter.constraints})`;
18
+ }
19
+ function formatToolSection(contract) {
20
+ const parameterLines = contract.params.map((parameter) => formatParameterLine(parameter));
21
+ if (contract.model === 'none') {
22
+ // Synchronous built-in tool (no Gemini call)
23
+ return `### \`${contract.name}\`
24
+ - Purpose: ${contract.purpose}
25
+ - Model: \`none\` (synchronous built-in)
26
+ - Parameters:
27
+ ${parameterLines.join('\n')}
28
+ - Output shape: \`${contract.outputShape}\``;
29
+ }
30
+ const thinkingLine = contract.thinkingBudget === undefined
31
+ ? '- Thinking budget: disabled'
32
+ : `- Thinking budget: ${contract.thinkingBudget}`;
33
+ return `### \`${contract.name}\`
34
+ - Purpose: ${contract.purpose}
35
+ - Model: \`${contract.model}\`
36
+ - Timeout: ${Math.round(contract.timeoutMs / 1_000)}s
37
+ ${thinkingLine}
38
+ - Max output tokens: ${contract.maxOutputTokens}
39
+ - Parameters:
40
+ ${parameterLines.join('\n')}
41
+ - Output shape: \`${contract.outputShape}\``;
42
+ }
43
+ export function buildServerInstructions() {
44
+ const contracts = getToolContracts();
45
+ const toolNames = contracts
46
+ .map((contract) => `\`${contract.name}\``)
47
+ .join(', ');
48
+ const toolSections = contracts.map((contract) => formatToolSection(contract));
49
+ const constraintLines = getSharedConstraints().map((constraint) => `- ${constraint}`);
50
+ return `# CODE REVIEW ANALYST MCP INSTRUCTIONS
51
+
52
+ ## CORE CAPABILITY
53
+ - Domain: Gemini-powered code review analysis over unified diffs.
54
+ - Tools: ${toolNames}
55
+ - Transport: stdio with task lifecycle support.
56
+
57
+ ## PROMPTS
58
+ ${PROMPT_LIST.join('\n')}
59
+
60
+ ## RESOURCES
61
+ ${RESOURCE_LIST.join('\n')}
62
+
63
+ ## TOOL CONTRACTS
64
+ ${toolSections.join('\n\n')}
65
+
66
+ ## SHARED CONSTRAINTS
67
+ ${constraintLines.join('\n')}
68
+ `;
69
+ }
@@ -0,0 +1 @@
1
+ export declare function buildServerConfig(): string;
@@ -0,0 +1,71 @@
1
+ import { createCachedEnvInt } from '../lib/env-config.js';
2
+ import { FLASH_MODEL } from '../lib/model-config.js';
3
+ import { getToolContracts } from '../lib/tool-contracts.js';
4
+ const DEFAULT_MAX_DIFF_CHARS = 120_000;
5
+ const DEFAULT_MAX_CONTEXT_CHARS = 500_000;
6
+ const DEFAULT_MAX_CONCURRENT_CALLS = 10;
7
+ const DEFAULT_CONCURRENT_WAIT_MS = 2_000;
8
+ const DEFAULT_SAFETY_THRESHOLD = 'BLOCK_NONE';
9
+ const GEMINI_HARM_BLOCK_THRESHOLD_ENV_VAR = 'GEMINI_HARM_BLOCK_THRESHOLD';
10
+ const GEMINI_MODEL_ENV_VAR = 'GEMINI_MODEL';
11
+ const diffCharsConfig = createCachedEnvInt('MAX_DIFF_CHARS', DEFAULT_MAX_DIFF_CHARS);
12
+ const contextCharsConfig = createCachedEnvInt('MAX_CONTEXT_CHARS', DEFAULT_MAX_CONTEXT_CHARS);
13
+ const concurrentCallsConfig = createCachedEnvInt('MAX_CONCURRENT_CALLS', DEFAULT_MAX_CONCURRENT_CALLS);
14
+ const concurrentWaitConfig = createCachedEnvInt('MAX_CONCURRENT_CALLS_WAIT_MS', DEFAULT_CONCURRENT_WAIT_MS);
15
+ function getModelOverride() {
16
+ return process.env[GEMINI_MODEL_ENV_VAR] ?? FLASH_MODEL;
17
+ }
18
+ function getSafetyThreshold() {
19
+ return (process.env[GEMINI_HARM_BLOCK_THRESHOLD_ENV_VAR] ?? DEFAULT_SAFETY_THRESHOLD);
20
+ }
21
+ function formatNumber(value) {
22
+ return new Intl.NumberFormat('en-US').format(value);
23
+ }
24
+ function formatTimeout(ms) {
25
+ return `${Math.round(ms / 1_000)}s`;
26
+ }
27
+ function formatThinkingBudget(budget) {
28
+ return budget !== undefined ? formatNumber(budget) : '—';
29
+ }
30
+ export function buildServerConfig() {
31
+ const maxDiffChars = diffCharsConfig.get();
32
+ const maxContextChars = contextCharsConfig.get();
33
+ const maxConcurrent = concurrentCallsConfig.get();
34
+ const concurrentWaitMs = concurrentWaitConfig.get();
35
+ const defaultModel = getModelOverride();
36
+ const safetyThreshold = getSafetyThreshold();
37
+ const toolRows = getToolContracts()
38
+ .filter((contract) => contract.model !== 'none')
39
+ .map((contract) => {
40
+ return `| \`${contract.name}\` | \`${contract.model}\` | ${formatThinkingBudget(contract.thinkingBudget)} | ${formatTimeout(contract.timeoutMs)} | ${formatNumber(contract.maxOutputTokens)} |`;
41
+ })
42
+ .join('\n');
43
+ return `# Server Configuration
44
+
45
+ ## Input Limits
46
+
47
+ | Limit | Value | Env |
48
+ |-------|-------|-----|
49
+ | Diff limit | ${formatNumber(maxDiffChars)} chars | \`MAX_DIFF_CHARS\` |
50
+ | Context limit (inspect) | ${formatNumber(maxContextChars)} chars | \`MAX_CONTEXT_CHARS\` |
51
+ | Concurrency limit | ${maxConcurrent} | \`MAX_CONCURRENT_CALLS\` |
52
+ | Wait timeout | ${formatNumber(concurrentWaitMs)}ms | \`MAX_CONCURRENT_CALLS_WAIT_MS\` |
53
+
54
+ ## Model Assignments
55
+
56
+ Default model: \`${defaultModel}\` (override with \`GEMINI_MODEL\`)
57
+
58
+ | Tool | Model | Thinking Budget | Timeout | Max Output Tokens |
59
+ |------|-------|----------------|---------|-------------------|
60
+ ${toolRows}
61
+
62
+ ## Safety
63
+
64
+ - Harm block threshold: \`${safetyThreshold}\`
65
+ - Override with \`GEMINI_HARM_BLOCK_THRESHOLD\` (BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE)
66
+
67
+ ## API Keys
68
+
69
+ - Set \`GEMINI_API_KEY\` or \`GOOGLE_API_KEY\` environment variable (required)
70
+ `;
71
+ }
@@ -0,0 +1 @@
1
+ export declare function buildToolCatalog(): string;
@@ -0,0 +1,39 @@
1
+ import { buildCoreContextPack } from './tool-info.js';
2
+ const TOOL_CATALOG_CONTENT = `# Tool Catalog Details
3
+
4
+ ## Optional Parameters
5
+
6
+ - \`language\`: Primary language hint (auto-detects). All tools except \`suggest_search_replace\`.
7
+ - \`focusAreas\`: Focus tags (security, performance, etc.). \`inspect_code_quality\` only.
8
+ - \`maxFindings\`: Output cap (1–25). \`inspect_code_quality\` only.
9
+ - \`files\`: File context (max 20 files, 100K chars/file). \`inspect_code_quality\` only.
10
+ - \`testFramework\`: Framework hint. \`generate_test_plan\` only.
11
+ - \`maxTestCases\`: Output cap (1–30). \`generate_test_plan\` only.
12
+
13
+ ## Cross-Tool Data Flow
14
+
15
+ \`\`\`
16
+ analyze_pr_impact ──→ severity/categories ──→ triage decision
17
+
18
+ generate_review_summary ──→ overallRisk ──────┤
19
+
20
+ inspect_code_quality
21
+
22
+ findings[].title ──→ suggest_search_replace.findingTitle
23
+ findings[].explanation ──→ suggest_search_replace.findingDetails
24
+
25
+ diff ─────┴──→ generate_test_plan
26
+ \`\`\`
27
+
28
+ ## When to Use Each Tool
29
+
30
+ - **Triage**: \`analyze_pr_impact\`, \`generate_review_summary\` (Flash).
31
+ - **Inspection**: \`inspect_code_quality\` (Pro).
32
+ - **Fixes**: \`suggest_search_replace\` (one finding/call).
33
+ - **Tests**: \`generate_test_plan\`.
34
+ - **Complexity**: \`analyze_time_space_complexity\`.
35
+ - **Breaking API**: \`detect_api_breaking_changes\`.
36
+ `;
37
+ export function buildToolCatalog() {
38
+ return `${buildCoreContextPack()}\n\n${TOOL_CATALOG_CONTENT}`;
39
+ }
@@ -0,0 +1,5 @@
1
+ export declare function buildCoreContextPack(): string;
2
+ export declare function getSharedConstraints(): readonly string[];
3
+ export declare function getToolInfoNames(): string[];
4
+ export declare function getToolInfo(toolName: string): string | undefined;
5
+ export declare function getToolPurpose(toolName: string): string | undefined;
@@ -0,0 +1,122 @@
1
+ import { getToolContract, getToolContracts } from '../lib/tool-contracts.js';
2
+ const GLOBAL_CONSTRAINTS = [
3
+ 'Diff budget: <= 120K chars.',
4
+ 'Structured output: tools return both `structuredContent` and JSON text `content`.',
5
+ ];
6
+ const numberFormatter = new Intl.NumberFormat('en-US');
7
+ function formatNumber(value) {
8
+ return numberFormatter.format(value);
9
+ }
10
+ function formatTimeout(timeoutMs) {
11
+ return `${Math.round(timeoutMs / 1_000)}s`;
12
+ }
13
+ function formatThinkingBudget(thinkingBudget) {
14
+ return thinkingBudget === undefined ? '-' : formatNumber(thinkingBudget);
15
+ }
16
+ function formatOutputTokens(maxOutputTokens) {
17
+ return formatNumber(maxOutputTokens);
18
+ }
19
+ function formatParameterRow(entry) {
20
+ return `| ${entry.name} | ${entry.type} | ${entry.required ? 'Yes' : 'No'} | ${entry.constraints} | ${entry.description} |`;
21
+ }
22
+ function toToolInfoEntry(contract) {
23
+ const parameterRows = [
24
+ '| Param | Type | Required | Constraints | Description |',
25
+ '|-------|------|----------|-------------|-------------|',
26
+ ...contract.params.map((parameter) => formatParameterRow(parameter)),
27
+ ];
28
+ return {
29
+ name: contract.name,
30
+ purpose: contract.purpose,
31
+ model: contract.model,
32
+ thinkingBudget: formatThinkingBudget(contract.thinkingBudget),
33
+ timeout: formatTimeout(contract.timeoutMs),
34
+ maxOutputTokens: formatOutputTokens(contract.maxOutputTokens),
35
+ params: parameterRows.join('\n'),
36
+ outputShape: `\`${contract.outputShape}\``,
37
+ gotchas: contract.gotchas,
38
+ crossToolFlow: contract.crossToolFlow,
39
+ ...(contract.constraints
40
+ ? { constraints: contract.constraints }
41
+ : undefined),
42
+ };
43
+ }
44
+ const TOOL_INFO_ENTRIES = Object.fromEntries(getToolContracts().map((contract) => [
45
+ contract.name,
46
+ toToolInfoEntry(contract),
47
+ ]));
48
+ const TOOL_NAMES = Object.keys(TOOL_INFO_ENTRIES).sort((a, b) => a.localeCompare(b));
49
+ function collectToolConstraints(entries) {
50
+ const constraints = new Set();
51
+ for (const [toolName, entry] of Object.entries(entries)) {
52
+ for (const constraint of entry.constraints ?? []) {
53
+ constraints.add(`\`${toolName}\`: ${constraint}`);
54
+ }
55
+ }
56
+ return Array.from(constraints).sort((a, b) => a.localeCompare(b));
57
+ }
58
+ function formatToolInfo(entry) {
59
+ return `# ${entry.name}
60
+
61
+ ## Purpose
62
+ ${entry.purpose}
63
+
64
+ ## Model
65
+ \`${entry.model}\` (thinking budget: ${entry.thinkingBudget}, timeout: ${entry.timeout}, max output tokens: ${entry.maxOutputTokens})
66
+
67
+ ## Parameters
68
+ ${entry.params}
69
+
70
+ ## Output Shape
71
+ ${entry.outputShape}
72
+
73
+ ## Gotchas
74
+ ${entry.gotchas.map((g) => `- ${g}`).join('\n')}
75
+
76
+ ## Cross-Tool Flow
77
+ ${entry.crossToolFlow.map((f) => `- ${f}`).join('\n')}
78
+ `;
79
+ }
80
+ function formatCompactToolRow(entry) {
81
+ return `| \`${entry.name}\` | ${entry.model} | ${entry.timeout} | ${entry.maxOutputTokens} | ${entry.purpose} |`;
82
+ }
83
+ export function buildCoreContextPack() {
84
+ const rows = TOOL_NAMES.flatMap((toolName) => {
85
+ const entry = TOOL_INFO_ENTRIES[toolName];
86
+ return entry ? [formatCompactToolRow(entry)] : [];
87
+ });
88
+ return `# Core Context Pack
89
+
90
+ ## Server Essentials
91
+ - Domain: Gemini-powered MCP server for diff-based code review.
92
+ - Surface: 7 review tools + internal resources + guided prompts.
93
+ - Transport: stdio with task lifecycle support.
94
+
95
+ ## Tool Matrix
96
+ | Tool | Model | Timeout | Max Output Tokens | Purpose |
97
+ |------|-------|---------|-------------------|---------|
98
+ ${rows.join('\n')}
99
+
100
+ ## Shared Constraints
101
+ ${getSharedConstraints()
102
+ .map((constraint) => `- ${constraint}`)
103
+ .join('\n')}
104
+ `;
105
+ }
106
+ export function getSharedConstraints() {
107
+ const toolConstraints = collectToolConstraints(TOOL_INFO_ENTRIES);
108
+ return [...GLOBAL_CONSTRAINTS, ...toolConstraints];
109
+ }
110
+ export function getToolInfoNames() {
111
+ return TOOL_NAMES;
112
+ }
113
+ export function getToolInfo(toolName) {
114
+ const entry = TOOL_INFO_ENTRIES[toolName];
115
+ if (!entry) {
116
+ return undefined;
117
+ }
118
+ return formatToolInfo(entry);
119
+ }
120
+ export function getToolPurpose(toolName) {
121
+ return getToolContract(toolName)?.purpose;
122
+ }
@@ -0,0 +1 @@
1
+ export declare function buildWorkflowGuide(): string;
@@ -0,0 +1,72 @@
1
+ import { getToolContracts } from '../lib/tool-contracts.js';
2
+ import { getSharedConstraints } from './tool-info.js';
3
+ function buildWorkflowToolReference() {
4
+ const contracts = getToolContracts();
5
+ return contracts
6
+ .map((c) => `### \`${c.name}\`\n- **Purpose:** ${c.purpose}\n- **Model:** ${c.model}\n- **Output:** \`${c.outputShape}\``)
7
+ .join('\n\n');
8
+ }
9
+ export function buildWorkflowGuide() {
10
+ return `# Workflow Reference
11
+
12
+ ## A: Full PR Review
13
+
14
+ 1. \`generate_review_summary\` → \`{overallRisk, keyChanges[], recommendation, stats}\`
15
+ 2. \`inspect_code_quality\` → \`{findings[], overallRisk, contextualInsights[]}\`
16
+ 3. For each finding: \`suggest_search_replace\` → \`{blocks[]}\`
17
+
18
+ > One finding per \`suggest_search_replace\` call.
19
+
20
+ ## B: Impact Assessment
21
+
22
+ 1. \`analyze_pr_impact\` → \`{severity, categories[], breakingChanges[], rollbackComplexity}\`
23
+ 2. \`generate_review_summary\` → complementary merge recommendation
24
+
25
+ > Use when categorization (breaking, api) or rollback assessment needed.
26
+
27
+ ## C: Remediation Loop
28
+
29
+ 1. \`inspect_code_quality\` → \`{findings[]}\`
30
+ 2. Pick one finding. \`suggest_search_replace\` → \`{blocks[]}\`
31
+ 3. Validate \`blocks[].search\` matches file content verbatim.
32
+
33
+ > Never batch findings.
34
+
35
+ ## D: Test Coverage
36
+
37
+ 1. \`generate_test_plan\` → \`{testCases[], coverageSummary}\`
38
+ 2. Review by priority: \`must_have\` → \`should_have\` → \`nice_to_have\`
39
+
40
+ > Combine with \`inspect_code_quality\`.
41
+
42
+ ## E: Complexity & Breaking Changes
43
+
44
+ 1. \`analyze_time_space_complexity\` → \`{timeComplexity, spaceComplexity, isDegradation}\`
45
+ 2. \`detect_api_breaking_changes\` → \`{hasBreakingChanges, breakingChanges[]}\`
46
+
47
+ > Use for algorithm or API changes. Diff-only input.
48
+
49
+ ## Shared Constraints
50
+ ${getSharedConstraints()
51
+ .map((constraint) => `- ${constraint}`)
52
+ .join('\n')}
53
+
54
+ ## Tool Reference
55
+
56
+ ${buildWorkflowToolReference()}
57
+
58
+ ## Output Shape Reference
59
+
60
+ ### Finding
61
+ \`{severity, file, line, title, explanation, recommendation}\`
62
+
63
+ ### Search/Replace Block
64
+ \`{file, search, replace, explanation}\`
65
+
66
+ ### Test Case
67
+ \`{name, type, file, description, pseudoCode, priority}\`
68
+
69
+ ### Breaking Change
70
+ \`{element, natureOfChange, consumerImpact, suggestedMitigation}\`
71
+ `;
72
+ }