@opticlm/connector 2.0.1 → 2.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.
- package/README.md +16 -38
- package/dist/index.d.ts +3 -4
- package/dist/{capabilities-CQHv2shL.d.ts → interfaces-wSmWeYxt.d.ts} +67 -1
- package/dist/lsp/index.d.ts +4 -2
- package/dist/lsp/index.js +3 -3
- package/dist/mcp/index.d.ts +23 -12
- package/dist/mcp/index.js +8 -8
- package/dist/resolver-CSuvobsJ.d.ts +69 -0
- package/package.json +2 -1
- package/dist/resolver-Cg62Av4_.d.ts +0 -129
package/README.md
CHANGED
|
@@ -4,7 +4,6 @@ Provides an abstract interface that allows LLMs to connect to fact sources such
|
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
-
- [Core Philosophy](#core-philosophy)
|
|
8
7
|
- [Installation](#installation)
|
|
9
8
|
- [Quick Start](#quick-start)
|
|
10
9
|
- [MCP Tools](#mcp-tools)
|
|
@@ -28,7 +27,9 @@ pnpm add @opticlm/connector
|
|
|
28
27
|
|
|
29
28
|
## Quick Start
|
|
30
29
|
|
|
31
|
-
Providers are installed
|
|
30
|
+
Providers are installed onto an MCP server using `install()` from `@opticlm/connector/mcp`. Each call registers the tools and resources for that specific provider. Providers that depend on file access (definition, references, hierarchy, edit) receive a `fileAccess` option.
|
|
31
|
+
|
|
32
|
+
You can pass a single provider or an array of providers of the same type. When an array is given, their results are merged automatically — array-returning methods (e.g. `provideDefinition`) are concatenated, void methods are called on all providers in parallel, and callback registrars (e.g. `onDiagnosticsChanged`) are registered on every provider in the array.
|
|
32
33
|
|
|
33
34
|
```typescript
|
|
34
35
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
@@ -91,6 +92,10 @@ install(server, definition, { fileAccess })
|
|
|
91
92
|
install(server, diagnostics, { fileAccess })
|
|
92
93
|
install(server, outline, { fileAccess })
|
|
93
94
|
|
|
95
|
+
// You can also pass an array to merge multiple providers of the same type:
|
|
96
|
+
// install(server, [definition, anotherDefinition], { fileAccess })
|
|
97
|
+
// install(server, [diagnostics, anotherDiagnostics], { fileAccess })
|
|
98
|
+
|
|
94
99
|
// 6. Connect to transport (you control the server lifecycle)
|
|
95
100
|
const transport = new StdioServerTransport()
|
|
96
101
|
await server.connect(transport)
|
|
@@ -110,6 +115,12 @@ Navigate to the definition of a symbol.
|
|
|
110
115
|
|
|
111
116
|
Find all references to a symbol.
|
|
112
117
|
|
|
118
|
+
### `find_file_references`
|
|
119
|
+
|
|
120
|
+
Find all references to a file across the workspace (e.g., all files that import or link to the given file).
|
|
121
|
+
|
|
122
|
+
Only registered when your `ReferencesProvider` implements the optional `provideFileReferences` method.
|
|
123
|
+
|
|
113
124
|
### `call_hierarchy`
|
|
114
125
|
|
|
115
126
|
Get call hierarchy for a function or method.
|
|
@@ -170,23 +181,6 @@ install(server, definitionProvider, {
|
|
|
170
181
|
})
|
|
171
182
|
```
|
|
172
183
|
|
|
173
|
-
### Callback reference
|
|
174
|
-
|
|
175
|
-
| Provider | Tool | Input callback | Output callback |
|
|
176
|
-
|----------|------|----------------|-----------------|
|
|
177
|
-
| `EditProvider` | `apply_edit` | `onEditInput` | `onEditOutput` |
|
|
178
|
-
| `DefinitionProvider` | `goto_definition` | `onDefinitionInput` | `onDefinitionOutput` |
|
|
179
|
-
| `DefinitionProvider` | `goto_type_definition` | `onTypeDefinitionInput` | `onTypeDefinitionOutput` |
|
|
180
|
-
| `ReferencesProvider` | `find_references` | `onReferencesInput` | `onReferencesOutput` |
|
|
181
|
-
| `HierarchyProvider` | `call_hierarchy` | `onCallHierarchyInput` | `onCallHierarchyOutput` |
|
|
182
|
-
| `GlobalFindProvider` | `global_find` | `onGlobalFindInput` | `onGlobalFindOutput` |
|
|
183
|
-
| `GraphProvider` | `get_link_structure` | — | `onLinkStructureOutput` |
|
|
184
|
-
| `GraphProvider` | `add_link` | `onAddLinkInput` | `onAddLinkOutput` |
|
|
185
|
-
| `FrontmatterProvider` | `get_frontmatter_structure` | `onFrontmatterStructureInput` | `onFrontmatterStructureOutput` |
|
|
186
|
-
| `FrontmatterProvider` | `set_frontmatter` | `onSetFrontmatterInput` | `onSetFrontmatterOutput` |
|
|
187
|
-
|
|
188
|
-
`get_link_structure` has no `onInput` since its input schema is empty. Resource-only providers (`FileAccessProvider`, `DiagnosticsProvider`, `OutlineProvider`) have no callbacks.
|
|
189
|
-
|
|
190
184
|
## MCP Resources
|
|
191
185
|
|
|
192
186
|
The SDK automatically registers resources based on which providers you install:
|
|
@@ -355,10 +349,7 @@ const server = await servePipe({
|
|
|
355
349
|
pipeName: 'my-ide-lsp',
|
|
356
350
|
fileAccess: { /* ... */ },
|
|
357
351
|
definition: { /* ... */ },
|
|
358
|
-
|
|
359
|
-
provideDiagnostics: async (uri) => { /* ... */ },
|
|
360
|
-
onDiagnosticsChanged: (callback) => { /* ... */ },
|
|
361
|
-
},
|
|
352
|
+
// ...
|
|
362
353
|
// Add only the providers your IDE supports
|
|
363
354
|
})
|
|
364
355
|
// server.pipePath — the resolved pipe path
|
|
@@ -387,8 +378,7 @@ const mcpServer = new McpServer({ name: 'my-mcp', version: '1.0.0' })
|
|
|
387
378
|
if (conn.fileAccess) install(mcpServer, conn.fileAccess)
|
|
388
379
|
if (conn.definition && conn.fileAccess)
|
|
389
380
|
install(mcpServer, conn.definition, { fileAccess: conn.fileAccess })
|
|
390
|
-
|
|
391
|
-
install(mcpServer, conn.diagnostics, { fileAccess: conn.fileAccess })
|
|
381
|
+
// ...
|
|
392
382
|
// Install whichever proxy providers are available
|
|
393
383
|
|
|
394
384
|
// When done:
|
|
@@ -429,13 +419,6 @@ const fileAccess = {
|
|
|
429
419
|
// Providers are automatically created based on server capabilities
|
|
430
420
|
install(server, fileAccess)
|
|
431
421
|
if (lsp.definition) install(server, lsp.definition, { fileAccess })
|
|
432
|
-
if (lsp.references) install(server, lsp.references, { fileAccess })
|
|
433
|
-
if (lsp.hierarchy) install(server, lsp.hierarchy, { fileAccess })
|
|
434
|
-
if (lsp.outline) install(server, lsp.outline, { fileAccess })
|
|
435
|
-
install(server, {
|
|
436
|
-
...lsp.diagnostics,
|
|
437
|
-
onDiagnosticsChanged: lsp.onDiagnosticsChanged,
|
|
438
|
-
}, { fileAccess })
|
|
439
422
|
|
|
440
423
|
const transport = new StdioServerTransport()
|
|
441
424
|
await server.connect(transport)
|
|
@@ -459,12 +442,7 @@ interface LspClientOptions {
|
|
|
459
442
|
### How It Works
|
|
460
443
|
|
|
461
444
|
1. `start()` spawns the LSP server process and performs the initialize/initialized handshake
|
|
462
|
-
2. The server's `ServerCapabilities` response determines which providers are created
|
|
463
|
-
- `definitionProvider` → `lsp.definition`
|
|
464
|
-
- `referencesProvider` → `lsp.references`
|
|
465
|
-
- `callHierarchyProvider` → `lsp.hierarchy`
|
|
466
|
-
- `documentSymbolProvider` → `lsp.outline`
|
|
467
|
-
- Diagnostics are always available (via `textDocument/publishDiagnostics` notifications)
|
|
445
|
+
2. The server's `ServerCapabilities` response determines which providers are created
|
|
468
446
|
3. Providers that the server does not support remain `undefined`
|
|
469
447
|
4. Documents are automatically opened/closed with the server on demand, with an idle timeout for cleanup
|
|
470
448
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider,
|
|
2
|
-
export { C as CodeSnippet,
|
|
3
|
-
|
|
4
|
-
export { R as ResolverConfig, S as SymbolResolutionError, a as SymbolResolver } from './resolver-Cg62Av4_.js';
|
|
1
|
+
import { F as FileAccessProvider, E as EditProvider, D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider, c as FrontmatterProvider } from './interfaces-wSmWeYxt.js';
|
|
2
|
+
export { C as CodeSnippet, d as Diagnostic, e as DiagnosticSeverity, f as DiskRange, g as DocumentSymbol, h as EditFailureReason, i as EditResult, j as ExactPosition, k as Frontmatter, l as FrontmatterMatch, m as FrontmatterValue, n as FuzzyPosition, o as GlobalFindMatch, p as GlobalFindOptions, L as Link, q as OnDiagnosticsChangedCallback, P as PendingEditOperation, S as SymbolKind, T as TextEdit, U as UnifiedUri } from './interfaces-wSmWeYxt.js';
|
|
3
|
+
export { R as ResolverConfig, S as SymbolResolutionError, a as SymbolResolver } from './resolver-CSuvobsJ.js';
|
|
5
4
|
|
|
6
5
|
interface connectPipeOptions {
|
|
7
6
|
pipeName: string;
|
|
@@ -211,6 +211,12 @@ interface ReferencesProvider {
|
|
|
211
211
|
* @returns Array of code snippets representing reference locations
|
|
212
212
|
*/
|
|
213
213
|
provideReferences(uri: UnifiedUri, position: ExactPosition): Promise<CodeSnippet[]>;
|
|
214
|
+
/**
|
|
215
|
+
* Finds all references to the given file.
|
|
216
|
+
*
|
|
217
|
+
* @param uri - The URI of the file
|
|
218
|
+
*/
|
|
219
|
+
provideFileReferences?(uri: UnifiedUri): Promise<CodeSnippet[]>;
|
|
214
220
|
}
|
|
215
221
|
/**
|
|
216
222
|
* Provides call hierarchy functionality.
|
|
@@ -376,4 +382,64 @@ interface FrontmatterProvider {
|
|
|
376
382
|
*/
|
|
377
383
|
type OnDiagnosticsChangedCallback = (uri: UnifiedUri) => void;
|
|
378
384
|
|
|
379
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Infrastructure Interfaces for MCP LSP Driver SDK
|
|
387
|
+
*
|
|
388
|
+
* The SDK consumer (Plugin Developer) must implement these interfaces
|
|
389
|
+
* to bridge the SDK to the specific IDE.
|
|
390
|
+
*/
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Provides access to the file system for reading files.
|
|
394
|
+
* Since the SDK is responsible for resolving FuzzyPosition to ExactPosition,
|
|
395
|
+
* it needs direct access to read files from the disk.
|
|
396
|
+
*/
|
|
397
|
+
interface FileAccessProvider {
|
|
398
|
+
/**
|
|
399
|
+
* Reads the content of a file from the disk (ignoring unsaved IDE buffers).
|
|
400
|
+
* Used for symbol resolution and context retrieval.
|
|
401
|
+
*
|
|
402
|
+
* @param uri - The URI of the file to read
|
|
403
|
+
* @returns The content of the file as a string
|
|
404
|
+
* @throws Error if the file cannot be read
|
|
405
|
+
*/
|
|
406
|
+
readFile(uri: UnifiedUri): Promise<string>;
|
|
407
|
+
/**
|
|
408
|
+
* Read children in a directory, exluding git-ignored files, similar to Unix `ls` command
|
|
409
|
+
* @param relativePath - The path to the folder to read
|
|
410
|
+
* @returns Array of file/folder paths in the directory
|
|
411
|
+
*/
|
|
412
|
+
readDirectory(relativePath: UnifiedUri): Promise<string[]>;
|
|
413
|
+
/**
|
|
414
|
+
* Called by the driver to register a callback for file changes.
|
|
415
|
+
* When provided, the files:// resources become subscribable.
|
|
416
|
+
* The plugin should call the registered callback whenever a file changes.
|
|
417
|
+
*
|
|
418
|
+
* @param callback - The callback to invoke when a file changes
|
|
419
|
+
*/
|
|
420
|
+
onFileChanged?(callback: (uri: UnifiedUri) => void): void;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Provides edit capabilities for applying changes to files.
|
|
424
|
+
* At least one of the methods must be implemented.
|
|
425
|
+
*/
|
|
426
|
+
interface EditProvider {
|
|
427
|
+
/**
|
|
428
|
+
* Applies edits directly without user interaction.
|
|
429
|
+
* Use this when user approval is handled elsewhere or not needed.
|
|
430
|
+
*
|
|
431
|
+
* @param operation - The pending edit operation to apply
|
|
432
|
+
* @returns true if applied successfully, false if failed
|
|
433
|
+
*/
|
|
434
|
+
applyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
435
|
+
/**
|
|
436
|
+
* Displays a diff view or a confirmation dialog in the IDE.
|
|
437
|
+
* The user decides whether to apply the edits or discard them.
|
|
438
|
+
*
|
|
439
|
+
* @param operation - The pending edit operation to preview
|
|
440
|
+
* @returns true if applied, false if rejected/cancelled
|
|
441
|
+
*/
|
|
442
|
+
previewAndApplyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export type { CodeSnippet as C, DefinitionProvider as D, EditProvider as E, FileAccessProvider as F, GlobalFindProvider as G, HierarchyProvider as H, Link as L, OutlineProvider as O, PendingEditOperation as P, ReferencesProvider as R, SymbolKind as S, TextEdit as T, UnifiedUri as U, DiagnosticsProvider as a, GraphProvider as b, FrontmatterProvider as c, Diagnostic as d, DiagnosticSeverity as e, DiskRange as f, DocumentSymbol as g, EditFailureReason as h, EditResult as i, ExactPosition as j, Frontmatter as k, FrontmatterMatch as l, FrontmatterValue as m, FuzzyPosition as n, GlobalFindMatch as o, GlobalFindOptions as p, OnDiagnosticsChangedCallback as q };
|
package/dist/lsp/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChildProcess } from 'node:child_process';
|
|
2
2
|
import { ProtocolConnection } from 'vscode-languageserver-protocol/node.js';
|
|
3
|
-
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, O as OutlineProvider, a as DiagnosticsProvider,
|
|
3
|
+
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, O as OutlineProvider, a as DiagnosticsProvider, F as FileAccessProvider, e as DiagnosticSeverity, C as CodeSnippet, d as Diagnostic$1, g as DocumentSymbol$1, j as ExactPosition, f as DiskRange, S as SymbolKind } from '../interfaces-wSmWeYxt.js';
|
|
4
4
|
import { Location, LocationLink, Diagnostic, DocumentSymbol, SymbolInformation } from 'vscode-languageserver-protocol';
|
|
5
5
|
|
|
6
6
|
interface LspClientOptions {
|
|
@@ -21,15 +21,16 @@ declare class LspClient {
|
|
|
21
21
|
private openDocuments;
|
|
22
22
|
private diagnosticsCache;
|
|
23
23
|
private diagnosticsListeners;
|
|
24
|
+
private fileChangedListeners;
|
|
24
25
|
readonly definition: DefinitionProvider | undefined;
|
|
25
26
|
readonly references: ReferencesProvider | undefined;
|
|
26
27
|
readonly hierarchy: HierarchyProvider | undefined;
|
|
27
28
|
readonly outline: OutlineProvider | undefined;
|
|
28
29
|
readonly diagnostics: DiagnosticsProvider | undefined;
|
|
30
|
+
readonly fileAccess: Pick<FileAccessProvider, 'onFileChanged'>;
|
|
29
31
|
private readonly options;
|
|
30
32
|
constructor(options: LspClientOptions);
|
|
31
33
|
getState(): LspClientState;
|
|
32
|
-
get onDiagnosticsChanged(): (callback: OnDiagnosticsChangedCallback) => void;
|
|
33
34
|
start(): Promise<void>;
|
|
34
35
|
stop(): Promise<void>;
|
|
35
36
|
notifyFileChanged(sdkPath: string): Promise<void>;
|
|
@@ -49,6 +50,7 @@ declare class LspClient {
|
|
|
49
50
|
private provideDefinition;
|
|
50
51
|
private provideTypeDefinition;
|
|
51
52
|
private provideReferences;
|
|
53
|
+
private provideFileReferences;
|
|
52
54
|
private provideCallHierarchy;
|
|
53
55
|
private provideDocumentSymbols;
|
|
54
56
|
private provideDiagnostics;
|
package/dist/lsp/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {spawn}from'child_process';import {InitializedNotification,ShutdownRequest,ExitNotification,StreamMessageReader,StreamMessageWriter,createProtocolConnection,InitializeRequest,CancellationTokenSource,DidOpenTextDocumentNotification,DidCloseTextDocumentNotification}from'vscode-languageserver-protocol/node.js';import D from'path';import {pathToFileURL,fileURLToPath}from'url';function c(o,t){let e=D.resolve(o,t);return pathToFileURL(e).toString()}function u(o,t){let e=fileURLToPath(t);return D.relative(o,e).replace(/\\/g,"/")}function p(o){return {start:{line:o.start.line,character:o.start.character},end:{line:o.end.line,character:o.end.character}}}function R(o){return {line:o.line,character:o.character}}var T={1:"error",2:"warning",3:"information",4:"hint"};function
|
|
2
|
-
`).slice(l.start.line,l.end.line+1);n.push({uri:m,range:l,content:
|
|
3
|
-
`)});}catch{n.push({uri:m,range:l,content:""});}}return n}var I={".ts":"typescript",".tsx":"typescriptreact",".js":"javascript",".jsx":"javascriptreact",".rs":"rust",".go":"go",".py":"python",".rb":"ruby",".java":"java",".kt":"kotlin",".kts":"kotlin",".c":"c",".h":"c",".cpp":"cpp",".cc":"cpp",".cxx":"cpp",".hpp":"cpp",".cs":"csharp",".swift":"swift",".lua":"lua",".zig":"zig",".json":"json",".yaml":"yaml",".yml":"yaml",".toml":"toml",".xml":"xml",".html":"html",".css":"css",".scss":"scss",".md":"markdown",".sql":"sql",".sh":"shellscript",".bash":"shellscript",".zsh":"shellscript",".ps1":"powershell",".dart":"dart",".ex":"elixir",".exs":"elixir",".erl":"erlang",".hs":"haskell",".ml":"ocaml",".mli":"ocaml",".scala":"scala",".r":"r",".R":"r",".php":"php",".pl":"perl",".vim":"vim"};function w(o){let t=D.extname(o);return I[t]??"plaintext"}var y=class{state="idle";process=null;connection=null;openDocuments=new Map;diagnosticsCache=new Map;diagnosticsListeners=[];definition;references;hierarchy;outline;diagnostics;options;constructor(t){this.options={documentIdleTimeout:3e4,requestTimeout:3e4,...t},this.definition=void 0,this.references=void 0,this.hierarchy=void 0,this.outline=void 0,this.diagnostics=void 0
|
|
1
|
+
import {spawn}from'child_process';import {InitializedNotification,ShutdownRequest,ExitNotification,StreamMessageReader,StreamMessageWriter,createProtocolConnection,InitializeRequest,CancellationTokenSource,DidOpenTextDocumentNotification,DidCloseTextDocumentNotification}from'vscode-languageserver-protocol/node.js';import D from'path';import {pathToFileURL,fileURLToPath}from'url';function c(o,t){let e=D.resolve(o,t);return pathToFileURL(e).toString()}function u(o,t){let e=fileURLToPath(t);return D.relative(o,e).replace(/\\/g,"/")}function p(o){return {start:{line:o.start.line,character:o.start.character},end:{line:o.end.line,character:o.end.character}}}function R(o){return {line:o.line,character:o.character}}var T={1:"error",2:"warning",3:"information",4:"hint"};function L(o){return T[o??1]??"error"}var U=["file","file","module","namespace","package","class","method","property","field","constructor","enum","interface","function","variable","constant","string","number","boolean","array","object","key","null","enumMember","struct","event","operator","typeParameter"];function P(o){return U[o]??"variable"}function g(o,t,e){return {uri:u(o,t),range:p(e.range),severity:L(e.severity),message:e.message,source:e.source,code:typeof e.code=="object"?String(e.code):e.code??void 0}}function f(o){return {name:o.name,detail:o.detail,kind:P(o.kind),range:p(o.range),selectionRange:p(o.selectionRange),children:o.children?.map(f)}}function C(o,t){return {name:t.name,kind:P(t.kind),range:p(t.location.range),selectionRange:p(t.location.range)}}async function h(o,t,e){if(!t)return [];let i=Array.isArray(t)?t:[t];if(i.length===0)return [];let n=[];for(let s of i){let r="targetUri"in s,a=r?s.targetUri:s.uri,d=r?s.targetRange:s.range,m=u(o,a),l=p(d);try{let b=(await e(m)).split(`
|
|
2
|
+
`).slice(l.start.line,l.end.line+1);n.push({uri:m,range:l,content:b.join(`
|
|
3
|
+
`)});}catch{n.push({uri:m,range:l,content:""});}}return n}var I={".ts":"typescript",".tsx":"typescriptreact",".js":"javascript",".jsx":"javascriptreact",".rs":"rust",".go":"go",".py":"python",".rb":"ruby",".java":"java",".kt":"kotlin",".kts":"kotlin",".c":"c",".h":"c",".cpp":"cpp",".cc":"cpp",".cxx":"cpp",".hpp":"cpp",".cs":"csharp",".swift":"swift",".lua":"lua",".zig":"zig",".json":"json",".yaml":"yaml",".yml":"yaml",".toml":"toml",".xml":"xml",".html":"html",".css":"css",".scss":"scss",".md":"markdown",".sql":"sql",".sh":"shellscript",".bash":"shellscript",".zsh":"shellscript",".ps1":"powershell",".dart":"dart",".ex":"elixir",".exs":"elixir",".erl":"erlang",".hs":"haskell",".ml":"ocaml",".mli":"ocaml",".scala":"scala",".r":"r",".R":"r",".php":"php",".pl":"perl",".vim":"vim"};function w(o){let t=D.extname(o);return I[t]??"plaintext"}var y=class{state="idle";process=null;connection=null;openDocuments=new Map;diagnosticsCache=new Map;diagnosticsListeners=[];fileChangedListeners=[];definition;references;hierarchy;outline;diagnostics;fileAccess;options;constructor(t){this.options={documentIdleTimeout:3e4,requestTimeout:3e4,...t},this.definition=void 0,this.references=void 0,this.hierarchy=void 0,this.outline=void 0,this.diagnostics=void 0,this.fileAccess={onFileChanged:e=>{this.fileChangedListeners.push(e);}};}getState(){return this.state}async start(){if(this.state!=="idle")throw new Error(`Cannot start: client is in "${this.state}" state`);this.state="starting";try{let t=this.spawnAndConnect();this.connection=t,t.onClose(()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();}),t.listen();let i=(await this.sendInitialize()).capabilities;this.setupProviders(i),t.onNotification("textDocument/publishDiagnostics",n=>{let s=u(this.options.workspacePath,n.uri),r=n.diagnostics.map(a=>g(this.options.workspacePath,n.uri,a));this.diagnosticsCache.set(s,r);for(let a of this.diagnosticsListeners)a(s);}),await t.sendNotification(InitializedNotification.type,{}),this.state="running";}catch(t){throw this.transitionToDead(),t}}async stop(){if(this.state!=="running")return;let t=[...this.openDocuments.keys()].map(e=>this.closeDocument(e));await Promise.all(t);try{await this.connection?.sendRequest(ShutdownRequest.type),await this.connection?.sendNotification(ExitNotification.type);}catch{}this.cleanup();}async notifyFileChanged(t){if(this.state!=="running")return;let e=c(this.options.workspacePath,t);if(this.openDocuments.get(e)){await this.closeDocument(e),await this.ensureDocumentOpen(e);for(let n of this.fileChangedListeners)n(t);}}startWithConnection(t,e){this.connection=t,this.process=e;}async initializeConnection(){let t=this.connection;if(!t)throw new Error("Connection not set");this.state="starting",t.onClose(()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();}),t.listen();let i=(await this.sendInitialize()).capabilities;this.setupProviders(i),t.onNotification("textDocument/publishDiagnostics",n=>{let s=u(this.options.workspacePath,n.uri),r=n.diagnostics.map(a=>g(this.options.workspacePath,n.uri,a));this.diagnosticsCache.set(s,r);for(let a of this.diagnosticsListeners)a(s);}),await t.sendNotification(InitializedNotification.type,{}),this.state="running";}spawnAndConnect(){let t=spawn(this.options.command,this.options.args??[],{stdio:["pipe","pipe","pipe"],env:this.options.env?{...process.env,...this.options.env}:process.env});this.process=t,t.on("exit",()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();});let e=new StreamMessageReader(t.stdout),i=new StreamMessageWriter(t.stdin);return createProtocolConnection(e,i)}async sendInitialize(){if(!this.connection)throw new Error("No connection");let t={textDocument:{synchronization:{dynamicRegistration:false,didSave:true},definition:{dynamicRegistration:false},references:{dynamicRegistration:false},callHierarchy:{dynamicRegistration:false},documentSymbol:{dynamicRegistration:false,hierarchicalDocumentSymbolSupport:true},publishDiagnostics:{relatedInformation:true}}};return await this.connection.sendRequest(InitializeRequest.type,{processId:process.pid,capabilities:t,rootUri:c(this.options.workspacePath,"."),initializationOptions:this.options.initializationOptions??null})}setupProviders(t){t.definitionProvider&&(this.definition={provideDefinition:(e,i)=>this.provideDefinition(e,i),provideTypeDefinition:(e,i)=>this.provideTypeDefinition(e,i)}),t.referencesProvider&&(this.references={provideReferences:(e,i)=>this.provideReferences(e,i),provideFileReferences:e=>this.provideFileReferences(e)}),t.callHierarchyProvider&&(this.hierarchy={provideCallHierarchy:(e,i,n)=>this.provideCallHierarchy(e,i,n)}),t.documentSymbolProvider&&(this.outline={provideDocumentSymbols:e=>this.provideDocumentSymbols(e)}),this.diagnostics={provideDiagnostics:e=>this.provideDiagnostics(e),getWorkspaceDiagnostics:()=>this.getWorkspaceDiagnostics(),onDiagnosticsChanged:e=>{this.diagnosticsListeners.push(e);}};}assertRunning(){if(this.state!=="running")throw new Error(`Client is not running (state: "${this.state}")`);if(!this.connection)throw new Error("No connection");return this.connection}createTimeout(){let t=new CancellationTokenSource,e,i=new Promise((n,s)=>{e=setTimeout(()=>{t.cancel(),s(new Error("LSP request timed out"));},this.options.requestTimeout);});return {token:t,promise:i,clear:()=>{e&&clearTimeout(e);}}}async sendRequest(t,e){let i=this.assertRunning(),n=this.createTimeout();try{return await Promise.race([i.sendRequest(t,e,n.token.token),n.promise])}finally{n.clear();}}async ensureDocumentOpen(t){let e=this.openDocuments.get(t);if(e){e.closeTimer&&clearTimeout(e.closeTimer),e.closeTimer=this.scheduleDocumentClose(t);return}let i=u(this.options.workspacePath,t),n=await this.options.readFile(i),s=w(i),r={uri:t,version:1,closeTimer:this.scheduleDocumentClose(t)};this.openDocuments.set(t,r),await this.connection?.sendNotification(DidOpenTextDocumentNotification.type,{textDocument:{uri:t,languageId:s,version:r.version,text:n}});}scheduleDocumentClose(t){return setTimeout(()=>{this.closeDocument(t);},this.options.documentIdleTimeout)}async closeDocument(t){let e=this.openDocuments.get(t);if(e&&(e.closeTimer&&clearTimeout(e.closeTimer),this.openDocuments.delete(t),this.connection&&this.state==="running"))try{await this.connection.sendNotification(DidCloseTextDocumentNotification.type,{textDocument:{uri:t}});}catch{}}async provideDefinition(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/definition",{textDocument:{uri:i},position:{line:e.line,character:e.character}});return h(this.options.workspacePath,n,this.options.readFile)}async provideTypeDefinition(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/typeDefinition",{textDocument:{uri:i},position:{line:e.line,character:e.character}});return h(this.options.workspacePath,n,this.options.readFile)}async provideReferences(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/references",{textDocument:{uri:i},position:{line:e.line,character:e.character},context:{includeDeclaration:true}});return h(this.options.workspacePath,n,this.options.readFile)}async provideFileReferences(t){let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=await this.sendRequest("textDocument/references",{textDocument:{uri:e},position:{line:0,character:0},context:{includeDeclaration:false}});return h(this.options.workspacePath,i,this.options.readFile)}async provideCallHierarchy(t,e,i){let n=c(this.options.workspacePath,t);await this.ensureDocumentOpen(n);let s=await this.sendRequest("textDocument/prepareCallHierarchy",{textDocument:{uri:n},position:{line:e.line,character:e.character}});if(!s||s.length===0)return [];let r=s[0],a=i==="incoming"?"callHierarchy/incomingCalls":"callHierarchy/outgoingCalls",d=await this.sendRequest(a,{item:r});if(!d||d.length===0)return [];let m=d.map(l=>{let v="from"in l?l.from:l.to;return {uri:v.uri,range:v.selectionRange}});return h(this.options.workspacePath,m,this.options.readFile)}async provideDocumentSymbols(t){let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=await this.sendRequest("textDocument/documentSymbol",{textDocument:{uri:e}});if(!i||i.length===0)return [];let n=i[0];return n&&"range"in n&&"selectionRange"in n?i.map(f):i.map(s=>C(this.options.workspacePath,s))}async provideDiagnostics(t){this.assertRunning();let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=this.diagnosticsCache.get(t);return i||(await new Promise(n=>setTimeout(n,200)),this.diagnosticsCache.get(t)??[])}async getWorkspaceDiagnostics(){this.assertRunning();let t=[];for(let e of this.diagnosticsCache.values())t.push(...e);return t}transitionToDead(){this.state="dead",this.cleanup();}cleanup(){for(let t of this.openDocuments.values())t.closeTimer&&clearTimeout(t.closeTimer);if(this.openDocuments.clear(),this.connection){try{this.connection.dispose();}catch{}this.connection=null;}if(this.process){try{this.process.kill();}catch{}this.process=null;}this.state="dead";}};function _(o){return new y(o)}export{y as LspClient,L as convertDiagnosticSeverity,h as convertLocationsToSnippets,g as convertLspDiagnostic,f as convertLspDocumentSymbol,R as convertLspPosition,p as convertLspRange,C as convertSymbolInformation,P as convertSymbolKind,_ as createLspClient,w as guessLanguageId,u as lspUriToPath,c as pathToLspUri};
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider,
|
|
4
|
-
import {
|
|
3
|
+
import { F as FileAccessProvider, E as EditProvider, D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider, c as FrontmatterProvider, i as EditResult, l as FrontmatterMatch, o as GlobalFindMatch, L as Link } from '../interfaces-wSmWeYxt.js';
|
|
4
|
+
import { R as ResolverConfig } from '../resolver-CSuvobsJ.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Zod schemas for MCP tool inputs and outputs.
|
|
@@ -42,6 +42,9 @@ declare const AddLinkSchema: z.ZodObject<{
|
|
|
42
42
|
pattern: z.ZodString;
|
|
43
43
|
link_to: z.ZodString;
|
|
44
44
|
}, z.core.$strip>;
|
|
45
|
+
declare const FileReferencesSchema: z.ZodObject<{
|
|
46
|
+
uri: z.ZodString;
|
|
47
|
+
}, z.core.$strip>;
|
|
45
48
|
declare const GetFrontmatterStructureSchema: z.ZodObject<{
|
|
46
49
|
property: z.ZodString;
|
|
47
50
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -104,6 +107,10 @@ interface ReferencesInstallOptions extends InstallOptions {
|
|
|
104
107
|
onReferencesInput?: (input: z.infer<typeof FuzzyPositionSchema>) => void;
|
|
105
108
|
/** Called with the result of `find_references`. */
|
|
106
109
|
onReferencesOutput?: (output: SnippetsOutput) => void;
|
|
110
|
+
/** Called with the raw tool input for `find_file_references`. */
|
|
111
|
+
onFileReferencesInput?: (input: z.infer<typeof FileReferencesSchema>) => void;
|
|
112
|
+
/** Called with the result of `find_file_references`. */
|
|
113
|
+
onFileReferencesOutput?: (output: SnippetsOutput) => void;
|
|
107
114
|
}
|
|
108
115
|
/** Options for installing a {@link HierarchyProvider}. */
|
|
109
116
|
interface HierarchyInstallOptions extends InstallOptions {
|
|
@@ -155,21 +162,25 @@ interface FrontmatterInstallOptions extends InstallOptions {
|
|
|
155
162
|
/**
|
|
156
163
|
* Install a provider's tools and resources onto an MCP server.
|
|
157
164
|
*
|
|
165
|
+
* Accepts a single provider or an array of providers of the same type. When an array is
|
|
166
|
+
* passed, their results are merged: array-returning methods are concatenated, void methods
|
|
167
|
+
* are called on all providers in parallel, and callback registrars are registered on all.
|
|
168
|
+
*
|
|
158
169
|
* The provider type is detected via duck-typing (checking for characteristic methods).
|
|
159
170
|
* Some providers require `fileAccess` in options:
|
|
160
171
|
* - `DefinitionProvider`, `ReferencesProvider`, `HierarchyProvider` need it for symbol resolution
|
|
161
172
|
* - `EditProvider` needs it for file hash verification
|
|
162
173
|
* - `DiagnosticsProvider`, `OutlineProvider`, `GraphProvider`, `FrontmatterProvider` use it optionally for auto-complete
|
|
163
174
|
*/
|
|
164
|
-
declare function install(server: McpServer, provider: FileAccessProvider, options?: InstallOptions): void;
|
|
165
|
-
declare function install(server: McpServer, provider: EditProvider, options?: EditInstallOptions): void;
|
|
166
|
-
declare function install(server: McpServer, provider: DefinitionProvider, options?: DefinitionInstallOptions): void;
|
|
167
|
-
declare function install(server: McpServer, provider: ReferencesProvider, options?: ReferencesInstallOptions): void;
|
|
168
|
-
declare function install(server: McpServer, provider: HierarchyProvider, options?: HierarchyInstallOptions): void;
|
|
169
|
-
declare function install(server: McpServer, provider: DiagnosticsProvider, options?: InstallOptions): void;
|
|
170
|
-
declare function install(server: McpServer, provider: OutlineProvider, options?: InstallOptions): void;
|
|
171
|
-
declare function install(server: McpServer, provider: GlobalFindProvider, options?: GlobalFindInstallOptions): void;
|
|
172
|
-
declare function install(server: McpServer, provider: GraphProvider, options?: GraphInstallOptions): void;
|
|
173
|
-
declare function install(server: McpServer, provider: FrontmatterProvider, options?: FrontmatterInstallOptions): void;
|
|
175
|
+
declare function install(server: McpServer, provider: FileAccessProvider | FileAccessProvider[], options?: InstallOptions): void;
|
|
176
|
+
declare function install(server: McpServer, provider: EditProvider | EditProvider[], options?: EditInstallOptions): void;
|
|
177
|
+
declare function install(server: McpServer, provider: DefinitionProvider | DefinitionProvider[], options?: DefinitionInstallOptions): void;
|
|
178
|
+
declare function install(server: McpServer, provider: ReferencesProvider | ReferencesProvider[], options?: ReferencesInstallOptions): void;
|
|
179
|
+
declare function install(server: McpServer, provider: HierarchyProvider | HierarchyProvider[], options?: HierarchyInstallOptions): void;
|
|
180
|
+
declare function install(server: McpServer, provider: DiagnosticsProvider | DiagnosticsProvider[], options?: InstallOptions): void;
|
|
181
|
+
declare function install(server: McpServer, provider: OutlineProvider | OutlineProvider[], options?: InstallOptions): void;
|
|
182
|
+
declare function install(server: McpServer, provider: GlobalFindProvider | GlobalFindProvider[], options?: GlobalFindInstallOptions): void;
|
|
183
|
+
declare function install(server: McpServer, provider: GraphProvider | GraphProvider[], options?: GraphInstallOptions): void;
|
|
184
|
+
declare function install(server: McpServer, provider: FrontmatterProvider | FrontmatterProvider[], options?: FrontmatterInstallOptions): void;
|
|
174
185
|
|
|
175
186
|
export { type AnyProvider, type DefinitionInstallOptions, type EditInstallOptions, type FrontmatterInstallOptions, type GlobalFindInstallOptions, type GraphInstallOptions, type HierarchyInstallOptions, type InstallOptions, type ReferencesInstallOptions, install };
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {z}from'zod';var g=class extends Error{constructor(i,
|
|
2
|
-
`)}function
|
|
3
|
-
${
|
|
4
|
-
`)}var H=new Uint16Array(256);for(let
|
|
5
|
-
`)}function
|
|
6
|
-
${
|
|
1
|
+
import {ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {z as z$1}from'zod';var g=class extends Error{constructor(i,r,t){super(`Could not find symbol '${i}' at or near line ${r}. ${t}`);this.symbolName=i;this.lineHint=r;this.reason=t;this.name="SymbolResolutionError";}},O=class{constructor(n,i){this.fs=n;this.lineSearchRadius=i?.lineSearchRadius??2;}lineSearchRadius;async resolvePosition(n,i){let t=(await this.fs.readFile(n)).split(/\r?\n/),e=i.lineHint-1,o=i.orderHint??0,s=this.findSymbolInLine(t[e],i.symbolName,o);if(s!==null)return {line:e,character:s};for(let c=1;c<=this.lineSearchRadius;c++){let l=e-c;if(l>=0){let h=this.findSymbolInLine(t[l],i.symbolName,o);if(h!==null)return {line:l,character:h}}let m=e+c;if(m<t.length){let h=this.findSymbolInLine(t[m],i.symbolName,o);if(h!==null)return {line:m,character:h}}}throw new g(i.symbolName,i.lineHint,`Please verify the file content and try again. Searched lines ${Math.max(1,i.lineHint-this.lineSearchRadius)} to ${Math.min(t.length,i.lineHint+this.lineSearchRadius)}.`)}findSymbolInLine(n,i,r){if(n===void 0||n.length===0)return null;let t=0,e=0;for(;t<n.length;){let o=n.indexOf(i,t);if(o===-1)break;if(e===r)return o;e++,t=o+1;}return null}};var f=a=>({content:[{type:"text",text:JSON.stringify(a??"")}],structuredContent:a});function d(a){if(a.includes(".."))throw new Error('URI could not include ".." operator');return a.replace(/\\/g,"/")}function C(){return `edit-${Date.now()}`}function L(a){return a.length===0?"No diagnostics found.":a.map(n=>{let i=n.range.start.line+1,r=n.severity.toUpperCase(),t=n.source?` [${n.source}]`:"",e=n.code!==void 0?` (${n.code})`:"";return `- **${r}**${t}${e} at line ${i}: ${n.message}`}).join(`
|
|
2
|
+
`)}function $(a,n=0){if(a.length===0&&n===0)return "No symbols found.";let i=" ".repeat(n);return a.map(r=>{let t=r.range.start.line+1,e=r.range.end.line+1,o=t===e?`line ${t}`:`lines ${t}-${e}`,s=r.detail?` - ${r.detail}`:"",c=`${i}- **${r.kind}** \`${r.name}\`${s} (${o})`;return r.children&&r.children.length>0?`${c}
|
|
3
|
+
${$(r.children,n+1)}`:c}).join(`
|
|
4
|
+
`)}var H=new Uint16Array(256);for(let a=0;a<256;a++){let n=a<<8;for(let i=0;i<8;i++)n=n<<1^(n&32768?4129:0);H[a]=n&65535;}function k(a){let n=65535;for(let i=0;i<a.length;i++)n=n<<8&65535^H[(n>>8^a.charCodeAt(i))&255];return (n&255).toString(16).padStart(2,"0")}function M(a){return a.split(/\r?\n/).map((n,i)=>({num:i+1,text:n}))}function j(a){return a.map(n=>`${n.num}:${k(n.text)}|${n.text}`).join(`
|
|
5
|
+
`)}function A(a){let n=a.match(/^(\d+):([0-9a-f]{2})$/);if(!n||!n[1]||!n[2])throw new Error(`Invalid hashline reference "${a}". Expected format: "<line>:<hash>" (e.g., "3:a1")`);return {line:parseInt(n[1],10),hash:n[2]}}var _=z$1.string().describe("The relative file path"),N=z$1.string().describe("The text of the symbol to find"),U=z$1.number().int().positive().describe("Approximate 1-based line number where the symbol is expected"),W=z$1.number().int().min(0).default(0).describe("0-based index of which occurrence to target if symbol appears multiple times"),R=z$1.object({uri:_,symbol_name:N,line_hint:U,order_hint:W}),v=z$1.object({uri:_,start_hash:z$1.string().describe('Start line reference from hashline output, format "<line>:<hash>" (e.g., "3:a1"). Copy this exactly from the files:// resource output.'),end_hash:z$1.string().optional().describe('End line reference for multi-line edits (e.g., "5:0e"). The range is inclusive. Omit for single-line edits (defaults to start_hash).'),replace_text:z$1.string().describe("The new text to replace the entire line range with"),description:z$1.string().describe("Rationale for the edit")}),J=z$1.object({uri:z$1.string().describe("The file URI or path"),symbol_name:N,line_hint:U,order_hint:W,direction:z$1.enum(["incoming","outgoing"]).describe("Direction of the call hierarchy")}),he=z$1.string().describe("The search query"),ge=z$1.boolean().default(false).describe("Whether the search is case-sensitive"),ye=z$1.boolean().default(false).describe("Whether to match exact words only"),ve=z$1.boolean().default(false).describe("Whether the query is a regular expression"),q=z$1.object({query:he,case_sensitive:ge,exact_match:ye,regex_mode:ve}),B=z$1.object({path:z$1.string().describe("The path to the document to modify"),pattern:z$1.string().describe("The text pattern to find and replace with a link"),link_to:z$1.string().describe("The target URI the link should point to")}),Q=z$1.object({uri:_}),K=z$1.object({property:z$1.string().describe("The frontmatter property name to search for"),path:z$1.string().optional().describe("Optional path to limit the search to a specific document. If not provided, searches all documents.")}),V=z$1.object({path:z$1.string().describe("The path to the document to modify"),property:z$1.string().describe("The frontmatter property name to set"),value:z$1.union([z$1.string(),z$1.array(z$1.string()),z$1.number(),z$1.array(z$1.number()),z$1.boolean(),z$1.array(z$1.boolean()),z$1.null()]).describe("The value to set. Can be a string, number, boolean, array of these types, or null to remove.")});function Se(a){if(!a)return null;let n=a.match(/^L(\d+)(?:-L(\d+))?$/);if(!n||!n[1])return null;let i=parseInt(n[1],10),r=n[2]?parseInt(n[2],10):i;return i<1||r<i?null:{start:i,end:r}}function X(a){let n=a.indexOf("?");return n===-1?{path:a,params:new URLSearchParams}:{path:a.slice(0,n),params:new URLSearchParams(a.slice(n+1))}}function z(a){return async n=>{let i=n.lastIndexOf("/"),r=i>=0?n.slice(0,i):"",t=i>=0?n.slice(i+1):n;try{let e=await a(r||"."),o=t.toLowerCase();return e.filter(s=>s.toLowerCase().startsWith(o)).map(s=>r?`${r}/${s}`:s)}catch{return []}}}function Y(a,n,i,r){a.registerTool("goto_definition",{description:"Navigate to the definition of a symbol.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideDefinition(e,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function Z(a,n,i,r){let t=n.provideTypeDefinition;t&&a.registerTool("goto_type_definition",{description:"Navigate to the type definition of a symbol.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async e=>{r?.onInput?.(e);try{let o=d(e.uri),s={symbolName:e.symbol_name,lineHint:e.line_hint,orderHint:e.order_hint},c=await i.resolvePosition(o,s),l=(await t(o,c)).map(m=>({uri:m.uri,startLine:m.range.start.line+1,endLine:m.range.end.line+1,content:m.content}));return r?.onOutput?.({snippets:l}),f({snippets:l})}catch(o){let s=o instanceof g?o.message:`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:s}],structuredContent:{error:s},isError:true}}});}function ee(a,n,i,r){a.registerTool("find_references",{description:"Find all references to a symbol. Returns a list of locations where the symbol is used.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideReferences(e,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function te(a,n,i,r){a.registerTool("call_hierarchy",{description:"Get call hierarchy for a function or method. Shows incoming or outgoing calls.",inputSchema:J,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideCallHierarchy(e,s,t.direction)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function re(a,n,i){let r=n.provideFileReferences;r&&a.registerTool("find_file_references",{description:"Find all references to a file across the workspace. Returns locations that import or link to the given file.",inputSchema:Q,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{i?.onInput?.(t);try{let e=d(t.uri),o=(await r(e)).map(s=>({uri:s.uri,startLine:s.range.start.line+1,endLine:s.range.end.line+1,content:s.content}));return i?.onOutput?.({snippets:o}),f({snippets:o})}catch(e){let o=`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function ne(a,n,i){let r=new ResourceTemplate("diagnostics://{+path}",{list:void 0,complete:i?{path:i}:void 0});if(a.registerResource("diagnostics",r,{description:"Diagnostics (errors, warnings, hints) for a specific file. Use the file path after diagnostics://",mimeType:"text/markdown"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.provideDiagnostics(s),l=L(c);return {contents:[{uri:`diagnostics://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`diagnostics://${e.path}`,mimeType:"text/markdown",text:s}]}}}),n.getWorkspaceDiagnostics){let t=n.getWorkspaceDiagnostics.bind(n);a.registerResource("workspace-diagnostics","diagnostics://workspace",{description:"All diagnostics (errors, warnings, hints) across the entire workspace",mimeType:"text/markdown"},async()=>{try{let e=await t(),o=new Map;for(let c of e){let l=o.get(c.uri)??[];l.push(c),o.set(c.uri,l);}if(o.size===0)return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:"No diagnostics found in workspace."}]};let s=[];for(let[c,l]of o)s.push(`## ${c}
|
|
6
|
+
${L(l)}`);return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:s.join(`
|
|
7
7
|
|
|
8
|
-
`)}]}}catch(n){return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:`Error: ${n instanceof Error?n.message:String(n)}`}]}}});}e.onDiagnosticsChanged&&e.onDiagnosticsChanged(o=>{let n=m(o);r.server.sendResourceUpdated({uri:`diagnostics://${n}`}),e.getWorkspaceDiagnostics&&r.server.sendResourceUpdated({uri:"diagnostics://workspace"});});}function ne(r,e,i){let a=new ResourceTemplate("outline://{+path}",{list:void 0,complete:i?{path:i}:void 0});r.registerResource("outline",a,{description:"Document outline (symbols like classes, functions, variables) for a specific file. Use the file path after outline://",mimeType:"text/markdown"},async(o,n)=>{try{let t=n.path,s=m(t),c=await e.provideDocumentSymbols(s),l=D(c);return {contents:[{uri:`outline://${t}`,mimeType:"text/markdown",text:l}]}}catch(t){let s=`Error: ${t instanceof Error?t.message:String(t)}`;return {contents:[{uri:`outline://${n.path}`,mimeType:"text/markdown",text:s}]}}});}function re(r,e,i,a){let o=e.previewAndApplyEdits?.bind(e)??e.applyEdits?.bind(e);o&&r.registerTool("apply_edit",{description:'Apply a text edit to a file. WORKFLOW: First read the file via the files:// resource to get hashline-formatted content (e.g., "3:a1| return x"). Then reference lines by their "line:hash" to specify the edit range. The hash verifies the file has not changed since your read \u2014 if it has, the edit is rejected and you must re-read the file. For single-line edits, only start_hash is needed. For multi-line edits, provide both start_hash and end_hash. The edit replaces the entire line range (inclusive) with replace_text. The edit must be approved by the user before being applied.',inputSchema:{uri:v.shape.uri,start_hash:v.shape.start_hash,end_hash:v.shape.end_hash,replace_text:v.shape.replace_text,description:v.shape.description},outputSchema:{success:z.boolean(),message:z.string()}},async n=>{a?.onInput?.(n);try{let t=m(n.uri),s=_(n.start_hash),c=n.end_hash?_(n.end_hash):s,d=(await i(t)).split(/\r?\n/);if(s.line<1||s.line>d.length)throw new Error(`Start line ${s.line} is out of range (file has ${d.length} lines)`);if(c.line<s.line||c.line>d.length)throw new Error(`End line ${c.line} is out of range (file has ${d.length} lines)`);let h=d[s.line-1],b=I(h);if(b!==s.hash)throw new Error(`Hash mismatch at line ${s.line}: expected "${s.hash}", got "${b}". File has changed since last read.`);let P=d[c.line-1],F=I(P);if(F!==c.hash)throw new Error(`Hash mismatch at line ${c.line}: expected "${c.hash}", got "${F}". File has changed since last read.`);let k={start:{line:s.line-1,character:0},end:{line:c.line-1,character:P.length}},T={id:G(),uri:t,edits:[{range:k,newText:n.replace_text}],description:n.description},y=await o(T)?{success:!0,message:"Edit successfully applied and saved."}:{success:!1,message:"Edit rejected by user."};return a?.onOutput?.(y),f(y)}catch(t){let s=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:s}],structuredContent:{success:false,message:s},isError:true}}});}function ie(r,e){let{readFile:i,readDirectory:a}=e,o=new ResourceTemplate("files://{+path}",{list:void 0,complete:{path:A(a)}});r.registerResource("filesystem",o,{description:'Access filesystem resources. For directories: returns children as JSON (git-ignored files excluded). For files: returns content in hashline format where each line is prefixed with "<lineNumber>:<hash>|" (e.g., "1:a3|function hello() {"). The hash is a 2-char hex CRC16 digest of the line content. Use these line:hash references with the apply_edit tool to make edits. Supports line ranges with #L23 or #L23-L30 fragment. Supports regex filtering with ?pattern=<regex> query parameter (matches raw line text, not the hash prefix). Line numbers in the output are always the original file line numbers, even when filtering.'},async(n,t)=>{let s=n.toString();try{let c=t.path,l,d=c,h=c.indexOf("#");h!==-1&&(l=c.slice(h+1),d=c.slice(0,h));let{path:b,params:P}=V(d),{path:F,params:k}=V(l??"");l=l?F:void 0;let T=new URLSearchParams([...P.entries(),...k.entries()]),R=m(b),y=ye(l),z=T.get("pattern");try{let L=await i(R),S=C(L);if(y&&(S=S.filter(O=>O.num>=y.start&&O.num<=y.end)),z){let O=new RegExp(z);S=S.filter(de=>O.test(de.text));}return {contents:[{uri:s,mimeType:"text/plain",text:M(S)}]}}catch{let L=await a(R);return {contents:[{uri:s,mimeType:"application/json",text:JSON.stringify(L)}]}}}catch(c){let l=`Error: ${c instanceof Error?c.message:String(c)}`;return {contents:[{uri:s,mimeType:"text/plain",text:l}]}}}),e.onFileChanged&&e.onFileChanged(n=>{let t=m(n);r.server.sendResourceUpdated({uri:`files://${t}`});});}function oe(r,e,i){r.registerTool("global_find",{description:"Search for text across the entire workspace.",inputSchema:q,outputSchema:{matches:z.array(z.object({uri:z.string(),line:z.number(),column:z.number(),matchText:z.string(),context:z.string()})),count:z.number()}},async a=>{i?.onInput?.(a);try{let o=a.case_sensitive??!1,n=a.exact_match??!1,t=a.regex_mode??!1,s=await e.globalFind(a.query,{caseSensitive:o,exactMatch:n,regexMode:t});return i?.onOutput?.({count:s.length,matches:s}),f({count:s.length,matches:s})}catch(o){let n=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:n}],structuredContent:{error:n},isError:true}}});}function se(r,e,i){let a=new ResourceTemplate("outlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});r.registerResource("outlinks",a,{description:"Outgoing links from a specific file. Use the file path after outlinks://",mimeType:"application/json"},async(n,t)=>{try{let s=t.path,c=m(s),l=await e.resolveOutlinks(c);return {contents:[{uri:`outlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`outlinks://${t.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});let o=new ResourceTemplate("backlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});r.registerResource("backlinks",o,{description:"Incoming links (backlinks) to a specific file. Use the file path after backlinks://",mimeType:"application/json"},async(n,t)=>{try{let s=t.path,c=m(s),l=await e.resolveBacklinks(c);return {contents:[{uri:`backlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`backlinks://${t.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});}function ae(r,e,i){r.registerTool("get_link_structure",{description:"Get all links in the workspace, showing relationships between documents.",inputSchema:{},outputSchema:{links:z.array(z.object({sourceUri:z.string(),targetUri:z.string(),subpath:z.string().optional(),displayText:z.string().optional(),resolved:z.boolean(),line:z.number(),column:z.number()}))}},async()=>{try{let a=await e.getLinkStructure();return i?.onOutput?.({links:a}),f({links:a})}catch(a){let o=`Error: ${a instanceof Error?a.message:String(a)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function ce(r,e,i){r.registerTool("add_link",{description:"Add a link to a document by finding a text pattern and replacing it with a link to the target.",inputSchema:B,outputSchema:{success:z.boolean(),message:z.string().optional()}},async a=>{i?.onInput?.(a);try{let o=m(a.path),n=m(a.link_to);await e.addLink(o,a.pattern,n);let t={success:!0,message:"Link added successfully."};return i?.onOutput?.(t),f(t)}catch(o){let n=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:n}],structuredContent:{success:false,message:n},isError:true}}});}function ue(r,e,i){r.registerTool("get_frontmatter_structure",{description:"Get frontmatter property values across documents. If path is provided, searches only that document. Otherwise, searches all documents.",inputSchema:Q,outputSchema:{matches:z.array(z.object({path:z.string(),value:z.unknown()}))}},async a=>{i?.onInput?.(a);try{let o=a.path?m(a.path):void 0,n=await e.getFrontmatterStructure(a.property,o);return i?.onOutput?.({matches:n}),f({matches:n})}catch(o){let n=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:n}],structuredContent:{error:n},isError:true}}});}function le(r,e,i){r.registerTool("set_frontmatter",{description:"Set a frontmatter property on a document. Use null to remove the property.",inputSchema:K,outputSchema:{success:z.boolean(),message:z.string().optional()}},async a=>{i?.onInput?.(a);try{let o=m(a.path),n=a.value===null?void 0:a.value;await e.setFrontmatter(o,a.property,n);let t={success:!0,message:"Frontmatter updated successfully."};return i?.onOutput?.(t),f(t)}catch(o){let n=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:n}],structuredContent:{success:false,message:n},isError:true}}});}function pe(r,e,i){let a=new ResourceTemplate("frontmatter://{+path}",{list:void 0,complete:i?{path:i}:void 0});r.registerResource("frontmatter",a,{description:"Frontmatter metadata for a specific file. Use the file path after frontmatter://",mimeType:"application/json"},async(o,n)=>{try{let t=n.path,s=m(t),c=await e.getFrontmatter(s);return {contents:[{uri:`frontmatter://${t}`,mimeType:"application/json",text:JSON.stringify(c,null,2)}]}}catch(t){let s=`Error: ${t instanceof Error?t.message:String(t)}`;return {contents:[{uri:`frontmatter://${n.path}`,mimeType:"application/json",text:JSON.stringify({error:s})}]}}});}function $e(r,e,i){let a=i?.fileAccess,o=a?A(a.readDirectory):void 0,n=()=>{if(!a)throw new Error("fileAccess is required in options for providers that need symbol resolution");return new w(a,i?.resolverConfig)};if("readFile"in e&&"readDirectory"in e){ie(r,e);return}if("applyEdits"in e||"previewAndApplyEdits"in e){if(!a)throw new Error("fileAccess is required in options when installing an EditProvider");let t=i;re(r,e,a.readFile,{onInput:t?.onEditInput,onOutput:t?.onEditOutput});return}if("provideDefinition"in e){let t=e,s=i,c=n();X(r,t,c,{onInput:s?.onDefinitionInput,onOutput:s?.onDefinitionOutput}),t.provideTypeDefinition&&Y(r,t,c,{onInput:s?.onTypeDefinitionInput,onOutput:s?.onTypeDefinitionOutput});return}if("provideReferences"in e){let t=i;Z(r,e,n(),{onInput:t?.onReferencesInput,onOutput:t?.onReferencesOutput});return}if("provideCallHierarchy"in e){let t=i;ee(r,e,n(),{onInput:t?.onCallHierarchyInput,onOutput:t?.onCallHierarchyOutput});return}if("provideDiagnostics"in e){te(r,e,o);return}if("provideDocumentSymbols"in e){ne(r,e,o);return}if("globalFind"in e){let t=i;oe(r,e,{onInput:t?.onGlobalFindInput,onOutput:t?.onGlobalFindOutput});return}if("getLinkStructure"in e){let t=e,s=i;ae(r,t,{onOutput:s?.onLinkStructureOutput}),ce(r,t,{onInput:s?.onAddLinkInput,onOutput:s?.onAddLinkOutput}),se(r,t,o);return}if("getFrontmatterStructure"in e){let t=e,s=i;ue(r,t,{onInput:s?.onFrontmatterStructureInput,onOutput:s?.onFrontmatterStructureOutput}),le(r,t,{onInput:s?.onSetFrontmatterInput,onOutput:s?.onSetFrontmatterOutput}),pe(r,t,o);return}}
|
|
9
|
-
export{
|
|
8
|
+
`)}]}}catch(e){return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:`Error: ${e instanceof Error?e.message:String(e)}`}]}}});}n.onDiagnosticsChanged&&n.onDiagnosticsChanged(t=>{let e=d(t);a.server.sendResourceUpdated({uri:`diagnostics://${e}`}),n.getWorkspaceDiagnostics&&a.server.sendResourceUpdated({uri:"diagnostics://workspace"});});}function ie(a,n,i){let r=new ResourceTemplate("outline://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("outline",r,{description:"Document outline (symbols like classes, functions, variables) for a specific file. Use the file path after outline://",mimeType:"text/markdown"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.provideDocumentSymbols(s),l=$(c);return {contents:[{uri:`outline://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`outline://${e.path}`,mimeType:"text/markdown",text:s}]}}});}function oe(a,n,i,r){let t=n.previewAndApplyEdits?.bind(n)??n.applyEdits?.bind(n);t&&a.registerTool("apply_edit",{description:'Apply a text edit to a file. WORKFLOW: First read the file via the files:// resource to get hashline-formatted content (e.g., "3:a1| return x"). Then reference lines by their "line:hash" to specify the edit range. The hash verifies the file has not changed since your read \u2014 if it has, the edit is rejected and you must re-read the file. For single-line edits, only start_hash is needed. For multi-line edits, provide both start_hash and end_hash. The edit replaces the entire line range (inclusive) with replace_text. The edit must be approved by the user before being applied.',inputSchema:{uri:v.shape.uri,start_hash:v.shape.start_hash,end_hash:v.shape.end_hash,replace_text:v.shape.replace_text,description:v.shape.description},outputSchema:{success:z$1.boolean(),message:z$1.string()}},async e=>{r?.onInput?.(e);try{let o=d(e.uri),s=A(e.start_hash),c=e.end_hash?A(e.end_hash):s,m=(await i(o)).split(/\r?\n/);if(s.line<1||s.line>m.length)throw new Error(`Start line ${s.line} is out of range (file has ${m.length} lines)`);if(c.line<s.line||c.line>m.length)throw new Error(`End line ${c.line} is out of range (file has ${m.length} lines)`);let h=m[s.line-1],b=k(h);if(b!==s.hash)throw new Error(`Hash mismatch at line ${s.line}: expected "${s.hash}", got "${b}". File has changed since last read.`);let P=m[c.line-1],F=k(P);if(F!==c.hash)throw new Error(`Hash mismatch at line ${c.line}: expected "${c.hash}", got "${F}". File has changed since last read.`);let E={start:{line:s.line-1,character:0},end:{line:c.line-1,character:P.length}},I={id:C(),uri:o,edits:[{range:E,newText:e.replace_text}],description:e.description},y=await t(I)?{success:!0,message:"Edit successfully applied and saved."}:{success:!1,message:"Edit rejected by user."};return r?.onOutput?.(y),f(y)}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:s}],structuredContent:{success:false,message:s},isError:true}}});}function se(a,n){let{readFile:i,readDirectory:r}=n,t=new ResourceTemplate("files://{+path}",{list:void 0,complete:{path:z(r)}});a.registerResource("filesystem",t,{description:'Access filesystem resources. For directories: returns children as JSON (git-ignored files excluded). For files: returns content in hashline format where each line is prefixed with "<lineNumber>:<hash>|" (e.g., "1:a3|function hello() {"). The hash is a 2-char hex CRC16 digest of the line content. Use these line:hash references with the apply_edit tool to make edits. Supports line ranges with #L23 or #L23-L30 fragment. Supports regex filtering with ?pattern=<regex> query parameter (matches raw line text, not the hash prefix). Line numbers in the output are always the original file line numbers, even when filtering.'},async(e,o)=>{let s=e.toString();try{let c=o.path,l,m=c,h=c.indexOf("#");h!==-1&&(l=c.slice(h+1),m=c.slice(0,h));let{path:b,params:P}=X(m),{path:F,params:E}=X(l??"");l=l?F:void 0;let I=new URLSearchParams([...P.entries(),...E.entries()]),T=d(b),y=Se(l),G=I.get("pattern");try{let D=await i(T),x=M(D);if(y&&(x=x.filter(w=>w.num>=y.start&&w.num<=y.end)),G){let w=new RegExp(G);x=x.filter(fe=>w.test(fe.text));}return {contents:[{uri:s,mimeType:"text/plain",text:j(x)}]}}catch{let D=await r(T);return {contents:[{uri:s,mimeType:"application/json",text:JSON.stringify(D)}]}}}catch(c){let l=`Error: ${c instanceof Error?c.message:String(c)}`;return {contents:[{uri:s,mimeType:"text/plain",text:l}]}}}),n.onFileChanged&&n.onFileChanged(e=>{let o=d(e);a.server.sendResourceUpdated({uri:`files://${o}`});});}function ae(a,n,i){a.registerTool("global_find",{description:"Search for text across the entire workspace.",inputSchema:q,outputSchema:{matches:z$1.array(z$1.object({uri:z$1.string(),line:z$1.number(),column:z$1.number(),matchText:z$1.string(),context:z$1.string()})),count:z$1.number()}},async r=>{i?.onInput?.(r);try{let t=r.case_sensitive??!1,e=r.exact_match??!1,o=r.regex_mode??!1,s=await n.globalFind(r.query,{caseSensitive:t,exactMatch:e,regexMode:o});return i?.onOutput?.({count:s.length,matches:s}),f({count:s.length,matches:s})}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{error:e},isError:true}}});}function ce(a,n,i){let r=new ResourceTemplate("outlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("outlinks",r,{description:"Outgoing links from a specific file. Use the file path after outlinks://",mimeType:"application/json"},async(e,o)=>{try{let s=o.path,c=d(s),l=await n.resolveOutlinks(c);return {contents:[{uri:`outlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`outlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});let t=new ResourceTemplate("backlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("backlinks",t,{description:"Incoming links (backlinks) to a specific file. Use the file path after backlinks://",mimeType:"application/json"},async(e,o)=>{try{let s=o.path,c=d(s),l=await n.resolveBacklinks(c);return {contents:[{uri:`backlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`backlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});}function ue(a,n,i){a.registerTool("get_link_structure",{description:"Get all links in the workspace, showing relationships between documents.",inputSchema:{},outputSchema:{links:z$1.array(z$1.object({sourceUri:z$1.string(),targetUri:z$1.string(),subpath:z$1.string().optional(),displayText:z$1.string().optional(),resolved:z$1.boolean(),line:z$1.number(),column:z$1.number()}))}},async()=>{try{let r=await n.getLinkStructure();return i?.onOutput?.({links:r}),f({links:r})}catch(r){let t=`Error: ${r instanceof Error?r.message:String(r)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function le(a,n,i){a.registerTool("add_link",{description:"Add a link to a document by finding a text pattern and replacing it with a link to the target.",inputSchema:B,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async r=>{i?.onInput?.(r);try{let t=d(r.path),e=d(r.link_to);await n.addLink(t,r.pattern,e);let o={success:!0,message:"Link added successfully."};return i?.onOutput?.(o),f(o)}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{success:false,message:e},isError:true}}});}function pe(a,n,i){a.registerTool("get_frontmatter_structure",{description:"Get frontmatter property values across documents. If path is provided, searches only that document. Otherwise, searches all documents.",inputSchema:K,outputSchema:{matches:z$1.array(z$1.object({path:z$1.string(),value:z$1.unknown()}))}},async r=>{i?.onInput?.(r);try{let t=r.path?d(r.path):void 0,e=await n.getFrontmatterStructure(r.property,t);return i?.onOutput?.({matches:e}),f({matches:e})}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{error:e},isError:true}}});}function de(a,n,i){a.registerTool("set_frontmatter",{description:"Set a frontmatter property on a document. Use null to remove the property.",inputSchema:V,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async r=>{i?.onInput?.(r);try{let t=d(r.path),e=r.value===null?void 0:r.value;await n.setFrontmatter(t,r.property,e);let o={success:!0,message:"Frontmatter updated successfully."};return i?.onOutput?.(o),f(o)}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{success:false,message:e},isError:true}}});}function me(a,n,i){let r=new ResourceTemplate("frontmatter://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("frontmatter",r,{description:"Frontmatter metadata for a specific file. Use the file path after frontmatter://",mimeType:"application/json"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.getFrontmatter(s);return {contents:[{uri:`frontmatter://${o}`,mimeType:"application/json",text:JSON.stringify(c,null,2)}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`frontmatter://${e.path}`,mimeType:"application/json",text:JSON.stringify({error:s})}]}}});}function xe(a){if(a.length<=0)throw new Error("providers array must not be empty");if(a.length===1&&a[0])return a[0];let n=a[0];if(!n)throw new Error("The first item of providers array must be defined");if("readFile"in n&&"readDirectory"in n){let i=a,r={readFile:t=>n.readFile(t),readDirectory:t=>n.readDirectory(t)};return i.some(t=>t.onFileChanged)&&(r.onFileChanged=t=>{for(let e of i)e.onFileChanged?.(t);}),r}if("applyEdits"in n||"previewAndApplyEdits"in n)return n;if("provideDefinition"in n){let i=a,r={async provideDefinition(t,e){return (await Promise.all(i.map(s=>s.provideDefinition(t,e)))).flat()}};return i.some(t=>t.provideTypeDefinition)&&(r.provideTypeDefinition=async(t,e)=>{let o=i.filter(c=>c.provideTypeDefinition);return (await Promise.all(o.map(c=>c.provideTypeDefinition(t,e)))).flat()}),r}if("provideReferences"in n){let i=a,r={async provideReferences(t,e){return (await Promise.all(i.map(s=>s.provideReferences(t,e)))).flat()}};return i.some(t=>t.provideFileReferences)&&(r.provideFileReferences=async t=>{let e=i.filter(s=>s.provideFileReferences);return (await Promise.all(e.map(s=>s.provideFileReferences(t)))).flat()}),r}if("provideCallHierarchy"in n){let i=a;return {async provideCallHierarchy(r,t,e){return (await Promise.all(i.map(s=>s.provideCallHierarchy(r,t,e)))).flat()}}}if("provideDiagnostics"in n){let i=a,r={async provideDiagnostics(t){return (await Promise.all(i.map(o=>o.provideDiagnostics(t)))).flat()}};return i.some(t=>t.getWorkspaceDiagnostics)&&(r.getWorkspaceDiagnostics=async()=>{let t=i.filter(o=>o.getWorkspaceDiagnostics);return (await Promise.all(t.map(o=>o.getWorkspaceDiagnostics()))).flat()}),i.some(t=>t.onDiagnosticsChanged)&&(r.onDiagnosticsChanged=t=>{for(let e of i)e.onDiagnosticsChanged?.(t);}),r}if("provideDocumentSymbols"in n){let i=a;return {async provideDocumentSymbols(r){return (await Promise.all(i.map(e=>e.provideDocumentSymbols(r)))).flat()}}}if("globalFind"in n){let i=a;return {async globalFind(r,t){return (await Promise.all(i.map(o=>o.globalFind(r,t)))).flat()}}}if("getLinkStructure"in n){let i=a;return {async getLinkStructure(){return (await Promise.all(i.map(t=>t.getLinkStructure()))).flat()},async resolveOutlinks(r){return (await Promise.all(i.map(e=>e.resolveOutlinks(r)))).flat()},async resolveBacklinks(r){return (await Promise.all(i.map(e=>e.resolveBacklinks(r)))).flat()},async addLink(r,t,e){await Promise.all(i.map(o=>o.addLink(r,t,e)));}}}if("getFrontmatterStructure"in n){let i=a;return {async getFrontmatterStructure(r,t){return (await Promise.all(i.map(o=>o.getFrontmatterStructure(r,t)))).flat()},async getFrontmatter(r){let t=await Promise.all(i.map(e=>e.getFrontmatter(r)));return Object.assign({},...t)},async setFrontmatter(r,t,e){await Promise.all(i.map(o=>o.setFrontmatter(r,t,e)));}}}return n}function _e(a,n,i){let r=Array.isArray(n)?xe(n):n,t=i?.fileAccess,e=t?z(t.readDirectory):void 0,o=()=>{if(!t)throw new Error("fileAccess is required in options for providers that need symbol resolution");return new O(t,i?.resolverConfig)};if("readFile"in r&&"readDirectory"in r){se(a,r);return}if("applyEdits"in r||"previewAndApplyEdits"in r){if(!t)throw new Error("fileAccess is required in options when installing an EditProvider");let s=i;oe(a,r,t.readFile,{onInput:s?.onEditInput,onOutput:s?.onEditOutput});return}if("provideDefinition"in r){let s=r,c=i,l=o();Y(a,s,l,{onInput:c?.onDefinitionInput,onOutput:c?.onDefinitionOutput}),s.provideTypeDefinition&&Z(a,s,l,{onInput:c?.onTypeDefinitionInput,onOutput:c?.onTypeDefinitionOutput});return}if("provideReferences"in r){let s=r,c=i;ee(a,s,o(),{onInput:c?.onReferencesInput,onOutput:c?.onReferencesOutput}),re(a,s,{onInput:c?.onFileReferencesInput,onOutput:c?.onFileReferencesOutput});return}if("provideCallHierarchy"in r){let s=i;te(a,r,o(),{onInput:s?.onCallHierarchyInput,onOutput:s?.onCallHierarchyOutput});return}if("provideDiagnostics"in r){ne(a,r,e);return}if("provideDocumentSymbols"in r){ie(a,r,e);return}if("globalFind"in r){let s=i;ae(a,r,{onInput:s?.onGlobalFindInput,onOutput:s?.onGlobalFindOutput});return}if("getLinkStructure"in r){let s=r,c=i;ue(a,s,{onOutput:c?.onLinkStructureOutput}),le(a,s,{onInput:c?.onAddLinkInput,onOutput:c?.onAddLinkOutput}),ce(a,s,e);return}if("getFrontmatterStructure"in r){let s=r,c=i;pe(a,s,{onInput:c?.onFrontmatterStructureInput,onOutput:c?.onFrontmatterStructureOutput}),de(a,s,{onInput:c?.onSetFrontmatterInput,onOutput:c?.onSetFrontmatterOutput}),me(a,s,e);return}}
|
|
9
|
+
export{_e as install};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { F as FileAccessProvider, U as UnifiedUri, n as FuzzyPosition, j as ExactPosition } from './interfaces-wSmWeYxt.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smart Resolver Logic for MCP LSP Driver SDK
|
|
5
|
+
*
|
|
6
|
+
* This is the internal engine of the SDK. It translates the LLM's imprecise
|
|
7
|
+
* instructions into precise coordinates.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configuration options for the SymbolResolver.
|
|
12
|
+
*/
|
|
13
|
+
interface ResolverConfig {
|
|
14
|
+
/**
|
|
15
|
+
* Number of lines to scan above and below the lineHint if the symbol
|
|
16
|
+
* is not found at the exact line. Defaults to 2.
|
|
17
|
+
*/
|
|
18
|
+
lineSearchRadius?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when a symbol cannot be resolved.
|
|
22
|
+
*/
|
|
23
|
+
declare class SymbolResolutionError extends Error {
|
|
24
|
+
readonly symbolName: string;
|
|
25
|
+
readonly lineHint: number;
|
|
26
|
+
readonly reason: string;
|
|
27
|
+
constructor(symbolName: string, lineHint: number, reason: string);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The SymbolResolver translates fuzzy positions (as provided by an LLM)
|
|
31
|
+
* into exact positions that can be used by the IDE.
|
|
32
|
+
*
|
|
33
|
+
* Algorithm:
|
|
34
|
+
* 1. Read file content via FileAccessProvider.
|
|
35
|
+
* 2. Split content into lines.
|
|
36
|
+
* 3. Target the lineHint (converting 1-based to 0-based).
|
|
37
|
+
* 4. Search for symbolName in that line.
|
|
38
|
+
* - If orderHint is 0, find first occurrence.
|
|
39
|
+
* - If orderHint is N, find Nth occurrence.
|
|
40
|
+
* 5. Robustness Fallback:
|
|
41
|
+
* - If the line is empty or symbol not found at lineHint,
|
|
42
|
+
* scan +/- lineSearchRadius lines to handle minor line shifts.
|
|
43
|
+
* 6. Return ExactPosition (line, character start index).
|
|
44
|
+
*/
|
|
45
|
+
declare class SymbolResolver {
|
|
46
|
+
private readonly fs;
|
|
47
|
+
private readonly lineSearchRadius;
|
|
48
|
+
constructor(fs: FileAccessProvider, config?: ResolverConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Resolves a fuzzy position to an exact position.
|
|
51
|
+
*
|
|
52
|
+
* @param uri - The URI of the file
|
|
53
|
+
* @param fuzzy - The fuzzy position provided by the LLM
|
|
54
|
+
* @returns The exact position in the file
|
|
55
|
+
* @throws SymbolResolutionError if the symbol cannot be found
|
|
56
|
+
*/
|
|
57
|
+
resolvePosition(uri: UnifiedUri, fuzzy: FuzzyPosition): Promise<ExactPosition>;
|
|
58
|
+
/**
|
|
59
|
+
* Finds the Nth occurrence of a symbol in a line.
|
|
60
|
+
*
|
|
61
|
+
* @param line - The line to search in (may be undefined if out of bounds)
|
|
62
|
+
* @param symbolName - The symbol to find
|
|
63
|
+
* @param orderHint - Which occurrence to find (0-based)
|
|
64
|
+
* @returns The character offset of the symbol, or null if not found
|
|
65
|
+
*/
|
|
66
|
+
private findSymbolInLine;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { type ResolverConfig as R, SymbolResolutionError as S, SymbolResolver as a };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opticlm/connector",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Provides an abstract interface that allows LLMs to connect to fact sources such as LSPs, code diagnostics, symbol definitions/references, links, and frontmatter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"@types/node": "^25",
|
|
53
53
|
"tsup": "^8.5.1",
|
|
54
54
|
"typescript": "^5.9",
|
|
55
|
+
"nanoid": "^5.1.7",
|
|
55
56
|
"vitest": "^4"
|
|
56
57
|
},
|
|
57
58
|
"engines": {
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { P as PendingEditOperation, U as UnifiedUri, l as FuzzyPosition, h as ExactPosition } from './capabilities-CQHv2shL.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Infrastructure Interfaces for MCP LSP Driver SDK
|
|
5
|
-
*
|
|
6
|
-
* The SDK consumer (Plugin Developer) must implement these interfaces
|
|
7
|
-
* to bridge the SDK to the specific IDE.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Provides access to the file system for reading files.
|
|
12
|
-
* Since the SDK is responsible for resolving FuzzyPosition to ExactPosition,
|
|
13
|
-
* it needs direct access to read files from the disk.
|
|
14
|
-
*/
|
|
15
|
-
interface FileAccessProvider {
|
|
16
|
-
/**
|
|
17
|
-
* Reads the content of a file from the disk (ignoring unsaved IDE buffers).
|
|
18
|
-
* Used for symbol resolution and context retrieval.
|
|
19
|
-
*
|
|
20
|
-
* @param uri - The URI of the file to read
|
|
21
|
-
* @returns The content of the file as a string
|
|
22
|
-
* @throws Error if the file cannot be read
|
|
23
|
-
*/
|
|
24
|
-
readFile(uri: UnifiedUri): Promise<string>;
|
|
25
|
-
/**
|
|
26
|
-
* Read children in a directory, exluding git-ignored files, similar to Unix `ls` command
|
|
27
|
-
* @param relativePath - The path to the folder to read
|
|
28
|
-
* @returns Array of file/folder paths in the directory
|
|
29
|
-
*/
|
|
30
|
-
readDirectory(relativePath: UnifiedUri): Promise<string[]>;
|
|
31
|
-
/**
|
|
32
|
-
* Called by the driver to register a callback for file changes.
|
|
33
|
-
* When provided, the files:// resources become subscribable.
|
|
34
|
-
* The plugin should call the registered callback whenever a file changes.
|
|
35
|
-
*
|
|
36
|
-
* @param callback - The callback to invoke when a file changes
|
|
37
|
-
*/
|
|
38
|
-
onFileChanged?(callback: (uri: UnifiedUri) => void): void;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Provides edit capabilities for applying changes to files.
|
|
42
|
-
* At least one of the methods must be implemented.
|
|
43
|
-
*/
|
|
44
|
-
interface EditProvider {
|
|
45
|
-
/**
|
|
46
|
-
* Applies edits directly without user interaction.
|
|
47
|
-
* Use this when user approval is handled elsewhere or not needed.
|
|
48
|
-
*
|
|
49
|
-
* @param operation - The pending edit operation to apply
|
|
50
|
-
* @returns true if applied successfully, false if failed
|
|
51
|
-
*/
|
|
52
|
-
applyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
53
|
-
/**
|
|
54
|
-
* Displays a diff view or a confirmation dialog in the IDE.
|
|
55
|
-
* The user decides whether to apply the edits or discard them.
|
|
56
|
-
*
|
|
57
|
-
* @param operation - The pending edit operation to preview
|
|
58
|
-
* @returns true if applied, false if rejected/cancelled
|
|
59
|
-
*/
|
|
60
|
-
previewAndApplyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Smart Resolver Logic for MCP LSP Driver SDK
|
|
65
|
-
*
|
|
66
|
-
* This is the internal engine of the SDK. It translates the LLM's imprecise
|
|
67
|
-
* instructions into precise coordinates.
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Configuration options for the SymbolResolver.
|
|
72
|
-
*/
|
|
73
|
-
interface ResolverConfig {
|
|
74
|
-
/**
|
|
75
|
-
* Number of lines to scan above and below the lineHint if the symbol
|
|
76
|
-
* is not found at the exact line. Defaults to 2.
|
|
77
|
-
*/
|
|
78
|
-
lineSearchRadius?: number;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Error thrown when a symbol cannot be resolved.
|
|
82
|
-
*/
|
|
83
|
-
declare class SymbolResolutionError extends Error {
|
|
84
|
-
readonly symbolName: string;
|
|
85
|
-
readonly lineHint: number;
|
|
86
|
-
readonly reason: string;
|
|
87
|
-
constructor(symbolName: string, lineHint: number, reason: string);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* The SymbolResolver translates fuzzy positions (as provided by an LLM)
|
|
91
|
-
* into exact positions that can be used by the IDE.
|
|
92
|
-
*
|
|
93
|
-
* Algorithm:
|
|
94
|
-
* 1. Read file content via FileAccessProvider.
|
|
95
|
-
* 2. Split content into lines.
|
|
96
|
-
* 3. Target the lineHint (converting 1-based to 0-based).
|
|
97
|
-
* 4. Search for symbolName in that line.
|
|
98
|
-
* - If orderHint is 0, find first occurrence.
|
|
99
|
-
* - If orderHint is N, find Nth occurrence.
|
|
100
|
-
* 5. Robustness Fallback:
|
|
101
|
-
* - If the line is empty or symbol not found at lineHint,
|
|
102
|
-
* scan +/- lineSearchRadius lines to handle minor line shifts.
|
|
103
|
-
* 6. Return ExactPosition (line, character start index).
|
|
104
|
-
*/
|
|
105
|
-
declare class SymbolResolver {
|
|
106
|
-
private readonly fs;
|
|
107
|
-
private readonly lineSearchRadius;
|
|
108
|
-
constructor(fs: FileAccessProvider, config?: ResolverConfig);
|
|
109
|
-
/**
|
|
110
|
-
* Resolves a fuzzy position to an exact position.
|
|
111
|
-
*
|
|
112
|
-
* @param uri - The URI of the file
|
|
113
|
-
* @param fuzzy - The fuzzy position provided by the LLM
|
|
114
|
-
* @returns The exact position in the file
|
|
115
|
-
* @throws SymbolResolutionError if the symbol cannot be found
|
|
116
|
-
*/
|
|
117
|
-
resolvePosition(uri: UnifiedUri, fuzzy: FuzzyPosition): Promise<ExactPosition>;
|
|
118
|
-
/**
|
|
119
|
-
* Finds the Nth occurrence of a symbol in a line.
|
|
120
|
-
*
|
|
121
|
-
* @param line - The line to search in (may be undefined if out of bounds)
|
|
122
|
-
* @param symbolName - The symbol to find
|
|
123
|
-
* @param orderHint - Which occurrence to find (0-based)
|
|
124
|
-
* @returns The character offset of the symbol, or null if not found
|
|
125
|
-
*/
|
|
126
|
-
private findSymbolInLine;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export { type EditProvider as E, type FileAccessProvider as F, type ResolverConfig as R, SymbolResolutionError as S, SymbolResolver as a };
|