lance-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.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # lance-context
2
+
3
+ An MCP plugin that adds semantic code search to Claude Code and other AI coding agents, giving them deep context from your entire codebase.
4
+
5
+ ## Features
6
+
7
+ - **Semantic Code Search**: Natural language queries locate relevant code across your entire codebase
8
+ - **Multiple Embedding Backends**: Jina v3 API, local sentence-transformers, or Ollama
9
+ - **LanceDB Vector Storage**: Fast, efficient vector search with hybrid BM25 + dense matching
10
+ - **MCP Compatible**: Works with Claude Code, Cursor, and other MCP-compatible tools
11
+
12
+ ## Installation
13
+
14
+ ### For Claude Code
15
+
16
+ ```bash
17
+ claude mcp add lance-context -- npx lance-context
18
+ ```
19
+
20
+ Or with a specific project path:
21
+
22
+ ```bash
23
+ claude mcp add lance-context -- npx lance-context --project /path/to/your/project
24
+ ```
25
+
26
+ ### Manual Installation
27
+
28
+ ```bash
29
+ npm install -g lance-context
30
+ ```
31
+
32
+ ## Configuration
33
+
34
+ ### Embedding Backends
35
+
36
+ lance-context automatically selects the best available backend:
37
+
38
+ 1. **Jina v3** (highest quality, free tier available)
39
+ ```bash
40
+ export JINA_API_KEY=your-api-key
41
+ ```
42
+
43
+ 2. **Ollama** (local, privacy-preserving)
44
+ ```bash
45
+ # Make sure Ollama is running with nomic-embed-text
46
+ ollama pull nomic-embed-text
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ Once installed, you'll have access to these tools:
52
+
53
+ ### `index_codebase`
54
+
55
+ Index your codebase for semantic search:
56
+
57
+ ```
58
+ > index_codebase
59
+ Indexed 150 files, created 800 chunks.
60
+ ```
61
+
62
+ With custom patterns:
63
+
64
+ ```
65
+ > index_codebase(patterns: ["**/*.py"], excludePatterns: ["**/tests/**"])
66
+ ```
67
+
68
+ ### `search_code`
69
+
70
+ Search using natural language:
71
+
72
+ ```
73
+ > search_code(query: "authentication middleware")
74
+
75
+ ## Result 1: src/middleware/auth.ts:1-50
76
+ ...
77
+ ```
78
+
79
+ ### `get_index_status`
80
+
81
+ Check index status:
82
+
83
+ ```
84
+ > get_index_status
85
+ {
86
+ "indexed": true,
87
+ "fileCount": 150,
88
+ "chunkCount": 800,
89
+ "lastUpdated": "2024-12-27T12:00:00Z"
90
+ }
91
+ ```
92
+
93
+ ### `clear_index`
94
+
95
+ Clear the index:
96
+
97
+ ```
98
+ > clear_index
99
+ Index cleared.
100
+ ```
101
+
102
+ ## How It Works
103
+
104
+ 1. **Indexing**: Code files are chunked into ~100-line segments with overlap
105
+ 2. **Embedding**: Each chunk is converted to a vector using your chosen backend
106
+ 3. **Storage**: Vectors are stored in LanceDB (`.lance-context/` directory)
107
+ 4. **Search**: Natural language queries are embedded and matched against stored vectors
108
+
109
+ ## Environment Variables
110
+
111
+ | Variable | Description | Default |
112
+ |----------|-------------|---------|
113
+ | `JINA_API_KEY` | Jina AI API key for embeddings | - |
114
+ | `OLLAMA_URL` | Ollama server URL | `http://localhost:11434` |
115
+ | `LANCE_CONTEXT_PROJECT` | Project path to index | Current directory |
116
+
117
+ ## Supported Languages
118
+
119
+ TypeScript, JavaScript, Python, Go, Rust, Java, Ruby, PHP, C/C++, C#, Swift, Kotlin, and more.
120
+
121
+ ## License
122
+
123
+ GPL-3.0 - See [LICENSE](LICENSE) for details.
124
+
125
+ ## Contributing
126
+
127
+ Contributions welcome! Please read our contributing guidelines before submitting PRs.
128
+
129
+ ## Related Projects
130
+
131
+ - [claude-context](https://github.com/zilliztech/claude-context) - Similar tool using Zilliz Cloud
132
+ - [Serena](https://github.com/oraios/serena) - Symbol-level code navigation
133
+
134
+ ## Credits
135
+
136
+ Built with:
137
+ - [LanceDB](https://lancedb.github.io/lancedb/) - Vector database
138
+ - [MCP SDK](https://github.com/modelcontextprotocol/sdk) - Model Context Protocol
139
+ - [Jina AI](https://jina.ai/) - Embedding API
@@ -0,0 +1,10 @@
1
+ import type { EmbeddingBackend, EmbeddingConfig } from './types.js';
2
+ export * from './types.js';
3
+ export { OllamaBackend } from './ollama.js';
4
+ export { JinaBackend } from './jina.js';
5
+ /**
6
+ * Create an embedding backend based on configuration
7
+ * Falls back through backends: jina -> local -> ollama
8
+ */
9
+ export declare function createEmbeddingBackend(config?: Partial<EmbeddingConfig>): Promise<EmbeddingBackend>;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIpE,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAgC3B"}
@@ -0,0 +1,37 @@
1
+ import { OllamaBackend } from './ollama.js';
2
+ import { JinaBackend } from './jina.js';
3
+ export * from './types.js';
4
+ export { OllamaBackend } from './ollama.js';
5
+ export { JinaBackend } from './jina.js';
6
+ /**
7
+ * Create an embedding backend based on configuration
8
+ * Falls back through backends: jina -> local -> ollama
9
+ */
10
+ export async function createEmbeddingBackend(config) {
11
+ const backends = [];
12
+ // Priority 1: Jina (if API key available)
13
+ const jinaKey = config?.apiKey || process.env.JINA_API_KEY;
14
+ if (jinaKey) {
15
+ backends.push(() => new JinaBackend({ backend: 'jina', apiKey: jinaKey, ...config }));
16
+ }
17
+ // Priority 2: Ollama (local fallback)
18
+ backends.push(() => new OllamaBackend({
19
+ backend: 'ollama',
20
+ baseUrl: config?.baseUrl || process.env.OLLAMA_URL || 'http://localhost:11434',
21
+ model: config?.model || 'nomic-embed-text',
22
+ }));
23
+ // Try each backend until one works
24
+ for (const createBackend of backends) {
25
+ try {
26
+ const backend = createBackend();
27
+ await backend.initialize();
28
+ console.error(`[lance-context] Using ${backend.name} embedding backend`);
29
+ return backend;
30
+ }
31
+ catch (error) {
32
+ console.error(`[lance-context] Backend failed: ${error}`);
33
+ }
34
+ }
35
+ throw new Error('No embedding backend available. Install Ollama or set JINA_API_KEY.');
36
+ }
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embeddings/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAiC;IAEjC,MAAM,QAAQ,GAAkC,EAAE,CAAC;IAEnD,0CAA0C;IAC1C,MAAM,OAAO,GAAG,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC3D,IAAI,OAAO,EAAE,CAAC;QACZ,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,sCAAsC;IACtC,QAAQ,CAAC,IAAI,CACX,GAAG,EAAE,CACH,IAAI,aAAa,CAAC;QAChB,OAAO,EAAE,QAAQ;QACjB,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,wBAAwB;QAC9E,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,kBAAkB;KAC3C,CAAC,CACL,CAAC;IAEF,mCAAmC;IACnC,KAAK,MAAM,aAAa,IAAI,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;YAChC,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,yBAAyB,OAAO,CAAC,IAAI,oBAAoB,CAAC,CAAC;YACzE,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { EmbeddingBackend, EmbeddingConfig } from './types.js';
2
+ /**
3
+ * Jina AI embedding backend
4
+ * Uses Jina's free API tier for high-quality embeddings
5
+ */
6
+ export declare class JinaBackend implements EmbeddingBackend {
7
+ name: string;
8
+ private model;
9
+ private apiKey;
10
+ private baseUrl;
11
+ private dimensions;
12
+ constructor(config: EmbeddingConfig);
13
+ initialize(): Promise<void>;
14
+ embed(text: string): Promise<number[]>;
15
+ embedBatch(texts: string[]): Promise<number[][]>;
16
+ getDimensions(): number;
17
+ }
18
+ //# sourceMappingURL=jina.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jina.d.ts","sourceRoot":"","sources":["../../src/embeddings/jina.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE;;;GAGG;AACH,qBAAa,WAAY,YAAW,gBAAgB;IAClD,IAAI,SAAU;IACd,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,UAAU,CAAQ;gBAEd,MAAM,EAAE,eAAe;IAQ7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsBtC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAsBtD,aAAa,IAAI,MAAM;CAGxB"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Jina AI embedding backend
3
+ * Uses Jina's free API tier for high-quality embeddings
4
+ */
5
+ export class JinaBackend {
6
+ name = 'jina';
7
+ model;
8
+ apiKey;
9
+ baseUrl = 'https://api.jina.ai/v1/embeddings';
10
+ dimensions = 1024; // jina-embeddings-v3 default
11
+ constructor(config) {
12
+ this.model = config.model || 'jina-embeddings-v3';
13
+ if (!config.apiKey) {
14
+ throw new Error('Jina API key is required. Set JINA_API_KEY environment variable.');
15
+ }
16
+ this.apiKey = config.apiKey;
17
+ }
18
+ async initialize() {
19
+ // Test API key with a small request
20
+ try {
21
+ await this.embed('test');
22
+ }
23
+ catch (error) {
24
+ throw new Error(`Failed to initialize Jina backend: ${error}`);
25
+ }
26
+ }
27
+ async embed(text) {
28
+ const response = await fetch(this.baseUrl, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ Authorization: `Bearer ${this.apiKey}`,
33
+ },
34
+ body: JSON.stringify({
35
+ model: this.model,
36
+ input: [text],
37
+ }),
38
+ });
39
+ if (!response.ok) {
40
+ const error = await response.text();
41
+ throw new Error(`Jina API error: ${response.status} - ${error}`);
42
+ }
43
+ const data = (await response.json());
44
+ return data.data[0].embedding;
45
+ }
46
+ async embedBatch(texts) {
47
+ const response = await fetch(this.baseUrl, {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ Authorization: `Bearer ${this.apiKey}`,
52
+ },
53
+ body: JSON.stringify({
54
+ model: this.model,
55
+ input: texts,
56
+ }),
57
+ });
58
+ if (!response.ok) {
59
+ const error = await response.text();
60
+ throw new Error(`Jina API error: ${response.status} - ${error}`);
61
+ }
62
+ const data = (await response.json());
63
+ return data.data.map((d) => d.embedding);
64
+ }
65
+ getDimensions() {
66
+ return this.dimensions;
67
+ }
68
+ }
69
+ //# sourceMappingURL=jina.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jina.js","sourceRoot":"","sources":["../../src/embeddings/jina.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,WAAW;IACtB,IAAI,GAAG,MAAM,CAAC;IACN,KAAK,CAAS;IACd,MAAM,CAAS;IACf,OAAO,GAAG,mCAAmC,CAAC;IAC9C,UAAU,GAAG,IAAI,CAAC,CAAC,6BAA6B;IAExD,YAAY,MAAuB;QACjC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,oBAAoB,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,oCAAoC;QACpC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;aACvC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,CAAC,IAAI,CAAC;aACd,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA6C,CAAC;QACjF,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAe;QAC9B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;aACvC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,KAAK;aACb,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA6C,CAAC;QACjF,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { EmbeddingBackend, EmbeddingConfig } from './types.js';
2
+ /**
3
+ * Ollama embedding backend
4
+ * Uses local Ollama server for embeddings
5
+ */
6
+ export declare class OllamaBackend implements EmbeddingBackend {
7
+ name: string;
8
+ private model;
9
+ private baseUrl;
10
+ private dimensions;
11
+ constructor(config: EmbeddingConfig);
12
+ initialize(): Promise<void>;
13
+ embed(text: string): Promise<number[]>;
14
+ embedBatch(texts: string[]): Promise<number[][]>;
15
+ getDimensions(): number;
16
+ }
17
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/embeddings/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE;;;GAGG;AACH,qBAAa,aAAc,YAAW,gBAAgB;IACpD,IAAI,SAAY;IAChB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAO;gBAEb,MAAM,EAAE,eAAe;IAK7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAkBtC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAMtD,aAAa,IAAI,MAAM;CAGxB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Ollama embedding backend
3
+ * Uses local Ollama server for embeddings
4
+ */
5
+ export class OllamaBackend {
6
+ name = 'ollama';
7
+ model;
8
+ baseUrl;
9
+ dimensions = 768; // nomic-embed-text default
10
+ constructor(config) {
11
+ this.model = config.model || 'nomic-embed-text';
12
+ this.baseUrl = config.baseUrl || 'http://localhost:11434';
13
+ }
14
+ async initialize() {
15
+ // Test connection
16
+ try {
17
+ const response = await fetch(`${this.baseUrl}/api/tags`);
18
+ if (!response.ok) {
19
+ throw new Error(`Ollama server returned ${response.status}`);
20
+ }
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Failed to connect to Ollama at ${this.baseUrl}: ${error}`);
24
+ }
25
+ }
26
+ async embed(text) {
27
+ const response = await fetch(`${this.baseUrl}/api/embeddings`, {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify({
31
+ model: this.model,
32
+ prompt: text,
33
+ }),
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`Ollama embedding failed: ${response.status}`);
37
+ }
38
+ const data = (await response.json());
39
+ return data.embedding;
40
+ }
41
+ async embedBatch(texts) {
42
+ // Ollama doesn't have native batch, so we parallelize
43
+ const embeddings = await Promise.all(texts.map((t) => this.embed(t)));
44
+ return embeddings;
45
+ }
46
+ getDimensions() {
47
+ return this.dimensions;
48
+ }
49
+ }
50
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/embeddings/ollama.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB,IAAI,GAAG,QAAQ,CAAC;IACR,KAAK,CAAS;IACd,OAAO,CAAS;IAChB,UAAU,GAAG,GAAG,CAAC,CAAC,2BAA2B;IAErD,YAAY,MAAuB;QACjC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,kBAAkB,CAAC;QAChD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,wBAAwB,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,UAAU;QACd,kBAAkB;QAClB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,iBAAiB,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI;aACb,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAChE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAe;QAC9B,sDAAsD;QACtD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Embedding backend interface
3
+ */
4
+ export interface EmbeddingBackend {
5
+ name: string;
6
+ initialize(): Promise<void>;
7
+ embed(text: string): Promise<number[]>;
8
+ embedBatch(texts: string[]): Promise<number[][]>;
9
+ getDimensions(): number;
10
+ }
11
+ export interface EmbeddingConfig {
12
+ backend: 'jina' | 'local' | 'ollama';
13
+ model?: string;
14
+ apiKey?: string;
15
+ baseUrl?: string;
16
+ }
17
+ export declare const DEFAULT_CONFIG: EmbeddingConfig;
18
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/embeddings/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjD,aAAa,IAAI,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,EAAE,eAI5B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export const DEFAULT_CONFIG = {
2
+ backend: 'ollama',
3
+ model: 'nomic-embed-text',
4
+ baseUrl: 'http://localhost:11434',
5
+ };
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/embeddings/types.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,wBAAwB;CAClC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { createEmbeddingBackend } from './embeddings/index.js';
6
+ import { CodeIndexer } from './search/indexer.js';
7
+ const PROJECT_PATH = process.env.LANCE_CONTEXT_PROJECT || process.cwd();
8
+ let indexer = null;
9
+ async function getIndexer() {
10
+ if (!indexer) {
11
+ const backend = await createEmbeddingBackend();
12
+ indexer = new CodeIndexer(PROJECT_PATH, backend);
13
+ await indexer.initialize();
14
+ }
15
+ return indexer;
16
+ }
17
+ const server = new Server({
18
+ name: 'lance-context',
19
+ version: '0.1.0',
20
+ }, {
21
+ capabilities: {
22
+ tools: {},
23
+ },
24
+ });
25
+ // List available tools
26
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
27
+ return {
28
+ tools: [
29
+ {
30
+ name: 'index_codebase',
31
+ description: 'Index the codebase for semantic search. Creates vector embeddings of all code files.',
32
+ inputSchema: {
33
+ type: 'object',
34
+ properties: {
35
+ patterns: {
36
+ type: 'array',
37
+ items: { type: 'string' },
38
+ description: 'Glob patterns for files to index (default: common code files)',
39
+ },
40
+ excludePatterns: {
41
+ type: 'array',
42
+ items: { type: 'string' },
43
+ description: 'Glob patterns for files to exclude (default: node_modules, dist, .git)',
44
+ },
45
+ },
46
+ },
47
+ },
48
+ {
49
+ name: 'search_code',
50
+ description: 'Search the codebase using natural language. Returns relevant code snippets.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ query: {
55
+ type: 'string',
56
+ description: 'Natural language query to search for',
57
+ },
58
+ limit: {
59
+ type: 'number',
60
+ description: 'Maximum number of results (default: 10)',
61
+ },
62
+ },
63
+ required: ['query'],
64
+ },
65
+ },
66
+ {
67
+ name: 'get_index_status',
68
+ description: 'Get the current status of the code index.',
69
+ inputSchema: {
70
+ type: 'object',
71
+ properties: {},
72
+ },
73
+ },
74
+ {
75
+ name: 'clear_index',
76
+ description: 'Clear the code index.',
77
+ inputSchema: {
78
+ type: 'object',
79
+ properties: {},
80
+ },
81
+ },
82
+ ],
83
+ };
84
+ });
85
+ // Handle tool calls
86
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
87
+ const { name, arguments: args } = request.params;
88
+ try {
89
+ const idx = await getIndexer();
90
+ switch (name) {
91
+ case 'index_codebase': {
92
+ const patterns = args?.patterns || undefined;
93
+ const excludePatterns = args?.excludePatterns || undefined;
94
+ const result = await idx.indexCodebase(patterns, excludePatterns);
95
+ return {
96
+ content: [
97
+ {
98
+ type: 'text',
99
+ text: `Indexed ${result.filesIndexed} files, created ${result.chunksCreated} chunks.`,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ case 'search_code': {
105
+ const query = args?.query;
106
+ const limit = args?.limit || 10;
107
+ const results = await idx.search(query, limit);
108
+ const formatted = results
109
+ .map((r, i) => `## Result ${i + 1}: ${r.filePath}:${r.startLine}-${r.endLine}\n\`\`\`${r.language}\n${r.content}\n\`\`\``)
110
+ .join('\n\n');
111
+ return {
112
+ content: [
113
+ {
114
+ type: 'text',
115
+ text: formatted || 'No results found.',
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ case 'get_index_status': {
121
+ const status = await idx.getStatus();
122
+ return {
123
+ content: [
124
+ {
125
+ type: 'text',
126
+ text: JSON.stringify(status, null, 2),
127
+ },
128
+ ],
129
+ };
130
+ }
131
+ case 'clear_index': {
132
+ await idx.clearIndex();
133
+ return {
134
+ content: [
135
+ {
136
+ type: 'text',
137
+ text: 'Index cleared.',
138
+ },
139
+ ],
140
+ };
141
+ }
142
+ default:
143
+ throw new Error(`Unknown tool: ${name}`);
144
+ }
145
+ }
146
+ catch (error) {
147
+ return {
148
+ content: [
149
+ {
150
+ type: 'text',
151
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
152
+ },
153
+ ],
154
+ isError: true,
155
+ };
156
+ }
157
+ });
158
+ // Start server
159
+ async function main() {
160
+ const transport = new StdioServerTransport();
161
+ await server.connect(transport);
162
+ console.error('[lance-context] MCP server started');
163
+ }
164
+ main().catch((error) => {
165
+ console.error('[lance-context] Fatal error:', error);
166
+ process.exit(1);
167
+ });
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { EmbeddingBackend } from '../embeddings/index.js';
2
+ export interface CodeChunk {
3
+ id: string;
4
+ filePath: string;
5
+ content: string;
6
+ startLine: number;
7
+ endLine: number;
8
+ language: string;
9
+ embedding?: number[];
10
+ }
11
+ export interface IndexStatus {
12
+ indexed: boolean;
13
+ fileCount: number;
14
+ chunkCount: number;
15
+ lastUpdated: string | null;
16
+ indexPath: string;
17
+ }
18
+ /**
19
+ * Code indexer using LanceDB for vector storage
20
+ */
21
+ export declare class CodeIndexer {
22
+ private db;
23
+ private table;
24
+ private embeddingBackend;
25
+ private indexPath;
26
+ private projectPath;
27
+ constructor(projectPath: string, embeddingBackend: EmbeddingBackend);
28
+ initialize(): Promise<void>;
29
+ getStatus(): Promise<IndexStatus>;
30
+ indexCodebase(patterns?: string[], excludePatterns?: string[]): Promise<{
31
+ filesIndexed: number;
32
+ chunksCreated: number;
33
+ }>;
34
+ private chunkFile;
35
+ private getLanguage;
36
+ search(query: string, limit?: number): Promise<CodeChunk[]>;
37
+ clearIndex(): Promise<void>;
38
+ }
39
+ //# sourceMappingURL=indexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/search/indexer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,gBAAgB;IAM7D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IA0BjC,aAAa,CACjB,QAAQ,GAAE,MAAM,EAAoF,EACpG,eAAe,GAAE,MAAM,EAAsE,GAC5F,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;YA+D7C,SAAS;IA2BvB,OAAO,CAAC,WAAW;IA2Bb,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsB/D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAOlC"}