@segosolutions/mcp-server 1.0.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.
- package/README.md +233 -0
- package/dist/api/client.d.ts +202 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +171 -0
- package/dist/api/client.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +478 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/claude-file-resolver.d.ts +74 -0
- package/dist/lib/claude-file-resolver.d.ts.map +1 -0
- package/dist/lib/claude-file-resolver.js +176 -0
- package/dist/lib/claude-file-resolver.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md File Resolver Service
|
|
3
|
+
*
|
|
4
|
+
* Handles secure file path resolution and CLAUDE.md file discovery
|
|
5
|
+
* with path traversal prevention and validation.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
// Constants
|
|
10
|
+
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB hard limit
|
|
11
|
+
/**
|
|
12
|
+
* Resolve and read CLAUDE.md file
|
|
13
|
+
*
|
|
14
|
+
* @param options - Resolution options
|
|
15
|
+
* @returns Resolved file with content and metadata
|
|
16
|
+
* @throws Error if file not found or validation fails
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Auto-detect
|
|
20
|
+
* const file = await resolveClaudeFile()
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Explicit path
|
|
24
|
+
* const file = await resolveClaudeFile({ explicitPath: 'docs/CLAUDE.md' })
|
|
25
|
+
*/
|
|
26
|
+
export async function resolveClaudeFile(options = {}) {
|
|
27
|
+
const baseDir = options.baseDir || process.cwd();
|
|
28
|
+
// Strategy 1: Explicit path provided
|
|
29
|
+
if (options.explicitPath) {
|
|
30
|
+
const resolvedPath = await resolveExplicitPath(options.explicitPath, baseDir);
|
|
31
|
+
const stats = await fs.stat(resolvedPath);
|
|
32
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
33
|
+
return {
|
|
34
|
+
path: resolvedPath,
|
|
35
|
+
content,
|
|
36
|
+
foundMethod: 'explicit',
|
|
37
|
+
sizeBytes: stats.size,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Strategy 2: Auto-detect
|
|
41
|
+
const autoDetectedPath = await autoDetectClaudeFile(baseDir, options.searchDirs);
|
|
42
|
+
if (!autoDetectedPath) {
|
|
43
|
+
const searchPaths = getSearchPaths(baseDir, options.searchDirs);
|
|
44
|
+
throw new ClaudeFileNotFoundError(searchPaths);
|
|
45
|
+
}
|
|
46
|
+
const stats = await fs.stat(autoDetectedPath);
|
|
47
|
+
const content = await fs.readFile(autoDetectedPath, 'utf-8');
|
|
48
|
+
return {
|
|
49
|
+
path: autoDetectedPath,
|
|
50
|
+
content,
|
|
51
|
+
foundMethod: 'auto-detect',
|
|
52
|
+
sizeBytes: stats.size,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve explicit path with security checks
|
|
57
|
+
*
|
|
58
|
+
* Prevents path traversal attacks by ensuring the resolved path
|
|
59
|
+
* stays within the allowed baseDir.
|
|
60
|
+
*
|
|
61
|
+
* @param explicitPath - User-provided file path
|
|
62
|
+
* @param baseDir - Base directory to restrict access to
|
|
63
|
+
* @returns Absolute path to file
|
|
64
|
+
* @throws Error if path is invalid or escapes baseDir
|
|
65
|
+
*/
|
|
66
|
+
export async function resolveExplicitPath(explicitPath, baseDir) {
|
|
67
|
+
// Resolve to absolute path
|
|
68
|
+
const absolutePath = path.resolve(baseDir, explicitPath);
|
|
69
|
+
// Security: ensure path doesn't escape baseDir
|
|
70
|
+
const normalizedBase = path.normalize(baseDir);
|
|
71
|
+
const normalizedPath = path.normalize(absolutePath);
|
|
72
|
+
if (!normalizedPath.startsWith(normalizedBase)) {
|
|
73
|
+
throw new SecurityError(`Path "${explicitPath}" attempts to access files outside allowed directory`);
|
|
74
|
+
}
|
|
75
|
+
// Check file exists and is readable
|
|
76
|
+
try {
|
|
77
|
+
await fs.access(absolutePath, fs.constants.R_OK);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`File not found or not readable: ${explicitPath}`);
|
|
81
|
+
}
|
|
82
|
+
// Check it's a file (not directory)
|
|
83
|
+
const stats = await fs.stat(absolutePath);
|
|
84
|
+
if (!stats.isFile()) {
|
|
85
|
+
throw new Error(`Path is not a file: ${explicitPath}`);
|
|
86
|
+
}
|
|
87
|
+
// Check file size
|
|
88
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
89
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
90
|
+
const maxMB = (MAX_FILE_SIZE_BYTES / (1024 * 1024)).toFixed(0);
|
|
91
|
+
throw new Error(`File size (${sizeMB}MB) exceeds maximum allowed size (${maxMB}MB)`);
|
|
92
|
+
}
|
|
93
|
+
return absolutePath;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Auto-detect CLAUDE.md in common locations
|
|
97
|
+
*
|
|
98
|
+
* Searches for CLAUDE.md in standard locations:
|
|
99
|
+
* 1. ./CLAUDE.md
|
|
100
|
+
* 2. ./.claude/CLAUDE.md
|
|
101
|
+
* 3. ./claude.md (lowercase variant)
|
|
102
|
+
* 4. ./.claude/claude.md
|
|
103
|
+
*
|
|
104
|
+
* @param baseDir - Base directory to search from
|
|
105
|
+
* @param additionalDirs - Additional directories to search
|
|
106
|
+
* @returns Absolute path to CLAUDE.md or null if not found
|
|
107
|
+
*/
|
|
108
|
+
export async function autoDetectClaudeFile(baseDir, additionalDirs = []) {
|
|
109
|
+
const searchPaths = getSearchPaths(baseDir, additionalDirs);
|
|
110
|
+
for (const searchPath of searchPaths) {
|
|
111
|
+
try {
|
|
112
|
+
await fs.access(searchPath, fs.constants.R_OK);
|
|
113
|
+
const stats = await fs.stat(searchPath);
|
|
114
|
+
if (stats.isFile()) {
|
|
115
|
+
// Check file size
|
|
116
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
117
|
+
console.warn(`Found ${searchPath} but file is too large (${(stats.size / (1024 * 1024)).toFixed(2)}MB)`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
return searchPath;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// File doesn't exist, continue searching
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get list of paths to search for CLAUDE.md
|
|
132
|
+
*
|
|
133
|
+
* @param baseDir - Base directory
|
|
134
|
+
* @param additionalDirs - Additional directories to search
|
|
135
|
+
* @returns Array of paths to search
|
|
136
|
+
*/
|
|
137
|
+
function getSearchPaths(baseDir, additionalDirs = []) {
|
|
138
|
+
return [
|
|
139
|
+
path.join(baseDir, 'CLAUDE.md'),
|
|
140
|
+
path.join(baseDir, '.claude', 'CLAUDE.md'),
|
|
141
|
+
path.join(baseDir, 'claude.md'), // lowercase variant
|
|
142
|
+
path.join(baseDir, '.claude', 'claude.md'),
|
|
143
|
+
...additionalDirs.map(dir => path.join(baseDir, dir, 'CLAUDE.md')),
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get relative path for display purposes
|
|
148
|
+
*
|
|
149
|
+
* @param absolutePath - Absolute path to file
|
|
150
|
+
* @param baseDir - Base directory (defaults to CWD)
|
|
151
|
+
* @returns Relative path from baseDir
|
|
152
|
+
*/
|
|
153
|
+
export function getDisplayPath(absolutePath, baseDir) {
|
|
154
|
+
const base = baseDir || process.cwd();
|
|
155
|
+
const relativePath = path.relative(base, absolutePath);
|
|
156
|
+
return relativePath || absolutePath;
|
|
157
|
+
}
|
|
158
|
+
// Custom Error Classes
|
|
159
|
+
export class SecurityError extends Error {
|
|
160
|
+
constructor(message) {
|
|
161
|
+
super(message);
|
|
162
|
+
this.name = 'SecurityError';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export class ClaudeFileNotFoundError extends Error {
|
|
166
|
+
constructor(searchedPaths) {
|
|
167
|
+
const pathList = searchedPaths.map(p => ` - ${p}`).join('\n');
|
|
168
|
+
super(`Could not find CLAUDE.md in the following locations:\n${pathList}\n\n` +
|
|
169
|
+
`Please either:\n` +
|
|
170
|
+
` 1. Create a CLAUDE.md file in your project root, or\n` +
|
|
171
|
+
` 2. Create one in a .claude/ subdirectory, or\n` +
|
|
172
|
+
` 3. Provide explicit path using 'claudePath' parameter`);
|
|
173
|
+
this.name = 'ClaudeFileNotFoundError';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=claude-file-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-file-resolver.js","sourceRoot":"","sources":["../../src/lib/claude-file-resolver.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@segosolutions/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Sego PM - enables Claude to manage tasks via API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"sego-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsx watch src/index.ts",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"claude",
|
|
26
|
+
"task-management",
|
|
27
|
+
"project-management",
|
|
28
|
+
"sego"
|
|
29
|
+
],
|
|
30
|
+
"author": "Sego Solutions",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/sego-solutions/sego-pm"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/sego-solutions/sego-pm/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://sego.pm",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
45
|
+
"dotenv": "^16.4.5"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.12.0",
|
|
49
|
+
"tsx": "^4.10.0",
|
|
50
|
+
"typescript": "^5.4.0"
|
|
51
|
+
}
|
|
52
|
+
}
|