@sharc-code/mcp 0.2.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.
Files changed (50) hide show
  1. package/README.md +134 -0
  2. package/dist/backend-client.d.ts +251 -0
  3. package/dist/backend-client.d.ts.map +1 -0
  4. package/dist/backend-client.js +269 -0
  5. package/dist/backend-client.js.map +1 -0
  6. package/dist/backend-handlers.d.ts +243 -0
  7. package/dist/backend-handlers.d.ts.map +1 -0
  8. package/dist/backend-handlers.js +1453 -0
  9. package/dist/backend-handlers.js.map +1 -0
  10. package/dist/config.d.ts +47 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +94 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +344 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/project-detector.d.ts +42 -0
  19. package/dist/project-detector.d.ts.map +1 -0
  20. package/dist/project-detector.js +135 -0
  21. package/dist/project-detector.js.map +1 -0
  22. package/dist/utils/env-manager.d.ts +19 -0
  23. package/dist/utils/env-manager.d.ts.map +1 -0
  24. package/dist/utils/env-manager.js +99 -0
  25. package/dist/utils/env-manager.js.map +1 -0
  26. package/dist/utils.d.ts +10 -0
  27. package/dist/utils.d.ts.map +1 -0
  28. package/dist/utils.js +27 -0
  29. package/dist/utils.js.map +1 -0
  30. package/dist/watcher/file-watcher.d.ts +64 -0
  31. package/dist/watcher/file-watcher.d.ts.map +1 -0
  32. package/dist/watcher/file-watcher.js +263 -0
  33. package/dist/watcher/file-watcher.js.map +1 -0
  34. package/dist/watcher/incremental-indexer.d.ts +68 -0
  35. package/dist/watcher/incremental-indexer.d.ts.map +1 -0
  36. package/dist/watcher/incremental-indexer.js +254 -0
  37. package/dist/watcher/incremental-indexer.js.map +1 -0
  38. package/dist/watcher/index.d.ts +10 -0
  39. package/dist/watcher/index.d.ts.map +1 -0
  40. package/dist/watcher/index.js +10 -0
  41. package/dist/watcher/index.js.map +1 -0
  42. package/dist/watcher/processing-queue.d.ts +79 -0
  43. package/dist/watcher/processing-queue.d.ts.map +1 -0
  44. package/dist/watcher/processing-queue.js +150 -0
  45. package/dist/watcher/processing-queue.js.map +1 -0
  46. package/dist/watcher/syntax-guard.d.ts +59 -0
  47. package/dist/watcher/syntax-guard.d.ts.map +1 -0
  48. package/dist/watcher/syntax-guard.js +136 -0
  49. package/dist/watcher/syntax-guard.js.map +1 -0
  50. package/package.json +52 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Processing Queue for File Changes
