@dudamel/mcp-context 0.1.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.
@@ -0,0 +1,70 @@
1
+ import type { DirectoryCatalog, FileReadResponse, GrepHit, ReadCsvResponse, ReadSectionResponse, ReadYamlResponse, SearchResult, StatResponse, TailResponse, TreeResponse } from './types.js';
2
+ export interface GrepRequest {
3
+ pattern: string;
4
+ path?: string;
5
+ maxResults?: number;
6
+ fixedString?: boolean;
7
+ ignoreCase?: boolean;
8
+ }
9
+ export interface FindRequest {
10
+ name: string;
11
+ path?: string;
12
+ maxResults?: number;
13
+ type?: 'f' | 'd';
14
+ }
15
+ /**
16
+ * HTTP client for the dudamel context-server. The context-server is the single
17
+ * source of truth for the VFS — directory layout, per-directory .index.yaml
18
+ * catalogs, and on-disk files. The MCP adapts those HTTP endpoints into tool
19
+ * calls for the LLM.
20
+ */
21
+ export declare class ContextServerError extends Error {
22
+ readonly status: number;
23
+ readonly url: string;
24
+ constructor(message: string, status: number, url: string);
25
+ }
26
+ export interface ContextServerClientOptions {
27
+ /**
28
+ * Base URL for the HTTP backend.
29
+ * - direct: the context-server itself (e.g. http://context-server:3001).
30
+ * - proxy: orchestrator base URL (we append /api/context to paths).
31
+ */
32
+ baseUrl: string;
33
+ /** When set, switches to proxy mode and adds Authorization: ApiKey <apiKey>. */
34
+ apiKey?: string;
35
+ }
36
+ export declare class ContextServerClient {
37
+ private readonly baseUrl;
38
+ private readonly apiKey?;
39
+ private readonly pathPrefix;
40
+ constructor(options: ContextServerClientOptions);
41
+ private url;
42
+ private headers;
43
+ private get;
44
+ private post;
45
+ grep(req: GrepRequest): Promise<SearchResult<GrepHit>>;
46
+ find(req: FindRequest): Promise<SearchResult<string>>;
47
+ stat(path: string): Promise<StatResponse>;
48
+ tree(path?: string, maxDepth?: number): Promise<TreeResponse>;
49
+ tail(path: string, lines?: number): Promise<TailResponse>;
50
+ readSection(path: string, heading?: string): Promise<ReadSectionResponse>;
51
+ readYaml(path: string, keyPath?: string): Promise<ReadYamlResponse>;
52
+ readCsv(path: string, opts?: {
53
+ filterColumn?: string;
54
+ filterValue?: string;
55
+ columns?: string[];
56
+ maxRows?: number;
57
+ }): Promise<ReadCsvResponse>;
58
+ /**
59
+ * Fetch a file's raw text from the VFS. Path is VFS-relative:
60
+ * "AGENTS.md" -> /data/context/AGENTS.md
61
+ * "src/foo" -> /data/context/src/foo
62
+ */
63
+ readFile(vfsPath: string): Promise<FileReadResponse>;
64
+ /**
65
+ * Parsed .index.yaml for a directory.
66
+ * `dirPath` is VFS-relative: "source" or "source/sub/dir".
67
+ */
68
+ readDirectoryCatalog(dirPath: string): Promise<DirectoryCatalog>;
69
+ }
70
+ //# sourceMappingURL=context-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-client.d.ts","sourceRoot":"","sources":["../src/context-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,OAAO,EACP,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAGzC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAFpB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM;CAKvB;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,EAAE,0BAA0B;IAM/C,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,OAAO;YAMD,GAAG;YAcH,IAAI;IAkBlB,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAItD,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAIrD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIzC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIzD,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIzE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAInE,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3F,OAAO,CAAC,eAAe,CAAC;IAI3B;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY1D;;;OAGG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAMvE"}
@@ -0,0 +1,118 @@
1
+ import yaml from 'js-yaml';
2
+ /**
3
+ * HTTP client for the dudamel context-server. The context-server is the single
4
+ * source of truth for the VFS — directory layout, per-directory .index.yaml
5
+ * catalogs, and on-disk files. The MCP adapts those HTTP endpoints into tool
6
+ * calls for the LLM.
7
+ */
8
+ export class ContextServerError extends Error {
9
+ status;
10
+ url;
11
+ constructor(message, status, url) {
12
+ super(message);
13
+ this.status = status;
14
+ this.url = url;
15
+ this.name = 'ContextServerError';
16
+ }
17
+ }
18
+ export class ContextServerClient {
19
+ baseUrl;
20
+ apiKey;
21
+ pathPrefix;
22
+ constructor(options) {
23
+ this.baseUrl = options.baseUrl.replace(/\/+$/, '');
24
+ this.apiKey = options.apiKey;
25
+ this.pathPrefix = this.apiKey ? '/api/context' : '';
26
+ }
27
+ url(path) {
28
+ if (!path.startsWith('/'))
29
+ throw new Error(`ContextServerClient path must start with "/", got "${path}"`);
30
+ return `${this.baseUrl}${this.pathPrefix}${path}`;
31
+ }
32
+ headers(extra) {
33
+ const out = { ...(extra ?? {}) };
34
+ if (this.apiKey)
35
+ out['Authorization'] = `ApiKey ${this.apiKey}`;
36
+ return out;
37
+ }
38
+ async get(path) {
39
+ const target = this.url(path);
40
+ const res = await fetch(target, { method: 'GET', headers: this.headers() });
41
+ if (!res.ok) {
42
+ const body = await res.text().catch(() => '');
43
+ throw new ContextServerError(`GET ${path} -> ${res.status}${body ? `: ${body}` : ''}`, res.status, target);
44
+ }
45
+ return (await res.json());
46
+ }
47
+ async post(path, body) {
48
+ const target = this.url(path);
49
+ const res = await fetch(target, {
50
+ method: 'POST',
51
+ headers: this.headers({ 'Content-Type': 'application/json' }),
52
+ body: JSON.stringify(body ?? {}),
53
+ });
54
+ if (!res.ok) {
55
+ const text = await res.text().catch(() => '');
56
+ throw new ContextServerError(`POST ${path} -> ${res.status}${text ? `: ${text}` : ''}`, res.status, target);
57
+ }
58
+ return (await res.json());
59
+ }
60
+ grep(req) {
61
+ return this.post('/search/grep', req);
62
+ }
63
+ find(req) {
64
+ return this.post('/search/find', req);
65
+ }
66
+ stat(path) {
67
+ return this.post('/search/stat', { path });
68
+ }
69
+ tree(path, maxDepth) {
70
+ return this.post('/search/tree', { path, maxDepth });
71
+ }
72
+ tail(path, lines) {
73
+ return this.post('/search/tail', { path, lines });
74
+ }
75
+ readSection(path, heading) {
76
+ return this.post('/search/section', { path, heading });
77
+ }
78
+ readYaml(path, keyPath) {
79
+ return this.post('/search/yaml', { path, keyPath });
80
+ }
81
+ readCsv(path, opts) {
82
+ return this.post('/search/csv', { path, ...opts });
83
+ }
84
+ /**
85
+ * Fetch a file's raw text from the VFS. Path is VFS-relative:
86
+ * "AGENTS.md" -> /data/context/AGENTS.md
87
+ * "src/foo" -> /data/context/src/foo
88
+ */
89
+ async readFile(vfsPath) {
90
+ const segments = vfsPath.split('/').filter((s) => s.length > 0);
91
+ if (segments.length === 0)
92
+ throw new Error('Path cannot be empty');
93
+ const [sourceName, ...rest] = segments;
94
+ const encoded = encodeURIComponent(sourceName);
95
+ const url = rest.length === 0
96
+ ? `/read/${encoded}`
97
+ : `/read/${encoded}/${rest.map(encodeURIComponent).join('/')}`;
98
+ return this.get(url);
99
+ }
100
+ /**
101
+ * Parsed .index.yaml for a directory.
102
+ * `dirPath` is VFS-relative: "source" or "source/sub/dir".
103
+ */
104
+ async readDirectoryCatalog(dirPath) {
105
+ const trimmed = dirPath.replace(/\/$/, '');
106
+ const catalogPath = trimmed.length === 0 ? '.index.yaml' : `${trimmed}/.index.yaml`;
107
+ const raw = await this.readFile(catalogPath);
108
+ return parseCatalog(raw.content);
109
+ }
110
+ }
111
+ function parseCatalog(text) {
112
+ const parsed = yaml.load(text);
113
+ if (parsed == null || typeof parsed !== 'object') {
114
+ throw new Error(`Catalog YAML did not parse into an object: ${typeof parsed}`);
115
+ }
116
+ return parsed;
117
+ }
118
+ //# sourceMappingURL=context-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-client.js","sourceRoot":"","sources":["../src/context-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { createContextServer } from './server.js';
2
+ export { ContextServerClient, ContextServerError } from './context-client.js';
3
+ export type { McpContextConfig, CatalogEntry, DirectoryCatalog, FileReadResponse } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createContextServer } from './server.js';
2
+ export { ContextServerClient, ContextServerError } from './context-client.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { McpContextConfig } from './types.js';
3
+ export declare function createContextServer(config: McpContextConfig): Promise<McpServer>;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiEnD,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CA8BtF"}
package/dist/server.js ADDED
@@ -0,0 +1,90 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { ContextServerClient } from './context-client.js';
3
+ import { registerInstructionsTool } from './tools/instructions.js';
4
+ import { registerListSourcesTool, registerBrowseTool, registerTreeTool } from './tools/navigate.js';
5
+ import { registerReadFileTool, registerStatTool, registerTailTool, registerReadSectionTool, registerReadYamlTool, registerReadCsvTool } from './tools/read.js';
6
+ import { registerGrepTool, registerFindTool } from './tools/search.js';
7
+ const BASE_INSTRUCTIONS = `
8
+ Dudamel Context MCP — read-only access to the organization's knowledge base (the VFS).
9
+
10
+ Call \`get_instructions\` on your first interaction for background on the .index.yaml
11
+ catalog convention. Not required — the tools work without it, but it helps you
12
+ understand the data model the first time.
13
+
14
+ Typical workflow:
15
+ 1. get_instructions — understand the conventions
16
+ 2. list_sources — see what knowledge bases exist
17
+ 3. browse <path> — explore a source; read catalog descriptions
18
+ 4. tree <path> — compact recursive map of a subtree in one call
19
+ 5. stat <path> — check size / line count before reading large files
20
+ 6. read_file <path> — fetch actual content when it's relevant
21
+ 7. read_section <path, h> — pull one markdown section instead of the whole file
22
+ 8. read_yaml <path, key> — navigate a YAML file by dot-path key
23
+ 9. read_csv <path, filter> — query a CSV file with column filtering
24
+ 10. tail <path> — peek at the end of a file (logs, recent entries)
25
+ 11. grep / find — search content or filenames across the VFS
26
+
27
+ Prefer reading catalog descriptions (via \`browse\`) over reading full files —
28
+ catalogs are dense summaries. Use \`grep\`/\`find\` to locate things you don't
29
+ already have a path for; scope them with \`path\` when you can.
30
+ `.trim();
31
+ /**
32
+ * Fetch the root catalog and render a "available sources" section the LLM can
33
+ * match against. Descriptions are generated by the indexer from actual source
34
+ * content, so this is how the server tells the model what topics it covers
35
+ * without anyone hardcoding "use this for Luna Lines" — the data is per-org.
36
+ * Falls back silently to the static base instructions if the catalog can't
37
+ * be reached (fresh deploy, network hiccup, etc.).
38
+ */
39
+ async function buildInstructions(client) {
40
+ try {
41
+ const catalog = await client.readDirectoryCatalog('');
42
+ const entries = catalog.entries ?? [];
43
+ if (entries.length === 0)
44
+ return BASE_INSTRUCTIONS;
45
+ const sourceLines = entries
46
+ .map((e) => `- **${e.path}** — ${e.description || '(no description yet — call browse for details)'}`)
47
+ .join('\n');
48
+ const discovery = `
49
+ ## Available knowledge sources
50
+
51
+ This server is the authoritative source for the topics below. **When the user
52
+ asks about anything that overlaps with these descriptions, use this server's
53
+ tools before answering from memory** — call \`browse\`/\`grep\`/\`read_file\`
54
+ to confirm the current state rather than guessing.
55
+
56
+ ${sourceLines}
57
+ `.trim();
58
+ return `${BASE_INSTRUCTIONS}\n\n${discovery}`;
59
+ }
60
+ catch {
61
+ // Best-effort — if we can't reach the context-server at init time, ship
62
+ // the static instructions. Subsequent tool calls will fail the same way
63
+ // and surface the real error to the LLM.
64
+ return BASE_INSTRUCTIONS;
65
+ }
66
+ }
67
+ export async function createContextServer(config) {
68
+ const client = config.mode === 'proxy'
69
+ ? new ContextServerClient({ baseUrl: config.baseUrl, apiKey: config.apiKey })
70
+ : new ContextServerClient({ baseUrl: config.baseUrl });
71
+ const instructions = await buildInstructions(client);
72
+ const server = new McpServer({ name: 'dudamel-context', version: '0.1.0' }, {
73
+ capabilities: { tools: {} },
74
+ instructions,
75
+ });
76
+ registerInstructionsTool(server, client);
77
+ registerListSourcesTool(server, client);
78
+ registerBrowseTool(server, client);
79
+ registerTreeTool(server, client);
80
+ registerReadFileTool(server, client);
81
+ registerReadSectionTool(server, client);
82
+ registerReadYamlTool(server, client);
83
+ registerReadCsvTool(server, client);
84
+ registerStatTool(server, client);
85
+ registerTailTool(server, client);
86
+ registerGrepTool(server, client);
87
+ registerFindTool(server, client);
88
+ return server;
89
+ }
90
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stdio entry point for the Dudamel Context MCP.
4
+ *
5
+ * Two mutually-exclusive connection modes:
6
+ *
7
+ * 1. Remote (preferred for external clients: Claude Code, Cursor, …):
8
+ * DUDAMEL_API_URL — e.g. https://dudamel.example.com
9
+ * DUDAMEL_API_KEY — API key for authentication
10
+ * Goes through the authenticated /api/context proxy on the orchestrator.
11
+ *
12
+ * 2. Direct (internal, inside the compose network):
13
+ * DUDAMEL_CONTEXT_SERVER_URL — defaults to http://context-server:3001
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=stdio-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio-server.d.ts","sourceRoot":"","sources":["../src/stdio-server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stdio entry point for the Dudamel Context MCP.
4
+ *
5
+ * Two mutually-exclusive connection modes:
6
+ *
7
+ * 1. Remote (preferred for external clients: Claude Code, Cursor, …):
8
+ * DUDAMEL_API_URL — e.g. https://dudamel.example.com
9
+ * DUDAMEL_API_KEY — API key for authentication
10
+ * Goes through the authenticated /api/context proxy on the orchestrator.
11
+ *
12
+ * 2. Direct (internal, inside the compose network):
13
+ * DUDAMEL_CONTEXT_SERVER_URL — defaults to http://context-server:3001
14
+ */
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+ import { createContextServer } from './server.js';
17
+ function resolveConfig() {
18
+ const apiUrl = process.env.DUDAMEL_API_URL?.trim();
19
+ const apiKey = process.env.DUDAMEL_API_KEY?.trim();
20
+ if (apiUrl && apiKey) {
21
+ return { mode: 'proxy', baseUrl: apiUrl, apiKey };
22
+ }
23
+ if (apiUrl && !apiKey) {
24
+ process.stderr.write('[dudamel-mcp-context] DUDAMEL_API_URL is set but DUDAMEL_API_KEY is missing — refusing to connect without auth.\n');
25
+ process.exit(2);
26
+ }
27
+ const direct = process.env.DUDAMEL_CONTEXT_SERVER_URL?.trim() || 'http://context-server:3001';
28
+ return { mode: 'direct', baseUrl: direct };
29
+ }
30
+ async function main() {
31
+ const config = resolveConfig();
32
+ const server = await createContextServer(config);
33
+ const transport = new StdioServerTransport();
34
+ await server.connect(transport);
35
+ // The SDK keeps the event loop alive while the transport is open; exit on
36
+ // stdin EOF so the process doesn't hang when the parent goes away.
37
+ process.stdin.on('end', () => process.exit(0));
38
+ }
39
+ void main().catch((error) => {
40
+ // Stdio MCPs must never write to stdout outside the protocol; stderr is safe.
41
+ process.stderr.write(`[dudamel-mcp-context] fatal: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
42
+ process.exit(1);
43
+ });
44
+ //# sourceMappingURL=stdio-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio-server.js","sourceRoot":"","sources":["../src/stdio-server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,SAAS,aAAa;IACpB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAEnD,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mHAAmH,CAAC,CAAC;QAC1I,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE,IAAI,EAAE,IAAI,4BAA4B,CAAC;IAC9F,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,0EAA0E;IAC1E,mEAAmE;IACnE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAC1G,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ContextServerClient } from '../context-client.js';
3
+ /**
4
+ * `get_instructions` — returns the AGENTS.md file at the VFS root, which
5
+ * teaches the agent the `.index.yaml` navigation pattern. Every other tool's
6
+ * description points here, and the server `instructions` field also tells the
7
+ * LLM to call this first.
8
+ */
9
+ export declare function registerInstructionsTool(server: McpServer, client: ContextServerClient): void;
10
+ //# sourceMappingURL=instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/tools/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAoC7F"}
@@ -0,0 +1,40 @@
1
+ import { ContextServerError } from '../context-client.js';
2
+ /**
3
+ * `get_instructions` — returns the AGENTS.md file at the VFS root, which
4
+ * teaches the agent the `.index.yaml` navigation pattern. Every other tool's
5
+ * description points here, and the server `instructions` field also tells the
6
+ * LLM to call this first.
7
+ */
8
+ export function registerInstructionsTool(server, client) {
9
+ server.tool('get_instructions', 'Read the usage guide for this knowledge base (AGENTS.md). Recommended on your first interaction — it explains the .index.yaml catalog structure and navigation conventions. Not required: `list_sources` and `browse` work without it.', {}, async () => {
10
+ try {
11
+ const result = await client.readFile('AGENTS.md');
12
+ return {
13
+ content: [{ type: 'text', text: result.content }],
14
+ };
15
+ }
16
+ catch (error) {
17
+ if (error instanceof ContextServerError && error.status === 404) {
18
+ return {
19
+ content: [
20
+ {
21
+ type: 'text',
22
+ text: 'No AGENTS.md was found at the VFS root. The admin may not have seeded one yet. ' +
23
+ 'For reference, each directory should contain a .index.yaml like:\n\n' +
24
+ '```yaml\n' +
25
+ 'description: "Summary of this directory"\n' +
26
+ 'entries:\n' +
27
+ ' - path: file.md\n' +
28
+ ' type: file\n' +
29
+ ' description: "Summary of this file"\n' +
30
+ '```\n\n' +
31
+ 'Start by calling `list_sources`, then `browse` to drill down, and `read_file` for actual content.',
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ throw error;
37
+ }
38
+ });
39
+ }
40
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/tools/instructions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,MAA2B;IACrF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,wOAAwO,EACxO,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAClD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;aAClD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,kBAAkB,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAChE,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EACF,iFAAiF;gCACjF,sEAAsE;gCACtE,WAAW;gCACX,4CAA4C;gCAC5C,YAAY;gCACZ,qBAAqB;gCACrB,kBAAkB;gCAClB,2CAA2C;gCAC3C,SAAS;gCACT,mGAAmG;yBACtG;qBACF;iBACF,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ContextServerClient } from '../context-client.js';
3
+ /**
4
+ * `list_sources` — top-level entry point. Returns the source catalog
5
+ * generated from /data/context/.index.yaml, where each source is a
6
+ * directory with its own subtree and `.index.yaml`.
7
+ */
8
+ export declare function registerListSourcesTool(server: McpServer, client: ContextServerClient): void;
9
+ /**
10
+ * `browse` — the ls-with-descriptions primitive. Returns the description of
11
+ * the directory itself plus the entries inside (files and subdirectories),
12
+ * each with their own description. One tool call per directory is enough to
13
+ * decide what to drill into next.
14
+ */
15
+ export declare function registerBrowseTool(server: McpServer, client: ContextServerClient): void;
16
+ /**
17
+ * `tree` — a compact recursive listing of a VFS subtree. One call gives a
18
+ * full picture of everything available under `path`, up to `maxDepth` levels.
19
+ * Rendered as an indented outline so the LLM can scan it line-by-line.
20
+ */
21
+ export declare function registerTreeTool(server: McpServer, client: ContextServerClient): void;
22
+ //# sourceMappingURL=navigate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigate.d.ts","sourceRoot":"","sources":["../../src/tools/navigate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAIhE;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAsB5F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAkDvF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAmCrF"}
@@ -0,0 +1,121 @@
1
+ import { z } from 'zod';
2
+ import { ContextServerError } from '../context-client.js';
3
+ /**
4
+ * `list_sources` — top-level entry point. Returns the source catalog
5
+ * generated from /data/context/.index.yaml, where each source is a
6
+ * directory with its own subtree and `.index.yaml`.
7
+ */
8
+ export function registerListSourcesTool(server, client) {
9
+ server.tool('list_sources', 'List the top-level knowledge sources in this VFS. Each source is a directory at the VFS root with its own documentation tree. Use this to discover what is available, then call `browse` on a source to explore it. If you have not read the usage guide yet, call `get_instructions` first.', {}, async () => {
10
+ const catalog = await client.readDirectoryCatalog('');
11
+ const entries = catalog.entries ?? [];
12
+ if (entries.length === 0) {
13
+ return {
14
+ content: [{ type: 'text', text: 'No sources are configured in the VFS yet.' }],
15
+ };
16
+ }
17
+ const text = entries
18
+ .map((e) => `- **${e.path}** — ${e.description || '(no description)'}`)
19
+ .join('\n');
20
+ return { content: [{ type: 'text', text }] };
21
+ });
22
+ }
23
+ /**
24
+ * `browse` — the ls-with-descriptions primitive. Returns the description of
25
+ * the directory itself plus the entries inside (files and subdirectories),
26
+ * each with their own description. One tool call per directory is enough to
27
+ * decide what to drill into next.
28
+ */
29
+ export function registerBrowseTool(server, client) {
30
+ server.tool('browse', 'List the contents of a directory in the VFS with a description for each entry. Path is VFS-relative, e.g. "my-source" or "my-source/subdir". Omit `path` to browse the root. Prefer this over `read_file` for navigation — reading catalog descriptions is much cheaper than reading every file.', {
31
+ path: z
32
+ .string()
33
+ .optional()
34
+ .describe('VFS path to the directory. Examples: "my-source", "my-source/tech", "". Leave empty for the root.'),
35
+ }, async ({ path }) => {
36
+ const normalised = (path ?? '').replace(/\/$/, '');
37
+ try {
38
+ const catalog = await client.readDirectoryCatalog(normalised);
39
+ const lines = [
40
+ normalised === '' ? '# / (root)' : `# /${normalised}`,
41
+ '',
42
+ catalog.description ? catalog.description : '(no directory description)',
43
+ '',
44
+ '## Entries',
45
+ ];
46
+ if (!catalog.entries || catalog.entries.length === 0) {
47
+ lines.push('_(empty)_');
48
+ }
49
+ else {
50
+ for (const entry of catalog.entries) {
51
+ lines.push(`- **${entry.path}** (${entry.type}) — ${entry.description || '(no description)'}`);
52
+ }
53
+ }
54
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
55
+ }
56
+ catch (error) {
57
+ if (error instanceof ContextServerError && error.status === 404) {
58
+ return {
59
+ content: [
60
+ {
61
+ type: 'text',
62
+ text: `No directory catalog found at "${normalised}". The path may not exist or may not have been indexed yet. Try \`list_sources\` to see what's available.`,
63
+ },
64
+ ],
65
+ isError: true,
66
+ };
67
+ }
68
+ throw error;
69
+ }
70
+ });
71
+ }
72
+ /**
73
+ * `tree` — a compact recursive listing of a VFS subtree. One call gives a
74
+ * full picture of everything available under `path`, up to `maxDepth` levels.
75
+ * Rendered as an indented outline so the LLM can scan it line-by-line.
76
+ */
77
+ export function registerTreeTool(server, client) {
78
+ server.tool('tree', 'Recursively list directories + files under a VFS path, with depth information. Returns a compact outline — much cheaper than chained `browse` calls when you want a broad map. Defaults to depth 3; max 10.', {
79
+ path: z
80
+ .string()
81
+ .optional()
82
+ .describe('VFS path to the subtree root. Examples: "my-source", "my-source/tech". Omit for the whole VFS.'),
83
+ maxDepth: z
84
+ .number()
85
+ .int()
86
+ .min(1)
87
+ .max(10)
88
+ .optional()
89
+ .describe('How many levels deep to descend (default 3, hard cap 10).'),
90
+ }, async ({ path, maxDepth }) => {
91
+ const result = await client.tree(path, maxDepth);
92
+ if (result.entries.length === 0) {
93
+ return {
94
+ content: [
95
+ {
96
+ type: 'text',
97
+ text: `No entries under ${result.root ? `"${result.root}"` : 'the VFS root'} (or it doesn't exist).`,
98
+ },
99
+ ],
100
+ };
101
+ }
102
+ const lines = formatTree(result.entries);
103
+ const footer = result.truncated ? '\n\n(truncated — pass a more specific `path` or a smaller `maxDepth`)' : '';
104
+ const header = result.root ? `# /${result.root}\n` : '# / (root)\n';
105
+ return { content: [{ type: 'text', text: header + lines + footer }] };
106
+ });
107
+ }
108
+ function formatTree(entries) {
109
+ return entries
110
+ .map((e) => {
111
+ const indent = ' '.repeat(Math.max(0, e.depth - 1));
112
+ const suffix = e.type === 'directory' ? '/' : '';
113
+ // Show only the leaf name so the indent carries hierarchy; keep full path
114
+ // available for follow-up tool calls via the leading ` ` (agents can
115
+ // reconstruct it or we can be explicit). Being explicit is cheaper to
116
+ // read — emit the full path.
117
+ return `${indent}- ${e.path}${suffix}`;
118
+ })
119
+ .join('\n');
120
+ }
121
+ //# sourceMappingURL=navigate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigate.js","sourceRoot":"","sources":["../../src/tools/navigate.ts"],"names":[],"mappings":"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"}