@ebowwa/claude-code-config-mcp 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/.claude/CLAUDE.md +3 -0
- package/README.md +237 -0
- package/bun.lock +206 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1744 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +51 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +63 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +156 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file.d.ts +32 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +146 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/path.d.ts +59 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +146 -0
- package/dist/utils/path.js.map +1 -0
- package/lmdb.db +0 -0
- package/lmdb.db-lock +0 -0
- package/package.json +43 -0
- package/src/index.js +2171 -0
- package/src/index.ts +1981 -0
- package/src/types.js +53 -0
- package/src/types.ts +237 -0
- package/src/utils/errors.js +231 -0
- package/src/utils/errors.ts +210 -0
- package/src/utils/file.js +251 -0
- package/src/utils/file.ts +174 -0
- package/src/utils/path.js +169 -0
- package/src/utils/path.ts +173 -0
- package/test/test.js +136 -0
- package/test/test.ts +79 -0
- package/test/write-test.js +153 -0
- package/test/write-test.ts +102 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Atomic File Operations - Safe config file writes with backup support
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import { writeFile, rename, unlink, readFile, mkdir, mkdtemp } from 'node:fs/promises';
|
|
5
|
+
import { existsSync, constants } from 'node:fs';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { randomBytes } from 'node:crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Atomic write implementation
|
|
11
|
+
* Writes to a temporary file, then renames over the target
|
|
12
|
+
* This is safe against crashes and concurrent access
|
|
13
|
+
*/
|
|
14
|
+
export async function atomicWrite(filePath, content, options = {}) {
|
|
15
|
+
const { createBackup = true, backupDir, encoding = 'utf8', } = options;
|
|
16
|
+
// Create parent directory if it doesn't exist
|
|
17
|
+
const parentDir = dirname(filePath);
|
|
18
|
+
await mkdir(parentDir, { recursive: true });
|
|
19
|
+
// Create backup if requested and file exists
|
|
20
|
+
if (createBackup && existsSync(filePath)) {
|
|
21
|
+
await createBackupFile(filePath, backupDir);
|
|
22
|
+
}
|
|
23
|
+
// Create temp file in OS temp directory (same filesystem for atomic rename)
|
|
24
|
+
const tempDir = await mkdtemp(join(getTempDir(), 'mcp-atomic-'));
|
|
25
|
+
const tempFileName = `${randomBytes(16).toString('hex')}.tmp`;
|
|
26
|
+
const tempFilePath = join(tempDir, tempFileName);
|
|
27
|
+
try {
|
|
28
|
+
// Write to temp file
|
|
29
|
+
await writeFile(tempFilePath, content, { encoding, mode: 0o644 });
|
|
30
|
+
// Atomic rename (overwrites target if exists)
|
|
31
|
+
await rename(tempFilePath, filePath);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
// Clean up temp file if rename failed
|
|
35
|
+
try {
|
|
36
|
+
await unlink(tempFilePath);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Ignore cleanup errors
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
// Clean up temp directory
|
|
45
|
+
try {
|
|
46
|
+
await unlink(tempDir).catch(() => { });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore cleanup errors
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a backup of a file
|
|
55
|
+
*/
|
|
56
|
+
async function createBackupFile(filePath, backupDir) {
|
|
57
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
58
|
+
const backupFileName = `${basename(filePath)}.backup.${timestamp}`;
|
|
59
|
+
const backupPath = backupDir
|
|
60
|
+
? join(backupDir, backupFileName)
|
|
61
|
+
: `${filePath}.backup`;
|
|
62
|
+
// Ensure backup directory exists
|
|
63
|
+
const backupDirPath = dirname(backupPath);
|
|
64
|
+
await mkdir(backupDirPath, { recursive: true });
|
|
65
|
+
// Copy file to backup location
|
|
66
|
+
const content = await readFile(filePath);
|
|
67
|
+
await writeFile(backupPath, content);
|
|
68
|
+
return backupPath;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get temp directory
|
|
72
|
+
*/
|
|
73
|
+
function getTempDir() {
|
|
74
|
+
return tmpdir();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get file basename without extension
|
|
78
|
+
*/
|
|
79
|
+
function basename(filePath) {
|
|
80
|
+
return filePath.split('/').pop() || filePath;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Safe file read with encoding detection
|
|
84
|
+
*/
|
|
85
|
+
export async function safeReadFile(filePath, encoding = 'utf8') {
|
|
86
|
+
try {
|
|
87
|
+
const content = await readFile(filePath, { encoding });
|
|
88
|
+
// Remove BOM if present
|
|
89
|
+
if (content.charCodeAt(0) === 0xFEFF) {
|
|
90
|
+
return content.slice(1);
|
|
91
|
+
}
|
|
92
|
+
return content;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw new Error(`Failed to read file ${filePath}: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if file is writable
|
|
100
|
+
*/
|
|
101
|
+
export async function isFileWritable(filePath) {
|
|
102
|
+
try {
|
|
103
|
+
await writeFile(filePath, '', { flag: constants.O_WRONLY | constants.O_CREAT });
|
|
104
|
+
await unlink(filePath);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Normalize line endings to LF
|
|
113
|
+
*/
|
|
114
|
+
export function normalizeLineEndings(content) {
|
|
115
|
+
return content.replace(/\r\n/g, '\n');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Detect and strip BOM
|
|
119
|
+
*/
|
|
120
|
+
export function stripBOM(content) {
|
|
121
|
+
if (content.charCodeAt(0) === 0xFEFF) {
|
|
122
|
+
return content.slice(1);
|
|
123
|
+
}
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Format JSON with proper indentation
|
|
128
|
+
*/
|
|
129
|
+
export function formatJSON(obj, indent = 2) {
|
|
130
|
+
return JSON.stringify(obj, null, indent) + '\n';
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Parse JSON with error reporting
|
|
134
|
+
*/
|
|
135
|
+
export function safeJSONParse(content, filePath) {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(content);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const message = error.message;
|
|
141
|
+
const match = message.match(/position (\d+)/);
|
|
142
|
+
const position = match ? match[1] : 'unknown';
|
|
143
|
+
throw new Error(`Invalid JSON in ${filePath} at position ${position}: ${message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/utils/file.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,uEAAuE;AACvE,+EAA+E;AAE/E,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,OAAe,EACf,UAA8B,EAAE;IAEhC,MAAM,EACJ,YAAY,GAAG,IAAI,EACnB,SAAS,EACT,QAAQ,GAAG,MAAM,GAClB,GAAG,OAAO,CAAC;IAEZ,8CAA8C;IAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,6CAA6C;IAC7C,IAAI,YAAY,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzC,MAAM,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED,4EAA4E;IAC5E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,qBAAqB;QACrB,MAAM,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAElE,8CAA8C;QAC9C,MAAM,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,0BAA0B;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAAC,QAAgB,EAAE,SAAkB;IAClE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,MAAM,cAAc,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,SAAS,EAAE,CAAC;IACnE,MAAM,UAAU,GAAG,SAAS;QAC1B,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC;QACjC,CAAC,CAAC,GAAG,QAAQ,SAAS,CAAC;IAEzB,iCAAiC;IACjC,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEhD,+BAA+B;IAC/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAErC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU;IACjB,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,WAA2B,MAAM;IAEjC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEvD,wBAAwB;QACxB,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;YACrC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACrC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,SAAiB,CAAC;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAI,OAAe,EAAE,QAAgB;IAChE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAI,KAAe,CAAC,OAAO,CAAC;QACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE9C,MAAM,IAAI,KAAK,CACb,mBAAmB,QAAQ,gBAAgB,QAAQ,KAAK,OAAO,EAAE,CAClE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get home directory cross-platform
|
|
3
|
+
*/
|
|
4
|
+
export declare function getHomeDir(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Get Claude Code global config directory (~/.claude)
|
|
7
|
+
*/
|
|
8
|
+
export declare function getClaudeConfigDir(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get Claude Code app config directory (~/.config/claude-code)
|
|
11
|
+
* This is where MCP servers are configured for the Claude Code app
|
|
12
|
+
*/
|
|
13
|
+
export declare function getClaudeAppConfigDir(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get Claude CLI config file path (~/.claude.json)
|
|
16
|
+
* This is where MCP servers are configured for the claude CLI
|
|
17
|
+
*/
|
|
18
|
+
export declare function getClaudeCliConfigPath(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Get project-local Claude Code config directory (.claude)
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProjectClaudeDir(cwd?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve Claude config file paths
|
|
25
|
+
*/
|
|
26
|
+
export declare function getConfigPaths(cwd?: string): {
|
|
27
|
+
globalClaudeMd: string;
|
|
28
|
+
keybindings: string;
|
|
29
|
+
settings: string;
|
|
30
|
+
claudeJson: string;
|
|
31
|
+
mcpServers: string;
|
|
32
|
+
hooksDir: string;
|
|
33
|
+
pluginsDir: string;
|
|
34
|
+
appConfig: string;
|
|
35
|
+
projectClaudeMd: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Validate and sanitize a file path
|
|
39
|
+
* Prevents path traversal attacks and ensures path is within allowed directories
|
|
40
|
+
*/
|
|
41
|
+
export declare function validatePath(userPath: string, cwd?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Check if a path exists
|
|
44
|
+
*/
|
|
45
|
+
export declare function pathExists(filePath: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Get relative path from home directory for display
|
|
48
|
+
*/
|
|
49
|
+
export declare function getDisplayPath(filePath: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Get MCP config file paths based on target
|
|
52
|
+
* @param target - Which config(s) to get ('cli', 'app', or 'both')
|
|
53
|
+
* @returns Object with the requested config paths
|
|
54
|
+
*/
|
|
55
|
+
export declare function getMCPConfigPaths(target?: 'cli' | 'app' | 'both'): {
|
|
56
|
+
cli?: string;
|
|
57
|
+
app?: string;
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAsB;;;;;;;;;;EAqBzD;AAYD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CA4ClF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,KAAK,GAAG,KAAK,GAAG,MAAe,GAAG;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAWA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Path Utilities - Cross-platform path handling for Claude Code config
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import { resolve, normalize, isAbsolute, join, dirname } from 'node:path';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
/**
|
|
8
|
+
* Get home directory cross-platform
|
|
9
|
+
*/
|
|
10
|
+
export function getHomeDir() {
|
|
11
|
+
return homedir();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get Claude Code global config directory (~/.claude)
|
|
15
|
+
*/
|
|
16
|
+
export function getClaudeConfigDir() {
|
|
17
|
+
return join(getHomeDir(), '.claude');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get Claude Code app config directory (~/.config/claude-code)
|
|
21
|
+
* This is where MCP servers are configured for the Claude Code app
|
|
22
|
+
*/
|
|
23
|
+
export function getClaudeAppConfigDir() {
|
|
24
|
+
return join(getHomeDir(), '.config', 'claude-code');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get Claude CLI config file path (~/.claude.json)
|
|
28
|
+
* This is where MCP servers are configured for the claude CLI
|
|
29
|
+
*/
|
|
30
|
+
export function getClaudeCliConfigPath() {
|
|
31
|
+
return join(getHomeDir(), '.claude.json');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get project-local Claude Code config directory (.claude)
|
|
35
|
+
*/
|
|
36
|
+
export function getProjectClaudeDir(cwd = process.cwd()) {
|
|
37
|
+
return join(cwd, '.claude');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve Claude config file paths
|
|
41
|
+
*/
|
|
42
|
+
export function getConfigPaths(cwd = process.cwd()) {
|
|
43
|
+
const claudeDir = getClaudeConfigDir();
|
|
44
|
+
const claudeAppDir = getClaudeAppConfigDir();
|
|
45
|
+
const projectClaudeDir = getProjectClaudeDir(cwd);
|
|
46
|
+
return {
|
|
47
|
+
// Global config files
|
|
48
|
+
globalClaudeMd: join(claudeDir, 'CLAUDE.md'),
|
|
49
|
+
keybindings: join(claudeDir, 'keybindings.json'),
|
|
50
|
+
settings: join(claudeDir, 'settings.json'),
|
|
51
|
+
claudeJson: join(claudeDir, 'claude.json'),
|
|
52
|
+
mcpServers: join(claudeDir, 'mcp_servers.json'),
|
|
53
|
+
hooksDir: join(claudeDir, 'hooks'),
|
|
54
|
+
pluginsDir: join(claudeDir, 'plugins'),
|
|
55
|
+
// App config file (where MCP servers are actually configured)
|
|
56
|
+
appConfig: join(claudeAppDir, 'config.json'),
|
|
57
|
+
// Project-local config files
|
|
58
|
+
projectClaudeMd: join(projectClaudeDir, 'CLAUDE.md'),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Allowed base paths for security (whitelist)
|
|
63
|
+
*/
|
|
64
|
+
const ALLOWED_BASES = [
|
|
65
|
+
getClaudeConfigDir(),
|
|
66
|
+
getProjectClaudeDir(),
|
|
67
|
+
getClaudeAppConfigDir(),
|
|
68
|
+
getClaudeCliConfigPath(),
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Validate and sanitize a file path
|
|
72
|
+
* Prevents path traversal attacks and ensures path is within allowed directories
|
|
73
|
+
*/
|
|
74
|
+
export function validatePath(userPath, cwd = process.cwd()) {
|
|
75
|
+
// Normalize path
|
|
76
|
+
const normalized = normalize(userPath.replace(/\0/g, ''));
|
|
77
|
+
// Resolve to absolute path
|
|
78
|
+
const absolute = isAbsolute(normalized)
|
|
79
|
+
? resolve(normalized)
|
|
80
|
+
: resolve(cwd, normalized);
|
|
81
|
+
// Check for path traversal attempts
|
|
82
|
+
if (normalized.includes('..')) {
|
|
83
|
+
throw new Error('Path traversal detected');
|
|
84
|
+
}
|
|
85
|
+
// Build allowed bases including the current project directory
|
|
86
|
+
const allowedBases = [
|
|
87
|
+
...ALLOWED_BASES,
|
|
88
|
+
getClaudeConfigDir(),
|
|
89
|
+
getProjectClaudeDir(cwd),
|
|
90
|
+
];
|
|
91
|
+
// Check if path is within allowed bases
|
|
92
|
+
// For files that don't exist yet, check the parent directory
|
|
93
|
+
let checkPath = absolute;
|
|
94
|
+
while (checkPath !== '/' && checkPath !== cwd && checkPath !== getHomeDir()) {
|
|
95
|
+
const isAllowed = allowedBases.some(base => {
|
|
96
|
+
const resolvedBase = resolve(base);
|
|
97
|
+
return (checkPath === resolvedBase ||
|
|
98
|
+
checkPath.startsWith(resolvedBase + '/') ||
|
|
99
|
+
resolvedBase.startsWith(checkPath + '/'));
|
|
100
|
+
});
|
|
101
|
+
if (isAllowed) {
|
|
102
|
+
return absolute;
|
|
103
|
+
}
|
|
104
|
+
// Move up one directory to check
|
|
105
|
+
checkPath = dirname(checkPath);
|
|
106
|
+
}
|
|
107
|
+
// If we get here, the path is not allowed
|
|
108
|
+
throw new Error(`Path not allowed: ${userPath}`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if a path exists
|
|
112
|
+
*/
|
|
113
|
+
export function pathExists(filePath) {
|
|
114
|
+
try {
|
|
115
|
+
return existsSync(filePath);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get relative path from home directory for display
|
|
123
|
+
*/
|
|
124
|
+
export function getDisplayPath(filePath) {
|
|
125
|
+
const home = getHomeDir();
|
|
126
|
+
if (filePath.startsWith(home)) {
|
|
127
|
+
return filePath.replace(home, '~');
|
|
128
|
+
}
|
|
129
|
+
return filePath;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get MCP config file paths based on target
|
|
133
|
+
* @param target - Which config(s) to get ('cli', 'app', or 'both')
|
|
134
|
+
* @returns Object with the requested config paths
|
|
135
|
+
*/
|
|
136
|
+
export function getMCPConfigPaths(target = 'both') {
|
|
137
|
+
const result = {};
|
|
138
|
+
if (target === 'cli' || target === 'both') {
|
|
139
|
+
result.cli = getClaudeCliConfigPath();
|
|
140
|
+
}
|
|
141
|
+
if (target === 'app' || target === 'both') {
|
|
142
|
+
result.app = getConfigPaths().appConfig;
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"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"}
|
package/lmdb.db
ADDED
|
Binary file
|
package/lmdb.db-lock
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ebowwa/claude-code-config-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code Config MCP server - Manage Claude Code configuration files (CLAUDE.md, keybindings.json, settings.json, hooks)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"mcp-claude-code-config": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "bun run --watch src/index.ts",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"lint": "eslint src/**/*.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
20
|
+
"zod": "^4.3.5"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.0.0",
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.0.0",
|
|
28
|
+
"bun": ">=1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"mcp",
|
|
32
|
+
"claude-code",
|
|
33
|
+
"config",
|
|
34
|
+
"configuration",
|
|
35
|
+
"settings",
|
|
36
|
+
"keybindings",
|
|
37
|
+
"claude-md",
|
|
38
|
+
"hooks",
|
|
39
|
+
"file-management"
|
|
40
|
+
],
|
|
41
|
+
"author": "ebowwa",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|