Skip to content

Commit 8f24c00

Browse files
committed
Implemented project configuration to disable certain MCP tools to spare tokens with the initial schema in the context
1 parent f34f606 commit 8f24c00

9 files changed

Lines changed: 817 additions & 34 deletions

File tree

‎README.md‎

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,8 @@ Four more tools (`codegraph_callees`, `codegraph_impact`, `codegraph_files`, `co
537537

538538
In a workspace with no `.codegraph/` index, the server announces itself inactive and lists **no** tools — agents work normally with their built-in tools, and indexing stays your decision.
539539

540+
You can hide specific MCP tools per-project via `.codegraph/config.json` (see Configuration below). Hidden tools are removed from `tools/list` and are treated as unknown if called directly.
541+
540542
---
541543

542544
## Library Usage
@@ -586,25 +588,42 @@ that drive the graph directly: `DatabaseConnection`, `QueryBuilder`,
586588

587589
## Configuration
588590

589-
There isn't any — CodeGraph is zero-config, with **no config file** to write or
590-
keep in sync. Language support is automatic from the file extension; there's
591-
nothing to wire up per language.
592-
593-
What it skips out of the box:
594-
595-
- **Dependency, build, and cache directories** — `node_modules`, `vendor`,
596-
`dist`, `build`, `target`, `.venv`, `Pods`, `.next`, and the like across every
597-
[supported stack](#supported-languages) — so the graph is your code, not
598-
third-party noise. This holds even with no `.gitignore`.
599-
- **Anything in your `.gitignore`** — honored in git repos via git, and in
600-
non-git projects by reading `.gitignore` directly (root and nested).
601-
- **Files larger than 1 MB** — generated bundles, minified JS, vendored blobs.
602-
603-
To keep something else out, add it to `.gitignore`. To pull a default-excluded
604-
directory back **in** (say you really do want a vendored dependency indexed),
605-
add a negation — `!vendor/`. The defaults apply uniformly, so committing a
606-
dependency or build directory doesn't force it into the graph; the `.gitignore`
607-
negation is the explicit opt-in.
591+
CodeGraph works out-of-the-box with defaults, while allowing optional
592+
project-local overrides via `.codegraph/config.json`.
593+
594+
Defaults still skip dependency/build/cache directories, respect `.gitignore`,
595+
and ignore files larger than 1 MB.
596+
597+
```json
598+
{
599+
"version": 1,
600+
"languages": ["typescript", "javascript"],
601+
"exclude": ["node_modules/**", "dist/**", "build/**", "*.min.js"],
602+
"frameworks": [],
603+
"maxFileSize": 1048576,
604+
"extractDocstrings": true,
605+
"trackCallSites": true,
606+
"mcp": {
607+
"disabledTools": ["codegraph_explore"]
608+
}
609+
}
610+
```
611+
612+
| Option | Description | Default |
613+
|--------|-------------|---------|
614+
| `languages` | Languages to index (auto-detected if empty) | `[]` |
615+
| `exclude` | Glob patterns to ignore | `["node_modules/**", ...]` |
616+
| `frameworks` | Framework hints for better resolution | `[]` |
617+
| `maxFileSize` | Skip files larger than this (bytes) | `1048576` (1MB) |
618+
| `extractDocstrings` | Extract docstrings from code | `true` |
619+
| `trackCallSites` | Track call site locations | `true` |
620+
| `mcp.disabledTools` | MCP tool names to hide/disable for this project | `[]` |
621+
622+
Example disabled tool names: `codegraph_explore`, `codegraph_context`, `codegraph_files`.
623+
624+
Notes:
625+
- This setting is project-local only (no global override).
626+
- Unknown tool names are ignored (lenient behavior).
608627

609628
## Telemetry
610629

‎__tests__/foundation.test.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import * as os from 'os';
1111
import { CodeGraph } from '../src';
1212
import { Node, Edge } from '../src/types';
1313
import { isInitialized, getCodeGraphDir, validateDirectory, codeGraphDirName, isCodeGraphDataDir } from '../src/directory';
14+
import { Node, Edge, DEFAULT_CONFIG } from '../src/types';
15+
import { isInitialized, getCodeGraphDir, validateDirectory } from '../src/directory';
1416
import { DatabaseConnection, getDatabasePath } from '../src/db';
17+
import { loadConfig } from '../src/config';
1518

1619
// Create a temporary directory for each test
1720
function createTempDir(): string {
@@ -144,6 +147,56 @@ describe('CodeGraph Foundation', () => {
144147
});
145148
});
146149

150+
describe('Configuration', () => {
151+
it('should load and merge config with defaults', () => {
152+
const cg = CodeGraph.initSync(tempDir);
153+
cg.close();
154+
155+
const config = loadConfig(tempDir);
156+
expect(config.version).toBe(DEFAULT_CONFIG.version);
157+
expect(config.rootDir).toBe(path.resolve(tempDir));
158+
});
159+
160+
it('should update configuration', () => {
161+
const cg = CodeGraph.initSync(tempDir);
162+
163+
cg.updateConfig({ maxFileSize: 999999 });
164+
165+
expect(cg.getConfig().maxFileSize).toBe(999999);
166+
167+
cg.close();
168+
169+
// Verify persistence
170+
const config = loadConfig(tempDir);
171+
expect(config.maxFileSize).toBe(999999);
172+
});
173+
174+
it('should persist MCP disabled tools config', () => {
175+
const cg = CodeGraph.initSync(tempDir);
176+
177+
cg.updateConfig({
178+
mcp: { disabledTools: ['codegraph_explore'] },
179+
});
180+
181+
cg.close();
182+
183+
const config = loadConfig(tempDir);
184+
expect(config.mcp?.disabledTools).toEqual(['codegraph_explore']);
185+
});
186+
187+
it('should reject invalid mcp.disabledTools format', () => {
188+
const cg = CodeGraph.initSync(tempDir);
189+
cg.close();
190+
191+
const configPath = path.join(getCodeGraphDir(tempDir), 'config.json');
192+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as Record<string, unknown>;
193+
raw.mcp = { disabledTools: [123] };
194+
fs.writeFileSync(configPath, JSON.stringify(raw, null, 2));
195+
196+
expect(() => loadConfig(tempDir)).toThrow(/Invalid configuration format/i);
197+
});
198+
});
199+
147200
describe('Directory Management', () => {
148201
it('should validate directory structure', () => {
149202
const cg = CodeGraph.initSync(tempDir);

‎__tests__/security.test.ts‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import * as path from 'path';
1414
import * as os from 'os';
1515
import { FileLock, validateProjectPath, validatePathWithinRoot } from '../src/utils';
1616
import CodeGraph from '../src/index';
17-
import { ToolHandler, tools } from '../src/mcp/tools';
17+
import { ToolHandler, tools, filterToolsByConfig } from '../src/mcp/tools';
1818
import { scanDirectory, isSourceFile } from '../src/extraction';
1919
import { DatabaseConnection, getDatabasePath } from '../src/db';
2020
import { QueryBuilder } from '../src/db/queries';
@@ -423,6 +423,37 @@ describe('MCP Input Validation', () => {
423423
expect(result.content[0].text).toMatch(/sensitive system directory/i);
424424
}
425425
);
426+
427+
it('should hide disabled tools from tool list', async () => {
428+
const projectWithDisabled = createTempDir();
429+
const srcDir = path.join(projectWithDisabled, 'src');
430+
fs.mkdirSync(srcDir);
431+
fs.writeFileSync(path.join(srcDir, 'x.ts'), 'export const x = 1;\n');
432+
433+
const disabledCg = CodeGraph.initSync(projectWithDisabled, {
434+
config: {
435+
include: ['**/*.ts'],
436+
exclude: [],
437+
mcp: { disabledTools: ['codegraph_explore'] },
438+
},
439+
});
440+
441+
try {
442+
const disabledHandler = new ToolHandler(disabledCg);
443+
const toolNames = disabledHandler.getTools().map(t => t.name);
444+
expect(toolNames).not.toContain('codegraph_explore');
445+
} finally {
446+
disabledCg.close();
447+
cleanupTempDir(projectWithDisabled);
448+
}
449+
});
450+
451+
it('should ignore unknown disabled tool names (lenient)', () => {
452+
const filtered = filterToolsByConfig(tools, {
453+
mcp: { disabledTools: ['not_a_real_tool_name'] },
454+
});
455+
expect(filtered).toHaveLength(tools.length);
456+
});
426457
});
427458

428459
describe('Atomic Writes', () => {

0 commit comments

Comments
 (0)