context-mcp-server 1.0.1 → 1.0.2
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/package.json +2 -1
- package/src/cli.js +65 -4
- package/src/tools/context.js +39 -3
- package/src/tools/fileTools.js +8 -6
- package/src/tools/gitTools.js +28 -12
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Persistent AI memory + codebase knowledge graph MCP server. Works across Claude Code, Cursor, Gemini CLI, Codex, Windsurf, VS Code Copilot, Claude.ai, and ChatGPT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"context-mcp": "./src/index.js",
|
|
8
|
+
"context-mcp-server": "./src/index.js",
|
|
8
9
|
"context-mcp-http": "./src/http.js",
|
|
9
10
|
"ctx": "./src/cli.js"
|
|
10
11
|
},
|
package/src/cli.js
CHANGED
|
@@ -119,9 +119,11 @@ function printUsage() {
|
|
|
119
119
|
cmd('ctx discuss [project]', 'show discussions');
|
|
120
120
|
cmd('ctx benchmark', 'token savings report (memory + graph)');
|
|
121
121
|
console.log('');
|
|
122
|
+
cmd('ctx install --initial', 'install / update Node.js + Python (codegraph) deps');
|
|
122
123
|
cmd('ctx install --<platform>', 'write MCP config + instruction file for an AI platform');
|
|
123
124
|
cmd('ctx install --all', 'install for all platforms at once');
|
|
124
125
|
cmd('ctx online [--port N]', 'start HTTP server + show credentials for Claude.ai / ChatGPT');
|
|
126
|
+
cmd('ctx online --close', 'stop the running HTTP server');
|
|
125
127
|
cmd('ctx settings', 'view and edit config (port, host, client id/secret)');
|
|
126
128
|
console.log('');
|
|
127
129
|
cmd('ctx help', 'show this screen');
|
|
@@ -586,10 +588,8 @@ const PLATFORMS = {
|
|
|
586
588
|
windsurf: {
|
|
587
589
|
label: 'Windsurf',
|
|
588
590
|
install(cwd) {
|
|
589
|
-
// Local rule file
|
|
590
591
|
const rules = _tpl('windsurf-rules.md');
|
|
591
592
|
if (rules) _writeFile(join(cwd, '.windsurf', 'rules', 'context-mcp.md'), rules, '.windsurf/rules/context-mcp.md');
|
|
592
|
-
// Global Windsurf config
|
|
593
593
|
const globalCfgPath = join(homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
594
594
|
let existing = {};
|
|
595
595
|
try { existing = JSON.parse(readFileSync(globalCfgPath, 'utf8')); } catch {}
|
|
@@ -602,12 +602,57 @@ const PLATFORMS = {
|
|
|
602
602
|
|
|
603
603
|
function cmdInstall(args) {
|
|
604
604
|
const flags = new Set(args.map(a => a.replace(/^--/, '')));
|
|
605
|
-
const all
|
|
605
|
+
const all = flags.has('all');
|
|
606
|
+
const initial = flags.has('initial');
|
|
606
607
|
const keys = all ? Object.keys(PLATFORMS) : Object.keys(PLATFORMS).filter(k => flags.has(k));
|
|
607
608
|
|
|
609
|
+
if (initial) {
|
|
610
|
+
printSection('Install', 'install / update');
|
|
611
|
+
console.log('');
|
|
612
|
+
|
|
613
|
+
const __dirname_init = dirname(fileURLToPath(import.meta.url));
|
|
614
|
+
const pkgRootInit = join(__dirname_init, '..');
|
|
615
|
+
|
|
616
|
+
// Node.js packages
|
|
617
|
+
console.log(` ${bold(lblue('Node.js packages'))}`);
|
|
618
|
+
const npmInstall = spawnSync('npm', ['install', '--omit=dev'], {
|
|
619
|
+
cwd: pkgRootInit, encoding: 'utf8', shell: true,
|
|
620
|
+
});
|
|
621
|
+
if (npmInstall.status !== 0) {
|
|
622
|
+
console.log(` ${bad('✗')} npm install failed:\n${faint((npmInstall.stderr || npmInstall.stdout || '').trim())}`);
|
|
623
|
+
} else {
|
|
624
|
+
console.log(` ${ok('✓')} Node.js dependencies installed`);
|
|
625
|
+
}
|
|
626
|
+
console.log('');
|
|
627
|
+
|
|
628
|
+
// Python / uv (codegraph)
|
|
629
|
+
console.log(` ${bold(lblue('Python Codegraph'))}`);
|
|
630
|
+
const uvCheck2 = spawnSync('uv', ['--version'], { encoding: 'utf8', shell: true });
|
|
631
|
+
if (uvCheck2.error || uvCheck2.status !== 0) {
|
|
632
|
+
console.log(` ${bad('✗')} uv not found — install from ${accent('https://docs.astral.sh/uv/')} to enable codegraph`);
|
|
633
|
+
} else {
|
|
634
|
+
console.log(` ${ok('✓')} uv found: ${faint(uvCheck2.stdout.trim())}`);
|
|
635
|
+
// On Windows, an existing .venv contains a lib64 junction that uv can't remove — wipe it first
|
|
636
|
+
if (process.platform === 'win32') {
|
|
637
|
+
const venvPath = join(pkgRootInit, '.venv');
|
|
638
|
+
spawnSync('cmd', ['/c', 'rmdir', '/s', '/q', venvPath], { encoding: 'utf8' });
|
|
639
|
+
}
|
|
640
|
+
const sync2 = spawnSync('uv', ['sync', '--no-dev'], { cwd: pkgRootInit, encoding: 'utf8', shell: true });
|
|
641
|
+
if (sync2.status !== 0) {
|
|
642
|
+
console.log(` ${bad('✗')} uv sync failed:\n${faint((sync2.stderr || sync2.stdout || '').trim())}`);
|
|
643
|
+
} else {
|
|
644
|
+
console.log(` ${ok('✓')} Python environment ready — codegraph enabled`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
console.log('');
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
|
|
608
651
|
if (!keys.length) {
|
|
609
652
|
printSection('Install');
|
|
610
|
-
console.log(` ${muted('Usage:')} ctx install ${faint('[--claude] [--cursor] [--vscode] [--gemini] [--codex] [--windsurf] [--all]')}`);
|
|
653
|
+
console.log(` ${muted('Usage:')} ctx install ${faint('[--initial] [--claude] [--cursor] [--vscode] [--gemini] [--codex] [--windsurf] [--all]')}`);
|
|
654
|
+
console.log('');
|
|
655
|
+
console.log(` ${accent('--initial ')} ${faint('Install / update Node.js + Python (codegraph) deps')}`);
|
|
611
656
|
console.log('');
|
|
612
657
|
console.log(` Writes MCP config file + AI instruction file for each selected platform.`);
|
|
613
658
|
console.log(` Files are written into the ${accent('current directory')} (your project root).`);
|
|
@@ -700,6 +745,7 @@ function cmdOnline(args) {
|
|
|
700
745
|
const host = hostIdx !== -1 && args[hostIdx + 1] ? args[hostIdx + 1] : null;
|
|
701
746
|
const git = args.includes('--access-git');
|
|
702
747
|
const restart = args.includes('--restart');
|
|
748
|
+
const close = args.includes('--close');
|
|
703
749
|
|
|
704
750
|
let cfg;
|
|
705
751
|
try { cfg = getConfig(); } catch { cfg = { client_id: 'context-mcp', client_secret: '(unavailable)', port: 3100, host: 'localhost' }; }
|
|
@@ -710,6 +756,21 @@ function cmdOnline(args) {
|
|
|
710
756
|
printSection('Online', `HTTP MCP server → Claude.ai / ChatGPT`);
|
|
711
757
|
console.log('');
|
|
712
758
|
|
|
759
|
+
if (close) {
|
|
760
|
+
const existing2 = _checkExistingHttpServer(resolvedPort);
|
|
761
|
+
if (existing2.status === 'running') {
|
|
762
|
+
if (existing2.pid) {
|
|
763
|
+
try { process.kill(existing2.pid); } catch {}
|
|
764
|
+
}
|
|
765
|
+
try { unlinkSync(_httpPidFile(resolvedPort)); } catch {}
|
|
766
|
+
const pidStr = existing2.pid ? `pid ${existing2.pid} · ` : '';
|
|
767
|
+
console.log(` ${ok('✓')} ${bold('server stopped')} ${faint(pidStr + 'port ' + resolvedPort)}\n`);
|
|
768
|
+
} else {
|
|
769
|
+
console.log(` ${warn('–')} no server running on port ${resolvedPort}\n`);
|
|
770
|
+
}
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
|
|
713
774
|
// Check if a server is already running on this port
|
|
714
775
|
const existing = _checkExistingHttpServer(resolvedPort);
|
|
715
776
|
if (existing.status === 'running') {
|
package/src/tools/context.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { guardPath } from '../guard.js';
|
|
1
3
|
import {
|
|
2
4
|
saveContext, updateContext, getContext, deleteContext,
|
|
3
5
|
listProjects, findDuplicate, archiveExpired, linkContextToDiscussion,
|
|
4
6
|
listDiscussions, listGraphs, countContext, shouldCompact, compactProject,
|
|
5
7
|
ensureProject, getProjectRoot,
|
|
6
8
|
} from '../db.js';
|
|
9
|
+
|
|
10
|
+
function detectGitRoot() {
|
|
11
|
+
try {
|
|
12
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
13
|
+
cwd: process.cwd(), encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
+
}).trim();
|
|
15
|
+
} catch { return null; }
|
|
16
|
+
}
|
|
7
17
|
import { summarizeEntries } from '../summarizer.js';
|
|
8
18
|
import { fireAutoLink } from '../hooks/autoLink.js';
|
|
9
19
|
|
|
@@ -71,10 +81,12 @@ export async function handle(args, state) {
|
|
|
71
81
|
// Set project on state so autoLink works for subsequent saves
|
|
72
82
|
if (proj) state.sessionProject = proj;
|
|
73
83
|
|
|
74
|
-
// Store rootPath with project (first time only) and load it onto session state
|
|
75
|
-
if
|
|
84
|
+
// Store rootPath with project (first time only) and load it onto session state.
|
|
85
|
+
// Auto-detect from git if neither provided nor previously stored.
|
|
76
86
|
const storedRoot = proj ? getProjectRoot(proj) : null;
|
|
77
|
-
|
|
87
|
+
const resolvedRoot = args.rootPath || storedRoot || detectGitRoot() || null;
|
|
88
|
+
if (proj) ensureProject(proj, resolvedRoot || undefined);
|
|
89
|
+
state.projectRootPath = resolvedRoot;
|
|
78
90
|
|
|
79
91
|
const entries = getContext({ project: proj, limit: 15, compact: true })
|
|
80
92
|
.filter(e => e.status !== 'archived');
|
|
@@ -105,6 +117,9 @@ export async function handle(args, state) {
|
|
|
105
117
|
stats: { totalEntries, projects: listProjects().length },
|
|
106
118
|
message: `Loaded ${totalEntries} entries for project "${proj || 'global'}".${discussions.length === 1 ? ` Auto-linked to discussion "${discussions[0].name}".` : ''}`,
|
|
107
119
|
rootPath: state.projectRootPath || undefined,
|
|
120
|
+
sandbox: state.projectRootPath
|
|
121
|
+
? `All file and git operations are sandboxed to: ${state.projectRootPath} — do not use paths outside this root.`
|
|
122
|
+
: 'No project root configured — pass rootPath to restrict file/git access to a directory.',
|
|
108
123
|
hint: graphStatus.built
|
|
109
124
|
? `Graph ready (${graphStatus.nodes} nodes). Use codegraph_query for structural questions.`
|
|
110
125
|
: 'No graph built yet. Call codegraph_build on the project root to enable graph queries.',
|
|
@@ -114,6 +129,27 @@ export async function handle(args, state) {
|
|
|
114
129
|
case 'save': {
|
|
115
130
|
if (!args.content) throw new Error('content is required for save');
|
|
116
131
|
if (!args.project && state.sessionProject) args = { ...args, project: state.sessionProject };
|
|
132
|
+
// Auto-detect and store project root if not yet configured
|
|
133
|
+
if (args.project) {
|
|
134
|
+
const existing = getProjectRoot(args.project);
|
|
135
|
+
if (!existing) {
|
|
136
|
+
const detected = state.projectRootPath || detectGitRoot();
|
|
137
|
+
if (detected) {
|
|
138
|
+
ensureProject(args.project, detected);
|
|
139
|
+
if (!state.projectRootPath) state.projectRootPath = detected;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Validate file paths in files[] and codeRefs[] stay within project root
|
|
144
|
+
if (state.projectRootPath) {
|
|
145
|
+
if (Array.isArray(args.files)) {
|
|
146
|
+
args.files.forEach(f => { if (f.path) guardPath(f.path, state.projectRootPath); });
|
|
147
|
+
}
|
|
148
|
+
if (Array.isArray(args.codeRefs)) {
|
|
149
|
+
args.codeRefs.forEach(r => { if (r.file) guardPath(r.file, state.projectRootPath); });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
117
153
|
const dupe = findDuplicate(args.content, args.project);
|
|
118
154
|
if (dupe) {
|
|
119
155
|
const updated = updateContext({
|
package/src/tools/fileTools.js
CHANGED
|
@@ -20,28 +20,30 @@ function atomicWrite(filePath, data) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const ROOT_NOTE = ' Sandboxed to project root — paths outside root are denied.';
|
|
24
|
+
|
|
23
25
|
export const definitions = [
|
|
24
26
|
{
|
|
25
27
|
name: 'create_dir',
|
|
26
|
-
description: 'Create a directory (and any missing parent directories). Safe to call if already exists.',
|
|
28
|
+
description: 'Create a directory (and any missing parent directories). Safe to call if already exists.' + ROOT_NOTE,
|
|
27
29
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
28
30
|
outputSchema: { type: 'object', properties: { path: { type: 'string' }, existed: { type: 'boolean' }, message: { type: 'string' } } },
|
|
29
31
|
},
|
|
30
32
|
{
|
|
31
33
|
name: 'list_dir',
|
|
32
|
-
description: 'List the contents of a local directory.',
|
|
34
|
+
description: 'List the contents of a local directory.' + ROOT_NOTE,
|
|
33
35
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
34
36
|
outputSchema: { type: 'object', properties: { directory: { type: 'string' }, items: { type: 'array' } } },
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
39
|
name: 'read_file',
|
|
38
|
-
description: 'Read the text contents of a local file.',
|
|
40
|
+
description: 'Read the text contents of a local file.' + ROOT_NOTE,
|
|
39
41
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
40
42
|
outputSchema: { type: 'object', properties: { file: { type: 'string' }, content: { type: 'string' } } },
|
|
41
43
|
},
|
|
42
44
|
{
|
|
43
45
|
name: 'write_file',
|
|
44
|
-
description: 'Create a new file or overwrite an existing file.',
|
|
46
|
+
description: 'Create a new file or overwrite an existing file.' + ROOT_NOTE,
|
|
45
47
|
inputSchema: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] },
|
|
46
48
|
outputSchema: { type: 'object', properties: { file: { type: 'string' }, message: { type: 'string' } } },
|
|
47
49
|
},
|
|
@@ -51,7 +53,7 @@ export const definitions = [
|
|
|
51
53
|
`Apply targeted string replacement(s) to a file.\n` +
|
|
52
54
|
`Single edit: pass old_str + new_str.\n` +
|
|
53
55
|
`Multi edit: pass edits:[{old_str, new_str, description?}] — atomic.\n` +
|
|
54
|
-
`Use dry_run:true to validate without writing
|
|
56
|
+
`Use dry_run:true to validate without writing.` + ROOT_NOTE,
|
|
55
57
|
inputSchema: {
|
|
56
58
|
type: 'object',
|
|
57
59
|
properties: {
|
|
@@ -68,7 +70,7 @@ export const definitions = [
|
|
|
68
70
|
},
|
|
69
71
|
{
|
|
70
72
|
name: 'delete_file',
|
|
71
|
-
description: 'Delete a local file. Pass recursive:true to delete a directory and its contents.',
|
|
73
|
+
description: 'Delete a local file. Pass recursive:true to delete a directory and its contents.' + ROOT_NOTE,
|
|
72
74
|
inputSchema: { type: 'object', properties: { path: { type: 'string' }, recursive: { type: 'boolean', description: 'Required to delete a directory. Defaults to false.' } }, required: ['path'] },
|
|
73
75
|
outputSchema: { type: 'object', properties: { path: { type: 'string' }, message: { type: 'string' } } },
|
|
74
76
|
},
|
package/src/tools/gitTools.js
CHANGED
|
@@ -15,69 +15,85 @@ function runGit(argArr, cwd) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function autoDetectRoot(fromDir) {
|
|
19
|
+
try {
|
|
20
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
21
|
+
cwd: fromDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
22
|
+
}).trim();
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
function resolveCwd(args, state) {
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
// Auto-detect project root on first use if not already configured.
|
|
30
|
+
if (!state.projectRootPath) {
|
|
31
|
+
const detected = autoDetectRoot(args.cwd ? pathResolve(args.cwd) : process.cwd());
|
|
32
|
+
if (detected) state.projectRootPath = detected;
|
|
33
|
+
}
|
|
34
|
+
const raw = args.cwd ? pathResolve(args.cwd) : (state.projectRootPath || process.cwd());
|
|
21
35
|
if (state.projectRootPath) return guardPath(raw, state.projectRootPath);
|
|
22
36
|
return raw;
|
|
23
37
|
}
|
|
24
38
|
|
|
39
|
+
const ROOT_NOTE = ' All paths must be within the project root (sandboxed — access outside root is denied).';
|
|
40
|
+
|
|
25
41
|
export const definitions = [
|
|
26
42
|
{
|
|
27
43
|
name: 'git_status',
|
|
28
|
-
description: 'Show working tree status — current branch, staged, unstaged, and untracked files.',
|
|
44
|
+
description: 'Show working tree status — current branch, staged, unstaged, and untracked files.' + ROOT_NOTE,
|
|
29
45
|
inputSchema: { type: 'object', properties: { cwd: { type: 'string' } } },
|
|
30
46
|
outputSchema: { type: 'object', properties: { branch: { type: 'string' }, clean: { type: 'boolean' }, staged: { type: 'array' }, unstaged: { type: 'array' }, untracked: { type: 'array' } } },
|
|
31
47
|
},
|
|
32
48
|
{
|
|
33
49
|
name: 'git_diff',
|
|
34
|
-
description: 'Show file changes. Use staged:true for cached diff. Optionally scope to a path.',
|
|
50
|
+
description: 'Show file changes. Use staged:true for cached diff. Optionally scope to a path.' + ROOT_NOTE,
|
|
35
51
|
inputSchema: { type: 'object', properties: { staged: { type: 'boolean' }, path: { type: 'string' }, cwd: { type: 'string' } } },
|
|
36
52
|
outputSchema: { type: 'object', properties: { diff: { type: 'string' }, staged: { type: 'boolean' } } },
|
|
37
53
|
},
|
|
38
54
|
{
|
|
39
55
|
name: 'git_log',
|
|
40
|
-
description: 'Show recent commit history — hash, author, date, message.',
|
|
56
|
+
description: 'Show recent commit history — hash, author, date, message.' + ROOT_NOTE,
|
|
41
57
|
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, path: { type: 'string' }, cwd: { type: 'string' } } },
|
|
42
58
|
outputSchema: { type: 'object', properties: { commits: { type: 'array' }, count: { type: 'number' } } },
|
|
43
59
|
},
|
|
44
60
|
{
|
|
45
61
|
name: 'git_add',
|
|
46
|
-
description: 'Stage files for commit. Pass paths:["."] to stage everything.',
|
|
62
|
+
description: 'Stage files for commit. Pass paths:["."] to stage everything.' + ROOT_NOTE,
|
|
47
63
|
inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' } }, cwd: { type: 'string' } }, required: ['paths'] },
|
|
48
64
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, staged: { type: 'array' }, message: { type: 'string' } } },
|
|
49
65
|
},
|
|
50
66
|
{
|
|
51
67
|
name: 'git_commit',
|
|
52
|
-
description: 'Commit staged changes. Set all:true to auto-stage tracked modified files first. Auto-saves context entry.',
|
|
68
|
+
description: 'Commit staged changes. Set all:true to auto-stage tracked modified files first. Auto-saves context entry.' + ROOT_NOTE,
|
|
53
69
|
inputSchema: { type: 'object', properties: { message: { type: 'string' }, all: { type: 'boolean' }, cwd: { type: 'string' } }, required: ['message'] },
|
|
54
70
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, hash: { type: 'string' }, branch: { type: 'string' }, message: { type: 'string' }, files: { type: 'array' } } },
|
|
55
71
|
},
|
|
56
72
|
{
|
|
57
73
|
name: 'git_push',
|
|
58
|
-
description: 'Push current branch to remote.',
|
|
74
|
+
description: 'Push current branch to remote.' + ROOT_NOTE,
|
|
59
75
|
inputSchema: { type: 'object', properties: { remote: { type: 'string' }, branch: { type: 'string' }, cwd: { type: 'string' } } },
|
|
60
76
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, remote: { type: 'string' }, branch: { type: 'string' }, output: { type: 'string' } } },
|
|
61
77
|
},
|
|
62
78
|
{
|
|
63
79
|
name: 'git_pull',
|
|
64
|
-
description: 'Pull from remote and merge into current branch.',
|
|
80
|
+
description: 'Pull from remote and merge into current branch.' + ROOT_NOTE,
|
|
65
81
|
inputSchema: { type: 'object', properties: { remote: { type: 'string' }, branch: { type: 'string' }, cwd: { type: 'string' } } },
|
|
66
82
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, remote: { type: 'string' }, output: { type: 'string' } } },
|
|
67
83
|
},
|
|
68
84
|
{
|
|
69
85
|
name: 'git_branch',
|
|
70
|
-
description: 'List, create, or checkout branches.',
|
|
86
|
+
description: 'List, create, or checkout branches.' + ROOT_NOTE,
|
|
71
87
|
inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['list', 'create', 'checkout'] }, name: { type: 'string' }, cwd: { type: 'string' } } },
|
|
72
88
|
},
|
|
73
89
|
{
|
|
74
90
|
name: 'git_stash',
|
|
75
|
-
description: 'Stash or restore work-in-progress changes.',
|
|
91
|
+
description: 'Stash or restore work-in-progress changes.' + ROOT_NOTE,
|
|
76
92
|
inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['save', 'pop', 'list', 'drop'] }, message: { type: 'string' }, ref: { type: 'string' }, cwd: { type: 'string' } } },
|
|
77
93
|
},
|
|
78
94
|
{
|
|
79
95
|
name: 'git_reset',
|
|
80
|
-
description: 'Unstage files or reset HEAD. Use mode:file + path to restore a single file.',
|
|
96
|
+
description: 'Unstage files or reset HEAD. Use mode:file + path to restore a single file.' + ROOT_NOTE,
|
|
81
97
|
inputSchema: { type: 'object', properties: { mode: { type: 'string', enum: ['soft', 'mixed', 'hard', 'file'] }, path: { type: 'string' }, ref: { type: 'string' }, cwd: { type: 'string' } } },
|
|
82
98
|
},
|
|
83
99
|
{
|