import path from 'path';
// Removed unused import: import { fileURLToPath } from 'url';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import fs from 'node:fs';
// Determine the project root:
// 1. Check for --root command line argument (highest priority)
// 2. Check for PDF_READER_MCP_ROOT environment variable
// 3. Fall back to process.cwd() if neither is set
const getProjectRoot = () => {
// Check command line arguments first
const args = process.argv.slice(2);
const rootArgIndex = args.indexOf('--root');
if (rootArgIndex !== -1 && rootArgIndex + 1 < args.length) {
const rootArg = args[rootArgIndex + 1];
if (rootArg) {
const resolvedRootArg = path.resolve(rootArg);
if (!fs.existsSync(resolvedRootArg)) {
console.error(
`[PDF Reader MCP] Warning: --root="${rootArg}" resolved to "${resolvedRootArg}" but directory does not exist. Checking environment variable...`
);
} else if (!fs.statSync(resolvedRootArg).isDirectory()) {
console.error(
`[PDF Reader MCP] Warning: --root="${rootArg}" resolved to "${resolvedRootArg}" but it is not a directory. Checking environment variable...`
);
} else {
console.error(
`[PDF Reader MCP - pathUtils] Using --root command line argument: ${resolvedRootArg}`
);
return resolvedRootArg;
}
}
}
// Check environment variable
const envRoot = process.env['PDF_READER_MCP_ROOT'];
if (envRoot) {
const resolvedEnvRoot = path.resolve(envRoot);
// Verify the directory exists
if (!fs.existsSync(resolvedEnvRoot)) {
console.error(
`[PDF Reader MCP] Warning: PDF_READER_MCP_ROOT="${envRoot}" resolved to "${resolvedEnvRoot}" but directory does not exist. Falling back to process.cwd().`
);
return process.cwd();
}
if (!fs.statSync(resolvedEnvRoot).isDirectory()) {
console.error(
`[PDF Reader MCP] Warning: PDF_READER_MCP_ROOT="${envRoot}" resolved to "${resolvedEnvRoot}" but it is not a directory. Falling back to process.cwd().`
);
return process.cwd();
}
console.error(
`[PDF Reader MCP - pathUtils] Using PDF_READER_MCP_ROOT environment variable: ${resolvedEnvRoot}`
);
return resolvedEnvRoot;
}
// Fall back to process.cwd()
const cwd = process.cwd();
console.error(`[PDF Reader MCP - pathUtils] Using process.cwd(): ${cwd}`);
return cwd;
};
export const PROJECT_ROOT = getProjectRoot();
console.error(`[PDF Reader MCP - pathUtils] Final Project Root: ${PROJECT_ROOT}`); // 写入 STDERR 避免污染 MCP 握手
console.error(`[PDF Reader MCP - pathUtils] process.cwd(): ${process.cwd()}`);
console.error(
`[PDF Reader MCP - pathUtils] PDF_READER_MCP_ROOT env: ${process.env['PDF_READER_MCP_ROOT'] ?? '(not set)'}`
);
console.error(`[PDF Reader MCP - pathUtils] Command line args: ${process.argv.slice(2).join(' ')}`);
/**
* Resolves a user-provided relative path against the project root,
* ensuring it stays within the project boundaries.
* Throws McpError on invalid input, absolute paths, or path traversal.
* @param userPath The relative path provided by the user.
* @returns The resolved absolute path.
*/
export const resolvePath = (userPath) => {
if (typeof userPath !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Path must be a string.');
}
const normalizedUserPath = path.normalize(userPath);
if (path.isAbsolute(normalizedUserPath)) {
throw new McpError(ErrorCode.InvalidParams, 'Absolute paths are not allowed.');
}
// Resolve against the calculated PROJECT_ROOT
const resolved = path.resolve(PROJECT_ROOT, normalizedUserPath);
// Security check: Ensure the resolved path is still within the project root
if (!resolved.startsWith(PROJECT_ROOT)) {
throw new McpError(ErrorCode.InvalidRequest, 'Path traversal detected. Access denied.');
}
// Enhanced error logging for debugging
if (!fs.existsSync(resolved)) {
console.error(`[PDF Reader MCP] Path resolution debug:`);
console.error(` - User provided path: "${userPath}"`);
console.error(` - Normalized path: "${normalizedUserPath}"`);
console.error(` - Project root: "${PROJECT_ROOT}"`);
console.error(` - Resolved absolute path: "${resolved}"`);
console.error(` - File exists: false`);
// Try to check if parent directory exists
const parentDir = path.dirname(resolved);
if (fs.existsSync(parentDir)) {
console.error(` - Parent directory exists: true`);
try {
const entries = fs.readdirSync(parentDir);
console.error(
` - Parent directory contents: ${entries.slice(0, 10).join(', ')}${entries.length > 10 ? '...' : ''}`
);
} catch (e) {
console.error(
` - Could not read parent directory: ${e instanceof Error ? e.message : String(e)}`
);
}
} else {
console.error(` - Parent directory exists: false`);
}
}
return resolved;
};