Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 10 additions & 48 deletions src/mcp/tools/fileSystemReadDirectoryTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { logger } from '../../shared/logger.js';
import { buildMcpToolErrorResponse, buildMcpToolSuccessResponse } from './mcpToolRuntime.js';

/**
* Register file system directory listing tool
Expand All @@ -28,41 +29,19 @@

// Ensure path is absolute
if (!path.isAbsolute(directoryPath)) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Path must be absolute. Received: ${directoryPath}`,
},
],
};
return buildMcpToolErrorResponse([`Error: Path must be absolute. Received: ${directoryPath}`]);
}

// Check if directory exists
try {
const stats = await fs.stat(directoryPath);
if (!stats.isDirectory()) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: The specified path is not a directory: ${directoryPath}. Use file_system_read_file for files.`,
},
],
};
return buildMcpToolErrorResponse([
`Error: The specified path is not a directory: ${directoryPath}. Use file_system_read_file for files.`,
]);

Check warning on line 41 in src/mcp/tools/fileSystemReadDirectoryTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadDirectoryTool.ts#L39-L41

Added lines #L39 - L41 were not covered by tests
}
} catch {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Directory not found at path: ${directoryPath}`,
},
],
};
return buildMcpToolErrorResponse([`Error: Directory not found at path: ${directoryPath}`]);
}

// Read directory contents
Expand All @@ -71,29 +50,12 @@
.map((entry) => `${entry.isDirectory() ? '[DIR]' : '[FILE]'} ${entry.name}`)
.join('\n');

return {
content: [
{
type: 'text',
text: `Contents of ${directoryPath}:`,
},
{
type: 'text',
text: formatted || '(empty directory)',
},
],
};
return buildMcpToolSuccessResponse([`Contents of ${directoryPath}:`, formatted || '(empty directory)']);
} catch (error) {
logger.error(`Error in file_system_read_directory tool: ${error}`);
return {
isError: true,
content: [
{
type: 'text',
text: `Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
return buildMcpToolErrorResponse([
`Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
]);

Check warning on line 58 in src/mcp/tools/fileSystemReadDirectoryTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadDirectoryTool.ts#L56-L58

Added lines #L56 - L58 were not covered by tests
}
},
);
Expand Down
70 changes: 13 additions & 57 deletions src/mcp/tools/fileSystemReadFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import type { SuspiciousFileResult } from '../../core/security/securityCheck.js';
import { createSecretLintConfig, runSecretLint } from '../../core/security/workers/securityCheckWorker.js';
import { logger } from '../../shared/logger.js';
import { buildMcpToolErrorResponse, buildMcpToolSuccessResponse } from './mcpToolRuntime.js';

/**
* Register file system read file tool with security checks
Expand All @@ -30,44 +31,22 @@

// Ensure path is absolute
if (!path.isAbsolute(filePath)) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Path must be absolute. Received: ${filePath}`,
},
],
};
return buildMcpToolErrorResponse([`Error: Path must be absolute. Received: ${filePath}`]);
}

// Check if file exists
try {
await fs.access(filePath);
} catch {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: File not found at path: ${filePath}`,
},
],
};
return buildMcpToolErrorResponse([`Error: File not found at path: ${filePath}`]);
}

// Check if it's a directory
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: The specified path is a directory, not a file: ${filePath}. Use file_system_read_directory for directories.`,
},
],
};
return buildMcpToolErrorResponse([
`Error: The specified path is a directory, not a file: ${filePath}. Use file_system_read_directory for directories.`,
]);

Check warning on line 49 in src/mcp/tools/fileSystemReadFileTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadFileTool.ts#L47-L49

Added lines #L47 - L49 were not covered by tests
}

// Read file content
Expand All @@ -79,40 +58,17 @@

// If security check found issues, block the file
if (securityCheckResult !== null) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Security check failed. The file at ${filePath} may contain sensitive information.`,
},
],
};
return buildMcpToolErrorResponse([
`Error: Security check failed. The file at ${filePath} may contain sensitive information.`,
]);

Check warning on line 63 in src/mcp/tools/fileSystemReadFileTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadFileTool.ts#L61-L63

Added lines #L61 - L63 were not covered by tests
}

return {
content: [
{
type: 'text',
text: `Content of ${filePath}:`,
},
{
type: 'text',
text: fileContent,
},
],
};
return buildMcpToolSuccessResponse([`Content of ${filePath}:`, fileContent]);

Check warning on line 66 in src/mcp/tools/fileSystemReadFileTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadFileTool.ts#L66

Added line #L66 was not covered by tests
} catch (error) {
logger.error(`Error in file_system_read_file tool: ${error}`);
return {
isError: true,
content: [
{
type: 'text',
text: `Error reading file: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
return buildMcpToolErrorResponse([
`Error reading file: ${error instanceof Error ? error.message : String(error)}`,
]);

Check warning on line 71 in src/mcp/tools/fileSystemReadFileTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/fileSystemReadFileTool.ts#L69-L71

Added lines #L69 - L71 were not covered by tests
}
},
);
Expand Down
75 changes: 18 additions & 57 deletions src/mcp/tools/grepRepomixOutputTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { logger } from '../../shared/logger.js';
import { getOutputFilePath } from './mcpToolRuntime.js';
import { buildMcpToolErrorResponse, buildMcpToolSuccessResponse, getOutputFilePath } from './mcpToolRuntime.js';

/**
* Search options for grep functionality
Expand Down Expand Up @@ -83,29 +83,17 @@

const filePath = getOutputFilePath(outputId);
if (!filePath) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Output file with ID ${outputId} not found. The output file may have been deleted or the ID is invalid.`,
},
],
};
return buildMcpToolErrorResponse([
`Error: Output file with ID ${outputId} not found. The output file may have been deleted or the ID is invalid.`,
]);
}

try {
await fs.access(filePath);
} catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: Output file does not exist at path: ${filePath}. The temporary file may have been cleaned up.`,
},
],
};
return buildMcpToolErrorResponse([
`Error: Output file does not exist at path: ${filePath}. The temporary file may have been cleaned up.`,
]);
}

const content = await fs.readFile(filePath, 'utf8');
Expand All @@ -125,51 +113,24 @@
ignoreCase,
});
} catch (error) {
return {
isError: true,
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
return buildMcpToolErrorResponse([`Error: ${error instanceof Error ? error.message : String(error)}`]);
}

if (searchResult.matches.length === 0) {
return {
content: [
{
type: 'text',
text: `No matches found for pattern "${pattern}" in Repomix output file (ID: ${outputId}).`,
},
],
};
return buildMcpToolSuccessResponse([
`No matches found for pattern "${pattern}" in Repomix output file (ID: ${outputId}).`,
]);
}

return {
content: [
{
type: 'text',
text: `Found ${searchResult.matches.length} match(es) for pattern "${pattern}" in Repomix output file (ID: ${outputId}):`,
},
{
type: 'text',
text: searchResult.formattedOutput.join('\n'),
},
],
};
return buildMcpToolSuccessResponse([
`Found ${searchResult.matches.length} match(es) for pattern "${pattern}" in Repomix output file (ID: ${outputId}):`,
searchResult.formattedOutput.join('\n'),
]);
} catch (error) {
logger.error(`Error in grep_repomix_output: ${error}`);
return {
isError: true,
content: [
{
type: 'text',
text: `Error searching Repomix output: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
return buildMcpToolErrorResponse([
`Error searching Repomix output: ${error instanceof Error ? error.message : String(error)}`,
]);

Check warning on line 133 in src/mcp/tools/grepRepomixOutputTool.ts

View check run for this annotation

Codecov / codecov/patch

src/mcp/tools/grepRepomixOutputTool.ts#L131-L133

Added lines #L131 - L133 were not covered by tests
}
},
);
Expand Down
25 changes: 25 additions & 0 deletions src/mcp/tools/mcpToolRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,28 @@ export const formatToolError = (error: unknown): CallToolResult => {
],
};
};

/**
* Creates a successful MCP tool response with type safety
*/
export const buildMcpToolSuccessResponse = (messages: string[]): CallToolResult => {
return {
content: messages.map((message) => ({
type: 'text' as const,
text: message,
})),
};
};

