import { ReadFromProjectArgs, MCPError, ErrorCode } from '../models/types.js';
import { ProjectManager } from '../services/project_manager.js';
import { readFile, stat } from 'fs/promises';
import { join, isAbsolute } from 'path';
export async function readFromProject(args: ReadFromProjectArgs): Promise<{
content: string;
path: string;
}> {
const projectManager = new ProjectManager();
const project = await projectManager.getProject(args.projectId);
if (!project) {
throw new MCPError(ErrorCode.PROJECT_NOT_FOUND, `Project '${args.projectId}' not found`);
}
// Prevent directory traversal
if (args.filePath.includes('..') || isAbsolute(args.filePath)) {
throw new MCPError(ErrorCode.INVALID_PATH, `Invalid file path: ${args.filePath}`);
}
const fullPath = join(project.rootPath, args.filePath);
try {
const stats = await stat(fullPath);
if (!stats.isFile()) {
throw new MCPError(ErrorCode.INVALID_PATH, `Path is not a file: ${args.filePath}`);
}
let content = await readFile(fullPath, 'utf-8');
// Handle line ranges if specified
if (args.startLine !== undefined || args.endLine !== undefined) {
const lines = content.split('\n');
const start = (args.startLine || 1) - 1;
const end = args.endLine || lines.length;
content = lines.slice(start, end).join('\n');
}
// Update last accessed
project.lastAccessed = new Date().toISOString();
return {
content,
path: fullPath
};
} catch (error: any) {
if (error.status === ErrorCode.INVALID_PATH || error instanceof MCPError) {
throw error;
}
if (error.code === 'ENOENT') {
throw new MCPError(ErrorCode.FILE_NOT_FOUND, `File '${args.filePath}' not found in project`);
}
throw new MCPError(ErrorCode.INTERNAL_ERROR, `Failed to read file: ${error.message}`);
}
}