smart-context-mcp 1.0.1 → 1.0.3
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 +25 -1
- package/package.json +1 -1
- package/scripts/init-clients.js +33 -7
- package/scripts/report-metrics.js +21 -17
- package/src/mcp-server.js +8 -0
- package/src/utils/runtime-config.js +13 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ npx smart-context-init --target .
|
|
|
27
27
|
|
|
28
28
|
That's it. Restart your AI client (Cursor, Codex, Claude Desktop) and the tools are available.
|
|
29
29
|
|
|
30
|
-
**Important:** The init command automatically sets the correct
|
|
30
|
+
**Important:** The init command automatically sets the correct project-root env var in the generated configs, so the MCP server runs from your project root. This works for standalone projects, monorepos, and nested workspaces.
|
|
31
31
|
|
|
32
32
|
## What you get
|
|
33
33
|
|
|
@@ -219,6 +219,30 @@ The `intent` parameter in `smart_search` and `smart_context` adjusts ranking and
|
|
|
219
219
|
|
|
220
220
|
The rules are idempotent — running `smart-context-init` again updates the section without duplicating it. Existing content in `AGENTS.md` and `CLAUDE.md` is preserved.
|
|
221
221
|
|
|
222
|
+
## Use against another repo
|
|
223
|
+
|
|
224
|
+
By default, `devctx` works against the repo where it is installed. You can point it at another repo without modifying that target project:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
node ./src/mcp-server.js --project-root /path/to/target-repo
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
or:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
DEVCTX_PROJECT_ROOT=/path/to/target-repo node ./src/mcp-server.js
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
or (recommended for MCP clients and generated configs):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
DEVCTX_PROJECT_ROOT=/path/to/target-repo node ./src/mcp-server.js
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Legacy configs that still set `MCP_PROJECT_ROOT` remain supported for backward compatibility.
|
|
243
|
+
|
|
244
|
+
`smart-context-init` automatically sets `DEVCTX_PROJECT_ROOT` in the generated client configs (`.cursor/mcp.json`, `.codex/config.toml`, `.mcp.json`, `.qwen/settings.json`), so the MCP server always launches from the correct project context, even in monorepos or when installed globally.
|
|
245
|
+
|
|
222
246
|
## What it is good at
|
|
223
247
|
|
|
224
248
|
| Level | Languages / Stack | Use cases |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smart-context-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "MCP server that reduces agent token usage and improves response quality with compact file summaries, ranked code search, and curated context.",
|
|
5
5
|
"author": "Francisco Caballero Portero <fcp1978@hotmail.com>",
|
|
6
6
|
"type": "module",
|
package/scripts/init-clients.js
CHANGED
|
@@ -123,7 +123,9 @@ const getServerConfig = ({ name, command, args, cwd }) => ({
|
|
|
123
123
|
config: {
|
|
124
124
|
command,
|
|
125
125
|
args,
|
|
126
|
-
|
|
126
|
+
env: {
|
|
127
|
+
DEVCTX_PROJECT_ROOT: cwd,
|
|
128
|
+
},
|
|
127
129
|
},
|
|
128
130
|
});
|
|
129
131
|
|
|
@@ -131,7 +133,14 @@ const updateCursorConfig = (targetDir, serverConfig, dryRun) => {
|
|
|
131
133
|
const filePath = path.join(targetDir, '.cursor', 'mcp.json');
|
|
132
134
|
const current = readJson(filePath, { mcpServers: {} });
|
|
133
135
|
current.mcpServers ??= {};
|
|
134
|
-
current.mcpServers[serverConfig.name]
|
|
136
|
+
const existing = current.mcpServers[serverConfig.name] || {};
|
|
137
|
+
current.mcpServers[serverConfig.name] = {
|
|
138
|
+
...serverConfig.config,
|
|
139
|
+
env: {
|
|
140
|
+
...existing.env,
|
|
141
|
+
...serverConfig.config.env,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
135
144
|
writeFile(filePath, `${JSON.stringify(current, null, 2)}\n`, dryRun);
|
|
136
145
|
};
|
|
137
146
|
|
|
@@ -139,9 +148,13 @@ const updateClaudeConfig = (targetDir, serverConfig, dryRun) => {
|
|
|
139
148
|
const filePath = path.join(targetDir, '.mcp.json');
|
|
140
149
|
const current = readJson(filePath, { mcpServers: {} });
|
|
141
150
|
current.mcpServers ??= {};
|
|
151
|
+
const existing = current.mcpServers[serverConfig.name] || {};
|
|
142
152
|
current.mcpServers[serverConfig.name] = {
|
|
143
153
|
...serverConfig.config,
|
|
144
|
-
env:
|
|
154
|
+
env: {
|
|
155
|
+
...existing.env,
|
|
156
|
+
...serverConfig.config.env,
|
|
157
|
+
},
|
|
145
158
|
};
|
|
146
159
|
writeFile(filePath, `${JSON.stringify(current, null, 2)}\n`, dryRun);
|
|
147
160
|
};
|
|
@@ -152,7 +165,14 @@ const updateQwenConfig = (targetDir, serverConfig, dryRun) => {
|
|
|
152
165
|
current.mcp ??= {};
|
|
153
166
|
current.mcp.enabled = true;
|
|
154
167
|
current.mcpServers ??= {};
|
|
155
|
-
current.mcpServers[serverConfig.name]
|
|
168
|
+
const existing = current.mcpServers[serverConfig.name] || {};
|
|
169
|
+
current.mcpServers[serverConfig.name] = {
|
|
170
|
+
...serverConfig.config,
|
|
171
|
+
env: {
|
|
172
|
+
...existing.env,
|
|
173
|
+
...serverConfig.config.env,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
156
176
|
writeFile(filePath, `${JSON.stringify(current, null, 2)}\n`, dryRun);
|
|
157
177
|
};
|
|
158
178
|
|
|
@@ -163,11 +183,17 @@ const buildCodexSection = (serverConfig) => {
|
|
|
163
183
|
'required = false',
|
|
164
184
|
`command = ${JSON.stringify(serverConfig.config.command)}`,
|
|
165
185
|
`args = [${serverConfig.config.args.map((value) => JSON.stringify(value)).join(', ')}]`,
|
|
166
|
-
...(serverConfig.config.cwd ? [`cwd = ${JSON.stringify(serverConfig.config.cwd)}`] : []),
|
|
167
|
-
'startup_timeout_sec = 15.0',
|
|
168
|
-
'tool_timeout_sec = 30.0',
|
|
169
186
|
];
|
|
170
187
|
|
|
188
|
+
if (serverConfig.config.env && Object.keys(serverConfig.config.env).length > 0) {
|
|
189
|
+
const envEntries = Object.entries(serverConfig.config.env)
|
|
190
|
+
.map(([key, value]) => ` ${JSON.stringify(key)} = ${JSON.stringify(value)}`)
|
|
191
|
+
.join(',\n');
|
|
192
|
+
body.push(`env = {\n${envEntries}\n}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
body.push('startup_timeout_sec = 15.0', 'tool_timeout_sec = 30.0');
|
|
196
|
+
|
|
171
197
|
return { header, body };
|
|
172
198
|
};
|
|
173
199
|
|
|
@@ -11,7 +11,7 @@ const requireValue = (argv, index, flag) => {
|
|
|
11
11
|
return value;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
const parseArgs = (argv) => {
|
|
14
|
+
export const parseArgs = (argv) => {
|
|
15
15
|
const options = {
|
|
16
16
|
file: null,
|
|
17
17
|
json: false,
|
|
@@ -46,7 +46,7 @@ const parseArgs = (argv) => {
|
|
|
46
46
|
|
|
47
47
|
const unique = (items) => [...new Set(items.filter(Boolean))];
|
|
48
48
|
|
|
49
|
-
const resolveMetricsInput = (options) => {
|
|
49
|
+
export const resolveMetricsInput = (options) => {
|
|
50
50
|
if (options.file) {
|
|
51
51
|
return { filePath: options.file, source: 'explicit' };
|
|
52
52
|
}
|
|
@@ -66,7 +66,7 @@ const resolveMetricsInput = (options) => {
|
|
|
66
66
|
return { filePath: defaultPath, source: 'default' };
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
const readEntries = (filePath) => {
|
|
69
|
+
export const readEntries = (filePath) => {
|
|
70
70
|
if (!fs.existsSync(filePath)) {
|
|
71
71
|
throw new Error(`No metrics file found at ${filePath}`);
|
|
72
72
|
}
|
|
@@ -100,7 +100,7 @@ const getSavedTokens = (entry, compressedTokens) => {
|
|
|
100
100
|
return Math.max(0, Number(entry.rawTokens ?? 0) - compressedTokens);
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
const aggregate = (entries) => {
|
|
103
|
+
export const aggregate = (entries) => {
|
|
104
104
|
const byTool = new Map();
|
|
105
105
|
let rawTokens = 0;
|
|
106
106
|
let compressedTokens = 0;
|
|
@@ -148,6 +148,21 @@ const aggregate = (entries) => {
|
|
|
148
148
|
|
|
149
149
|
const formatNumber = (value) => new Intl.NumberFormat('en-US').format(value);
|
|
150
150
|
|
|
151
|
+
export const createReport = (options) => {
|
|
152
|
+
const resolved = resolveMetricsInput(options);
|
|
153
|
+
const { entries, invalidLines } = readEntries(resolved.filePath);
|
|
154
|
+
const filteredEntries = options.tool ? entries.filter((entry) => entry.tool === options.tool) : entries;
|
|
155
|
+
const summary = aggregate(filteredEntries);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
filePath: resolved.filePath,
|
|
159
|
+
source: resolved.source,
|
|
160
|
+
toolFilter: options.tool,
|
|
161
|
+
invalidLines,
|
|
162
|
+
summary,
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
151
166
|
const printHuman = (report) => {
|
|
152
167
|
console.log('');
|
|
153
168
|
console.log('devctx metrics report');
|
|
@@ -176,20 +191,9 @@ const printHuman = (report) => {
|
|
|
176
191
|
}
|
|
177
192
|
};
|
|
178
193
|
|
|
179
|
-
const main = () => {
|
|
194
|
+
export const main = () => {
|
|
180
195
|
const options = parseArgs(process.argv.slice(2));
|
|
181
|
-
const
|
|
182
|
-
const { entries, invalidLines } = readEntries(resolved.filePath);
|
|
183
|
-
const filteredEntries = options.tool ? entries.filter((entry) => entry.tool === options.tool) : entries;
|
|
184
|
-
const summary = aggregate(filteredEntries);
|
|
185
|
-
|
|
186
|
-
const report = {
|
|
187
|
-
filePath: resolved.filePath,
|
|
188
|
-
source: resolved.source,
|
|
189
|
-
toolFilter: options.tool,
|
|
190
|
-
invalidLines,
|
|
191
|
-
summary,
|
|
192
|
-
};
|
|
196
|
+
const report = createReport(options);
|
|
193
197
|
|
|
194
198
|
if (options.json) {
|
|
195
199
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
package/src/mcp-server.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
const configuredProjectRoot =
|
|
2
|
+
process.env.DEVCTX_PROJECT_ROOT?.trim() ||
|
|
3
|
+
process.env.MCP_PROJECT_ROOT?.trim();
|
|
4
|
+
|
|
5
|
+
if (configuredProjectRoot) {
|
|
6
|
+
process.chdir(configuredProjectRoot);
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
import { runDevctxServer } from './server.js';
|
|
2
10
|
|
|
3
11
|
await runDevctxServer();
|
|
@@ -14,10 +14,22 @@ const readArgValue = (name) => {
|
|
|
14
14
|
return process.argv[index + 1] ?? null;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const readEnvValue = (...names) => {
|
|
18
|
+
for (const name of names) {
|
|
19
|
+
const value = process.env[name]?.trim();
|
|
20
|
+
|
|
21
|
+
if (value) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
|
|
17
29
|
const defaultDevctxRoot = path.resolve(currentDir, '..', '..');
|
|
18
30
|
const defaultProjectRoot = path.resolve(defaultDevctxRoot, '..', '..');
|
|
19
31
|
const projectRootArg = readArgValue('--project-root');
|
|
20
|
-
const projectRootEnv =
|
|
32
|
+
const projectRootEnv = readEnvValue('DEVCTX_PROJECT_ROOT', 'MCP_PROJECT_ROOT');
|
|
21
33
|
const rawProjectRoot = projectRootArg ?? projectRootEnv ?? defaultProjectRoot;
|
|
22
34
|
|
|
23
35
|
export const devctxRoot = defaultDevctxRoot;
|