/**
* Creates an error MCP tool response with type safety
*/
export const buildMcpToolErrorResponse = (errorMessages: string[]): CallToolResult => {
return {
isError: true,
content: errorMessages.map((message) => ({
type: 'text' as const,
text: message,
})),
};
};
24 changes: 7 additions & 17 deletions src/mcp/tools/packCodebaseTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { runCli } from '../../cli/cliRun.js';
import type { CliOptions } from '../../cli/types.js';
import { createToolWorkspace, formatToolError, formatToolResponse } from './mcpToolRuntime.js';
import {
buildMcpToolErrorResponse,
createToolWorkspace,
formatToolError,
formatToolResponse,
} from './mcpToolRuntime.js';

export const registerPackCodebaseTool = (mcpServer: McpServer) => {
mcpServer.tool(
Expand Down Expand Up @@ -65,22 +70,7 @@ export const registerPackCodebaseTool = (mcpServer: McpServer) => {

const result = await runCli(['.'], directory, cliOptions);
if (!result) {
return {
isError: true,
content: [
{
type: 'text',
text: JSON.stringify(
{
success: false,
error: 'Failed to return a result',
},
null,
2,
),
},
],
};
return buildMcpToolErrorResponse(['Failed to return a result']);
}

// Extract metrics information from the pack result
Expand Down
Loading
Loading