3
+ *
4
+ * Handles race conditions when files change rapidly:
5
+ * - Deduplicates by file path (latest event wins)
6
+ * - Processes one file at a time per queue
7
+ * - Allows concurrent processing across different codebases
8
+ */
9
+ export type FileChangeType = 'add' | 'modify' | 'delete';
10
+ export interface FileChange {
11
+ type: FileChangeType;
12
+ relativePath: string;
13
+ absolutePath: string;
14
+ }
15
+ export interface QueuedChange {
16
+ change: FileChange;
17
+ timestamp: number;
18
+ }
19
+ export interface ProcessingResult {
20
+ success: boolean;
21
+ chunksDeleted: number;
22
+ chunksIndexed: number;
23
+ skipped?: boolean;
24
+ error?: string;
25
+ }
26
+ export type ChangeProcessor = (change: FileChange) => Promise<ProcessingResult>;
27
+ /**
28
+ * Processing queue that deduplicates file changes by path
29
+ *
30
+ * When a file changes multiple times before processing:
31
+ * - Old events are discarded
32
+ * - Only the latest event is processed
33
+ * - This prevents wasted work and race conditions
34
+ */
35
+ export declare class ProcessingQueue {
36
+ private queue;
37
+ private isProcessing;
38
+ private processingPath;
39
+ private pendingReprocess;
40
+ private processor;
41
+ private verbose;
42
+ constructor(processor: ChangeProcessor, verbose?: boolean);
43
+ /**
44
+ * Add a file change to the queue
45
+ * If the file is already queued, replaces with the new event
46
+ */
47
+ enqueue(change: FileChange): void;
48
+ /**
49
+ * Get the number of pending changes
50
+ */
51
+ getPendingCount(): number;
52
+ /**
53
+ * Check if the queue is idle (not processing and no pending)
54
+ */
55
+ isIdle(): boolean;
56
+ /**
57
+ * Get the currently processing path
58
+ */
59
+ getCurrentlyProcessing(): string | null;
60
+ /**
61
+ * Process the next item in the queue
62
+ * Only one item is processed at a time
63
+ */
64
+ private tryProcessNext;
65
+ /**
66
+ * Get the oldest entry from the queue (FIFO order)
67
+ */
68
+ private getOldestEntry;
69
+ /**
70
+ * Clear the queue (for shutdown)
71
+ */
72
+ clear(): void;
73
+ /**
74
+ * Wait for the queue to become idle
75
+ * Useful for graceful shutdown
76
+ */
77
+ waitForIdle(timeoutMs?: number): Promise<boolean>;
78
+ }
79
+ //# sourceMappingURL=processing-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processing-queue.d.ts","sourceRoot":"","sources":["../../src/watcher/processing-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhF;;;;;;;GAOG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,OAAO,CAAU;gBAEb,SAAS,EAAE,eAAe,EAAE,OAAO,GAAE,OAAe;IAKhE;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAyBjC;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,sBAAsB,IAAI,MAAM,GAAG,IAAI;IAIvC;;;OAGG;YACW,cAAc;IA8C5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;;OAGG;IACG,WAAW,CAAC,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,OAAO,CAAC;CAYjE"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Processing Queue for File Changes
3
+ *
4
+ * Handles race conditions when files change rapidly:
5
+ * - Deduplicates by file path (latest event wins)
6
+ * - Processes one file at a time per queue
7
+ * - Allows concurrent processing across different codebases
8
+ */
9
+ /**
10
+ * Processing queue that deduplicates file changes by path
11
+ *
12
+ * When a file changes multiple times before processing:
13
+ * - Old events are discarded
14
+ * - Only the latest event is processed
15
+ * - This prevents wasted work and race conditions
16
+ */
17
+ export class ProcessingQueue {
18
+ constructor(processor, verbose = false) {
19
+ this.queue = new Map();
20
+ this.isProcessing = false;
21
+ this.processingPath = null;
22
+ this.pendingReprocess = new Set();
23
+ this.processor = processor;
24
+ this.verbose = verbose;
25
+ }
26
+ /**
27
+ * Add a file change to the queue
28
+ * If the file is already queued, replaces with the new event
29
+ */
30
+ enqueue(change) {
31
+ const { relativePath } = change;
32
+ // If this file is currently being processed, mark for reprocess
33
+ if (this.processingPath === relativePath) {
34
+ this.pendingReprocess.add(relativePath);
35
+ if (this.verbose) {
36
+ console.log(`[Queue] File ${relativePath} changed during processing, will reprocess`);
37
+ }
38
+ }
39
+ // Always replace existing - latest event wins
40
+ this.queue.set(relativePath, {
41
+ change,
42
+ timestamp: Date.now(),
43
+ });
44
+ if (this.verbose) {
45
+ console.log(`[Queue] Enqueued ${change.type}: ${relativePath} (queue size: ${this.queue.size})`);
46
+ }
47
+ // Try to process next item
48
+ this.tryProcessNext();
49
+ }
50
+ /**
51
+ * Get the number of pending changes
52
+ */
53
+ getPendingCount() {
54
+ return this.queue.size;
55
+ }
56
+ /**
57
+ * Check if the queue is idle (not processing and no pending)
58
+ */
59
+ isIdle() {
60
+ return !this.isProcessing && this.queue.size === 0;
61
+ }
62
+ /**
63
+ * Get the currently processing path
64
+ */
65
+ getCurrentlyProcessing() {
66
+ return this.processingPath;
67
+ }
68
+ /**
69
+ * Process the next item in the queue
70
+ * Only one item is processed at a time
71
+ */
72
+ async tryProcessNext() {
73
+ if (this.isProcessing || this.queue.size === 0) {
74
+ return;
75
+ }
76
+ this.isProcessing = true;
77
+ // Get oldest entry (FIFO within deduplication)
78
+ const [path, queued] = this.getOldestEntry();
79
+ this.queue.delete(path);
80
+ this.processingPath = path;
81
+ try {
82
+ if (this.verbose) {
83
+ console.log(`[Queue] Processing ${queued.change.type}: ${path}`);
84
+ }
85
+ const result = await this.processor(queued.change);
86
+ if (this.verbose) {
87
+ if (result.success) {
88
+ console.log(`[Queue] Completed ${path}: deleted=${result.chunksDeleted}, indexed=${result.chunksIndexed}`);
89
+ }
90
+ else {
91
+ console.log(`[Queue] Failed ${path}: ${result.error}`);
92
+ }
93
+ }
94
+ }
95
+ catch (error) {
96
+ console.error(`[Queue] Error processing ${path}:`, error);
97
+ }
98
+ finally {
99
+ this.processingPath = null;
100
+ this.isProcessing = false;
101
+ // Check if this file needs reprocessing
102
+ if (this.pendingReprocess.has(path)) {
103
+ this.pendingReprocess.delete(path);
104
+ // The file should already be in queue with updated content
105
+ if (this.verbose) {
106
+ console.log(`[Queue] File ${path} changed during processing, continuing with updated content`);
107
+ }
108
+ }
109
+ // Process next item
110
+ this.tryProcessNext();
111
+ }
112
+ }
113
+ /**
114
+ * Get the oldest entry from the queue (FIFO order)
115
+ */
116
+ getOldestEntry() {
117
+ let oldest = null;
118
+ for (const entry of this.queue.entries()) {
119
+ if (!oldest || entry[1].timestamp < oldest[1].timestamp) {
120
+ oldest = entry;
121
+ }
122
+ }
123
+ if (!oldest) {
124
+ throw new Error('Queue is empty');
125
+ }
126
+ return oldest;
127
+ }
128
+ /**
129
+ * Clear the queue (for shutdown)
130
+ */
131
+ clear() {
132
+ this.queue.clear();
133
+ this.pendingReprocess.clear();
134
+ }
135
+ /**
136
+ * Wait for the queue to become idle
137
+ * Useful for graceful shutdown
138
+ */
139
+ async waitForIdle(timeoutMs = 30000) {
140
+ const startTime = Date.now();
141
+ while (!this.isIdle()) {
142
+ if (Date.now() - startTime > timeoutMs) {
143
+ return false; // Timeout
144
+ }
145
+ await new Promise(resolve => setTimeout(resolve, 100));
146
+ }
147
+ return true;
148
+ }
149
+ }
150
+ //# sourceMappingURL=processing-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processing-queue.js","sourceRoot":"","sources":["../../src/watcher/processing-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Syntax Guard
3
+ *
4
+ * Protects vector database from incomplete/broken code.
5
+ * When a file has syntax errors, we skip re-indexing and keep old vectors.
6
+ *
7
+ * This prevents the scenario where:
8
+ * 1. User saves file mid-typing (incomplete code)
9
+ * 2. Old vectors get deleted
10
+ * 3. New code fails to parse → no new vectors
11
+ * 4. Search results disappear until next successful save
12
+ */
13
+ export interface SyntaxCheckResult {
14
+ hasErrors: boolean;
15
+ errorCount: number;
16
+ language: string;
17
+ supported: boolean;
18
+ }
19
+ /**
20
+ * Syntax Guard class
21
+ *
22
+ * Uses tree-sitter via AST splitter to detect syntax errors.
23
+ * Only checks languages that have tree-sitter grammars.
24
+ */
25
+ export declare class SyntaxGuard {
26
+ private splitter;
27
+ constructor();
28
+ /**
29
+ * Get language from file extension
30
+ */
31
+ getLanguageFromExtension(ext: string): string;
32
+ /**
33
+ * Check if a language is supported for syntax checking
34
+ */
35
+ isLanguageSupported(language: string): boolean;
36
+ /**
37
+ * Check file content for syntax errors
38
+ *
39
+ * @param content - File content to check
40
+ * @param language - Language identifier (e.g., 'typescript', 'python')
41
+ * @returns SyntaxCheckResult with error information
42
+ */
43
+ checkSyntax(content: string, language: string): SyntaxCheckResult;
44
+ /**
45
+ * Check file by extension
46
+ *
47
+ * Convenience method that determines language from extension.
48
+ *
49
+ * @param content - File content to check
50
+ * @param ext - File extension (e.g., '.ts', '.py')
51
+ * @returns SyntaxCheckResult with error information
52
+ */
53
+ checkSyntaxByExtension(content: string, ext: string): SyntaxCheckResult;
54
+ }
55
+ /**
56
+ * Get shared SyntaxGuard instance
57
+ */
58
+ export declare function getSyntaxGuard(): SyntaxGuard;
59
+ //# sourceMappingURL=syntax-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syntax-guard.d.ts","sourceRoot":"","sources":["../../src/watcher/syntax-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAkB;;IAMlC;;OAEG;IACH,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI7C;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI9C;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB;IAgCjE;;;;;;;;OAQG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB;CAI1E;AAKD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Syntax Guard
3
+ *
4
+ * Protects vector database from incomplete/broken code.
5
+ * When a file has syntax errors, we skip re-indexing and keep old vectors.
6
+ *
7
+ * This prevents the scenario where:
8
+ * 1. User saves file mid-typing (incomplete code)
9
+ * 2. Old vectors get deleted
10
+ * 3. New code fails to parse → no new vectors
11
+ * 4. Search results disappear until next successful save
12
+ */
13
+ // Import from @sharc-code/splitter package
14
+ import { AstCodeSplitter } from '@sharc-code/splitter';
15
+ // Languages supported by tree-sitter AST parsing
16
+ const SUPPORTED_LANGUAGES = new Set([
17
+ 'typescript',
18
+ 'javascript',
19
+ 'python',
20
+ 'go',
21
+ 'rust',
22
+ 'java',
23
+ 'c',
24
+ 'cpp',
25
+ 'c_sharp',
26
+ 'scala',
27
+ ]);
28
+ // Extension to language mapping
29
+ const EXTENSION_TO_LANGUAGE = {
30
+ '.ts': 'typescript',
31
+ '.tsx': 'typescript',
32
+ '.js': 'javascript',
33
+ '.jsx': 'javascript',
34
+ '.mjs': 'javascript',
35
+ '.cjs': 'javascript',
36
+ '.py': 'python',
37
+ '.go': 'go',
38
+ '.rs': 'rust',
39
+ '.java': 'java',
40
+ '.c': 'c',
41
+ '.h': 'c',
42
+ '.cpp': 'cpp',
43
+ '.cc': 'cpp',
44
+ '.cxx': 'cpp',
45
+ '.hpp': 'cpp',
46
+ '.hxx': 'cpp',
47
+ '.cs': 'c_sharp',
48
+ '.scala': 'scala',
49
+ '.sc': 'scala',
50
+ };
51
+ /**
52
+ * Syntax Guard class
53
+ *
54
+ * Uses tree-sitter via AST splitter to detect syntax errors.
55
+ * Only checks languages that have tree-sitter grammars.
56
+ */
57
+ export class SyntaxGuard {
58
+ constructor() {
59
+ this.splitter = new AstCodeSplitter();
60
+ }
61
+ /**
62
+ * Get language from file extension
63
+ */
64
+ getLanguageFromExtension(ext) {
65
+ return EXTENSION_TO_LANGUAGE[ext.toLowerCase()] || 'unknown';
66
+ }
67
+ /**
68
+ * Check if a language is supported for syntax checking
69
+ */
70
+ isLanguageSupported(language) {
71
+ return SUPPORTED_LANGUAGES.has(language);
72
+ }
73
+ /**
74
+ * Check file content for syntax errors
75
+ *
76
+ * @param content - File content to check
77
+ * @param language - Language identifier (e.g., 'typescript', 'python')
78
+ * @returns SyntaxCheckResult with error information
79
+ */
80
+ checkSyntax(content, language) {
81
+ // If language is not supported by tree-sitter, allow indexing
82
+ if (!this.isLanguageSupported(language)) {
83
+ return {
84
+ hasErrors: false,
85
+ errorCount: 0,
86
+ language,
87
+ supported: false,
88
+ };
89
+ }
90
+ try {
91
+ const result = this.splitter.checkSyntaxErrors(content, language);
92
+ return {
93
+ hasErrors: result.hasErrors,
94
+ errorCount: result.errorCount,
95
+ language,
96
+ supported: true,
97
+ };
98
+ }
99
+ catch (error) {
100
+ // If parsing fails completely, treat as having errors
101
+ // This is conservative - better to keep old vectors than risk losing them
102
+ console.error(`[SyntaxGuard] Error checking syntax for ${language}:`, error);
103
+ return {
104
+ hasErrors: true,
105
+ errorCount: 1,
106
+ language,
107
+ supported: true,
108
+ };
109
+ }
110
+ }
111
+ /**
112
+ * Check file by extension
113
+ *
114
+ * Convenience method that determines language from extension.
115
+ *
116
+ * @param content - File content to check
117
+ * @param ext - File extension (e.g., '.ts', '.py')
118
+ * @returns SyntaxCheckResult with error information
119
+ */
120
+ checkSyntaxByExtension(content, ext) {
121
+ const language = this.getLanguageFromExtension(ext);
122
+ return this.checkSyntax(content, language);
123
+ }
124
+ }
125
+ // Singleton instance for shared use
126
+ let sharedInstance = null;
127
+ /**
128
+ * Get shared SyntaxGuard instance
129
+ */
130
+ export function getSyntaxGuard() {
131
+ if (!sharedInstance) {
132
+ sharedInstance = new SyntaxGuard();
133
+ }
134
+ return sharedInstance;
135
+ }
136
+ //# sourceMappingURL=syntax-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syntax-guard.js","sourceRoot":"","sources":["../../src/watcher/syntax-guard.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@sharc-code/mcp",
3
+ "version": "0.2.0",
4
+ "description": "SHARC MCP Server - Semantic code search for AI assistants",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": "dist/index.js",
9
+ "scripts": {
10
+ "build": "bun run clean && tsc --build --force",
11
+ "dev": "tsx --watch src/index.ts",
12
+ "clean": "rimraf dist",
13
+ "lint": "eslint src --ext .ts",
14
+ "lint:fix": "eslint src --ext .ts --fix",
15
+ "typecheck": "tsc --noEmit",
16
+ "start": "tsx src/index.ts",
17
+ "start:with-env": "OPENAI_API_KEY=${OPENAI_API_KEY:your-api-key-here} MILVUS_ADDRESS=${MILVUS_ADDRESS:localhost:19530} tsx src/index.ts",
18
+ "prepublishOnly": "bun run build"
19
+ },
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.12.1",
22
+ "@sharc-code/splitter": "^0.2.0",
23
+ "chokidar": "^3.6.0",
24
+ "dotenv": "^17.2.3",
25
+ "zod": "^4.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "tsx": "^4.19.4",
30
+ "typescript": "^5.0.0"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md"
35
+ ],
36
+ "keywords": [
37
+ "code-search",
38
+ "semantic-search",
39
+ "mcp",
40
+ "model-context-protocol",
41
+ "ai",
42
+ "embeddings",
43
+ "tree-sitter",
44
+ "ast"
45
+ ],
46
+ "author": "SHARC <npm@sharc.co>",
47
+ "homepage": "https://sharc.co",
48
+ "license": "MIT",
49
+ "publishConfig": {
50
+ "access": "public"
51
+ }
52
+ }