@opticlm/connector 2.0.0 → 2.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 CHANGED
@@ -1,10 +1,9 @@
1
- # MCP LSP Driver SDK
1
+ # @opticlm/connector
2
2
 
3
- A TypeScript SDK that bridges Language Server Protocol (LSP) capabilities with the Model Context Protocol (MCP). Designed for IDE plugin developers building AI-assisted coding tools for VS Code, JetBrains, and other editors.
3
+ Provides an abstract interface that allows LLMs to connect to fact sources such as LSPs, code diagnostics, symbol definitions/references, links, and frontmatter; currently includes an MCP implementation.
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)
@@ -18,12 +17,6 @@ A TypeScript SDK that bridges Language Server Protocol (LSP) capabilities with t
18
17
  - [Requirements](#requirements)
19
18
  - [License](#license)
20
19
 
21
- ## Core Philosophy
22
-
23
- - **Fuzzy-to-Exact Resolution**: LLMs interact via semantic anchors (`symbolName`, `lineHint`), and the SDK resolves them to precise coordinates
24
- - **Disk-Based Truth**: All read operations reflect the state of files on disk, ignoring unsaved IDE buffers
25
- - **High Abstraction**: Beyond LSP, it also provides functionality related to something like dual chains (graph capability) and metadata (frontmatter capability).
26
-
27
20
  ## Installation
28
21
 
29
22
  ```bash
@@ -34,7 +27,9 @@ pnpm add @opticlm/connector
34
27
 
35
28
  ## Quick Start
36
29
 
37
- Providers are installed one at a time 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.
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.
38
33
 
39
34
  ```typescript
40
35
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
@@ -97,6 +92,10 @@ install(server, definition, { fileAccess })
97
92
  install(server, diagnostics, { fileAccess })
98
93
  install(server, outline, { fileAccess })
99
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
+
100
99
  // 6. Connect to transport (you control the server lifecycle)
101
100
  const transport = new StdioServerTransport()
102
101
  await server.connect(transport)
@@ -155,21 +155,25 @@ interface FrontmatterInstallOptions extends InstallOptions {
155
155
  /**
156
156
  * Install a provider's tools and resources onto an MCP server.
157
157
  *
158
+ * Accepts a single provider or an array of providers of the same type. When an array is
159
+ * passed, their results are merged: array-returning methods are concatenated, void methods
160
+ * are called on all providers in parallel, and callback registrars are registered on all.
161
+ *
158
162
  * The provider type is detected via duck-typing (checking for characteristic methods).
159
163
  * Some providers require `fileAccess` in options:
160
164
  * - `DefinitionProvider`, `ReferencesProvider`, `HierarchyProvider` need it for symbol resolution
161
165
  * - `EditProvider` needs it for file hash verification
162
166
  * - `DiagnosticsProvider`, `OutlineProvider`, `GraphProvider`, `FrontmatterProvider` use it optionally for auto-complete
163
167
  */
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;
168
+ declare function install(server: McpServer, provider: FileAccessProvider | FileAccessProvider[], options?: InstallOptions): void;
169
+ declare function install(server: McpServer, provider: EditProvider | EditProvider[], options?: EditInstallOptions): void;
170
+ declare function install(server: McpServer, provider: DefinitionProvider | DefinitionProvider[], options?: DefinitionInstallOptions): void;
171
+ declare function install(server: McpServer, provider: ReferencesProvider | ReferencesProvider[], options?: ReferencesInstallOptions): void;
172
+ declare function install(server: McpServer, provider: HierarchyProvider | HierarchyProvider[], options?: HierarchyInstallOptions): void;
173
+ declare function install(server: McpServer, provider: DiagnosticsProvider | DiagnosticsProvider[], options?: InstallOptions): void;
174
+ declare function install(server: McpServer, provider: OutlineProvider | OutlineProvider[], options?: InstallOptions): void;
175
+ declare function install(server: McpServer, provider: GlobalFindProvider | GlobalFindProvider[], options?: GlobalFindInstallOptions): void;
176
+ declare function install(server: McpServer, provider: GraphProvider | GraphProvider[], options?: GraphInstallOptions): void;
177
+ declare function install(server: McpServer, provider: FrontmatterProvider | FrontmatterProvider[], options?: FrontmatterInstallOptions): void;
174
178
 
175
179
  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,a,o){super(`Could not find symbol '${i}' at or near line ${a}. ${o}`);this.symbolName=i;this.lineHint=a;this.reason=o;this.name="SymbolResolutionError";}},w=class{constructor(e,i){this.fs=e;this.lineSearchRadius=i?.lineSearchRadius??2;}lineSearchRadius;async resolvePosition(e,i){let o=(await this.fs.readFile(e)).split(/\r?\n/),n=i.lineHint-1,t=i.orderHint??0,s=this.findSymbolInLine(o[n],i.symbolName,t);if(s!==null)return {line:n,character:s};for(let c=1;c<=this.lineSearchRadius;c++){let l=n-c;if(l>=0){let h=this.findSymbolInLine(o[l],i.symbolName,t);if(h!==null)return {line:l,character:h}}let d=n+c;if(d<o.length){let h=this.findSymbolInLine(o[d],i.symbolName,t);if(h!==null)return {line:d,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(o.length,i.lineHint+this.lineSearchRadius)}.`)}findSymbolInLine(e,i,a){if(e===void 0||e.length===0)return null;let o=0,n=0;for(;o<e.length;){let t=e.indexOf(i,o);if(t===-1)break;if(n===a)return t;n++,o=t+1;}return null}};var f=r=>({content:[{type:"text",text:JSON.stringify(r??"")}],structuredContent:r});function m(r){if(r.includes(".."))throw new Error('URI could not include ".." operator');return r.replace(/\\/g,"/")}function G(){return `edit-${Date.now()}`}function $(r){return r.length===0?"No diagnostics found.":r.map(e=>{let i=e.range.start.line+1,a=e.severity.toUpperCase(),o=e.source?` [${e.source}]`:"",n=e.code!==void 0?` (${e.code})`:"";return `- **${a}**${o}${n} at line ${i}: ${e.message}`}).join(`
2
- `)}function D(r,e=0){if(r.length===0&&e===0)return "No symbols found.";let i=" ".repeat(e);return r.map(a=>{let o=a.range.start.line+1,n=a.range.end.line+1,t=o===n?`line ${o}`:`lines ${o}-${n}`,s=a.detail?` - ${a.detail}`:"",c=`${i}- **${a.kind}** \`${a.name}\`${s} (${t})`;return a.children&&a.children.length>0?`${c}
3
- ${D(a.children,e+1)}`:c}).join(`
4
- `)}var H=new Uint16Array(256);for(let r=0;r<256;r++){let e=r<<8;for(let i=0;i<8;i++)e=e<<1^(e&32768?4129:0);H[r]=e&65535;}function I(r){let e=65535;for(let i=0;i<r.length;i++)e=e<<8&65535^H[(e>>8^r.charCodeAt(i))&255];return (e&255).toString(16).padStart(2,"0")}function C(r){return r.split(/\r?\n/).map((e,i)=>({num:i+1,text:e}))}function M(r){return r.map(e=>`${e.num}:${I(e.text)}|${e.text}`).join(`
5
- `)}function _(r){let e=r.match(/^(\d+):([0-9a-f]{2})$/);if(!e||!e[1]||!e[2])throw new Error(`Invalid hashline reference "${r}". Expected format: "<line>:<hash>" (e.g., "3:a1")`);return {line:parseInt(e[1],10),hash:e[2]}}var j=z.string().describe("The relative file path"),N=z.string().describe("The text of the symbol to find"),U=z.number().int().positive().describe("Approximate 1-based line number where the symbol is expected"),W=z.number().int().min(0).default(0).describe("0-based index of which occurrence to target if symbol appears multiple times"),E=z.object({uri:j,symbol_name:N,line_hint:U,order_hint:W}),v=z.object({uri:j,start_hash:z.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.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.string().describe("The new text to replace the entire line range with"),description:z.string().describe("Rationale for the edit")}),J=z.object({uri:z.string().describe("The file URI or path"),symbol_name:N,line_hint:U,order_hint:W,direction:z.enum(["incoming","outgoing"]).describe("Direction of the call hierarchy")}),me=z.string().describe("The search query"),he=z.boolean().default(false).describe("Whether the search is case-sensitive"),fe=z.boolean().default(false).describe("Whether to match exact words only"),ge=z.boolean().default(false).describe("Whether the query is a regular expression"),q=z.object({query:me,case_sensitive:he,exact_match:fe,regex_mode:ge}),B=z.object({path:z.string().describe("The path to the document to modify"),pattern:z.string().describe("The text pattern to find and replace with a link"),link_to:z.string().describe("The target URI the link should point to")}),Q=z.object({property:z.string().describe("The frontmatter property name to search for"),path:z.string().optional().describe("Optional path to limit the search to a specific document. If not provided, searches all documents.")}),K=z.object({path:z.string().describe("The path to the document to modify"),property:z.string().describe("The frontmatter property name to set"),value:z.union([z.string(),z.array(z.string()),z.number(),z.array(z.number()),z.boolean(),z.array(z.boolean()),z.null()]).describe("The value to set. Can be a string, number, boolean, array of these types, or null to remove.")});function ye(r){if(!r)return null;let e=r.match(/^L(\d+)(?:-L(\d+))?$/);if(!e||!e[1])return null;let i=parseInt(e[1],10),a=e[2]?parseInt(e[2],10):i;return i<1||a<i?null:{start:i,end:a}}function V(r){let e=r.indexOf("?");return e===-1?{path:r,params:new URLSearchParams}:{path:r.slice(0,e),params:new URLSearchParams(r.slice(e+1))}}function A(r){return async e=>{let i=e.lastIndexOf("/"),a=i>=0?e.slice(0,i):"",o=i>=0?e.slice(i+1):e;try{let n=await r(a||"."),t=o.toLowerCase();return n.filter(s=>s.toLowerCase().startsWith(t)).map(s=>a?`${a}/${s}`:s)}catch{return []}}}function X(r,e,i,a){r.registerTool("goto_definition",{description:"Navigate to the definition of a symbol.",inputSchema:E,outputSchema:{snippets:z.array(z.object({uri:z.string(),startLine:z.number(),endLine:z.number(),content:z.string()}))}},async o=>{a?.onInput?.(o);try{let n=m(o.uri),t={symbolName:o.symbol_name,lineHint:o.line_hint,orderHint:o.order_hint},s=await i.resolvePosition(n,t),c=(await e.provideDefinition(n,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return a?.onOutput?.({snippets:c}),f({snippets:c})}catch(n){let t=n instanceof g?n.message:`Error: ${n instanceof Error?n.message:String(n)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function Y(r,e,i,a){let o=e.provideTypeDefinition;o&&r.registerTool("goto_type_definition",{description:"Navigate to the type definition of a symbol.",inputSchema:E,outputSchema:{snippets:z.array(z.object({uri:z.string(),startLine:z.number(),endLine:z.number(),content:z.string()}))}},async n=>{a?.onInput?.(n);try{let t=m(n.uri),s={symbolName:n.symbol_name,lineHint:n.line_hint,orderHint:n.order_hint},c=await i.resolvePosition(t,s),l=(await o(t,c)).map(d=>({uri:d.uri,startLine:d.range.start.line+1,endLine:d.range.end.line+1,content:d.content}));return a?.onOutput?.({snippets:l}),f({snippets:l})}catch(t){let s=t instanceof g?t.message:`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:s}],structuredContent:{error:s},isError:true}}});}function Z(r,e,i,a){r.registerTool("find_references",{description:"Find all references to a symbol. Returns a list of locations where the symbol is used.",inputSchema:E,outputSchema:{snippets:z.array(z.object({uri:z.string(),startLine:z.number(),endLine:z.number(),content:z.string()}))}},async o=>{a?.onInput?.(o);try{let n=m(o.uri),t={symbolName:o.symbol_name,lineHint:o.line_hint,orderHint:o.order_hint},s=await i.resolvePosition(n,t),c=(await e.provideReferences(n,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return a?.onOutput?.({snippets:c}),f({snippets:c})}catch(n){let t=n instanceof g?n.message:`Error: ${n instanceof Error?n.message:String(n)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function ee(r,e,i,a){r.registerTool("call_hierarchy",{description:"Get call hierarchy for a function or method. Shows incoming or outgoing calls.",inputSchema:J,outputSchema:{snippets:z.array(z.object({uri:z.string(),startLine:z.number(),endLine:z.number(),content:z.string()}))}},async o=>{a?.onInput?.(o);try{let n=m(o.uri),t={symbolName:o.symbol_name,lineHint:o.line_hint,orderHint:o.order_hint},s=await i.resolvePosition(n,t),c=(await e.provideCallHierarchy(n,s,o.direction)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return a?.onOutput?.({snippets:c}),f({snippets:c})}catch(n){let t=n instanceof g?n.message:`Error: ${n instanceof Error?n.message:String(n)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function te(r,e,i){let a=new ResourceTemplate("diagnostics://{+path}",{list:void 0,complete:i?{path:i}:void 0});if(r.registerResource("diagnostics",a,{description:"Diagnostics (errors, warnings, hints) for a specific file. Use the file path after diagnostics://",mimeType:"text/markdown"},async(o,n)=>{try{let t=n.path,s=m(t),c=await e.provideDiagnostics(s),l=$(c);return {contents:[{uri:`diagnostics://${t}`,mimeType:"text/markdown",text:l}]}}catch(t){let s=`Error: ${t instanceof Error?t.message:String(t)}`;return {contents:[{uri:`diagnostics://${n.path}`,mimeType:"text/markdown",text:s}]}}}),e.getWorkspaceDiagnostics){let o=e.getWorkspaceDiagnostics.bind(e);r.registerResource("workspace-diagnostics","diagnostics://workspace",{description:"All diagnostics (errors, warnings, hints) across the entire workspace",mimeType:"text/markdown"},async()=>{try{let n=await o(),t=new Map;for(let c of n){let l=t.get(c.uri)??[];l.push(c),t.set(c.uri,l);}if(t.size===0)return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:"No diagnostics found in workspace."}]};let s=[];for(let[c,l]of t)s.push(`## ${c}
6
- ${$(l)}`);return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:s.join(`
1
+ import {ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {z as z$1}from'zod';var g=class extends Error{constructor(i,e,t){super(`Could not find symbol '${i}' at or near line ${e}. ${t}`);this.symbolName=i;this.lineHint=e;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/),r=i.lineHint-1,o=i.orderHint??0,a=this.findSymbolInLine(t[r],i.symbolName,o);if(a!==null)return {line:r,character:a};for(let c=1;c<=this.lineSearchRadius;c++){let l=r-c;if(l>=0){let f=this.findSymbolInLine(t[l],i.symbolName,o);if(f!==null)return {line:l,character:f}}let d=r+c;if(d<t.length){let f=this.findSymbolInLine(t[d],i.symbolName,o);if(f!==null)return {line:d,character:f}}}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,e){if(n===void 0||n.length===0)return null;let t=0,r=0;for(;t<n.length;){let o=n.indexOf(i,t);if(o===-1)break;if(r===e)return o;r++,t=o+1;}return null}};var h=s=>({content:[{type:"text",text:JSON.stringify(s??"")}],structuredContent:s});function m(s){if(s.includes(".."))throw new Error('URI could not include ".." operator');return s.replace(/\\/g,"/")}function z(){return `edit-${Date.now()}`}function L(s){return s.length===0?"No diagnostics found.":s.map(n=>{let i=n.range.start.line+1,e=n.severity.toUpperCase(),t=n.source?` [${n.source}]`:"",r=n.code!==void 0?` (${n.code})`:"";return `- **${e}**${t}${r} at line ${i}: ${n.message}`}).join(`
2
+ `)}function $(s,n=0){if(s.length===0&&n===0)return "No symbols found.";let i=" ".repeat(n);return s.map(e=>{let t=e.range.start.line+1,r=e.range.end.line+1,o=t===r?`line ${t}`:`lines ${t}-${r}`,a=e.detail?` - ${e.detail}`:"",c=`${i}- **${e.kind}** \`${e.name}\`${a} (${o})`;return e.children&&e.children.length>0?`${c}
3
+ ${$(e.children,n+1)}`:c}).join(`
4
+ `)}var C=new Uint16Array(256);for(let s=0;s<256;s++){let n=s<<8;for(let i=0;i<8;i++)n=n<<1^(n&32768?4129:0);C[s]=n&65535;}function k(s){let n=65535;for(let i=0;i<s.length;i++)n=n<<8&65535^C[(n>>8^s.charCodeAt(i))&255];return (n&255).toString(16).padStart(2,"0")}function H(s){return s.split(/\r?\n/).map((n,i)=>({num:i+1,text:n}))}function M(s){return s.map(n=>`${n.num}:${k(n.text)}|${n.text}`).join(`
5
+ `)}function A(s){let n=s.match(/^(\d+):([0-9a-f]{2})$/);if(!n||!n[1]||!n[2])throw new Error(`Invalid hashline reference "${s}". Expected format: "<line>:<hash>" (e.g., "3:a1")`);return {line:parseInt(n[1],10),hash:n[2]}}var j=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"),E=z$1.object({uri:j,symbol_name:N,line_hint:U,order_hint:W}),v=z$1.object({uri:j,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")}),me=z$1.string().describe("The search query"),fe=z$1.boolean().default(false).describe("Whether the search is case-sensitive"),he=z$1.boolean().default(false).describe("Whether to match exact words only"),ge=z$1.boolean().default(false).describe("Whether the query is a regular expression"),q=z$1.object({query:me,case_sensitive:fe,exact_match:he,regex_mode:ge}),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({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.")}),K=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 ye(s){if(!s)return null;let n=s.match(/^L(\d+)(?:-L(\d+))?$/);if(!n||!n[1])return null;let i=parseInt(n[1],10),e=n[2]?parseInt(n[2],10):i;return i<1||e<i?null:{start:i,end:e}}function V(s){let n=s.indexOf("?");return n===-1?{path:s,params:new URLSearchParams}:{path:s.slice(0,n),params:new URLSearchParams(s.slice(n+1))}}function _(s){return async n=>{let i=n.lastIndexOf("/"),e=i>=0?n.slice(0,i):"",t=i>=0?n.slice(i+1):n;try{let r=await s(e||"."),o=t.toLowerCase();return r.filter(a=>a.toLowerCase().startsWith(o)).map(a=>e?`${e}/${a}`:a)}catch{return []}}}function X(s,n,i,e){s.registerTool("goto_definition",{description:"Navigate to the definition of a symbol.",inputSchema:E,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=>{e?.onInput?.(t);try{let r=m(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},a=await i.resolvePosition(r,o),c=(await n.provideDefinition(r,a)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return e?.onOutput?.({snippets:c}),h({snippets:c})}catch(r){let o=r instanceof g?r.message:`Error: ${r instanceof Error?r.message:String(r)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function Y(s,n,i,e){let t=n.provideTypeDefinition;t&&s.registerTool("goto_type_definition",{description:"Navigate to the type definition of a symbol.",inputSchema:E,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 r=>{e?.onInput?.(r);try{let o=m(r.uri),a={symbolName:r.symbol_name,lineHint:r.line_hint,orderHint:r.order_hint},c=await i.resolvePosition(o,a),l=(await t(o,c)).map(d=>({uri:d.uri,startLine:d.range.start.line+1,endLine:d.range.end.line+1,content:d.content}));return e?.onOutput?.({snippets:l}),h({snippets:l})}catch(o){let a=o instanceof g?o.message:`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:a}],structuredContent:{error:a},isError:true}}});}function Z(s,n,i,e){s.registerTool("find_references",{description:"Find all references to a symbol. Returns a list of locations where the symbol is used.",inputSchema:E,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=>{e?.onInput?.(t);try{let r=m(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},a=await i.resolvePosition(r,o),c=(await n.provideReferences(r,a)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return e?.onOutput?.({snippets:c}),h({snippets:c})}catch(r){let o=r instanceof g?r.message:`Error: ${r instanceof Error?r.message:String(r)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function ee(s,n,i,e){s.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=>{e?.onInput?.(t);try{let r=m(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},a=await i.resolvePosition(r,o),c=(await n.provideCallHierarchy(r,a,t.direction)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return e?.onOutput?.({snippets:c}),h({snippets:c})}catch(r){let o=r instanceof g?r.message:`Error: ${r instanceof Error?r.message:String(r)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function te(s,n,i){let e=new ResourceTemplate("diagnostics://{+path}",{list:void 0,complete:i?{path:i}:void 0});if(s.registerResource("diagnostics",e,{description:"Diagnostics (errors, warnings, hints) for a specific file. Use the file path after diagnostics://",mimeType:"text/markdown"},async(t,r)=>{try{let o=r.path,a=m(o),c=await n.provideDiagnostics(a),l=L(c);return {contents:[{uri:`diagnostics://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`diagnostics://${r.path}`,mimeType:"text/markdown",text:a}]}}}),n.getWorkspaceDiagnostics){let t=n.getWorkspaceDiagnostics.bind(n);s.registerResource("workspace-diagnostics","diagnostics://workspace",{description:"All diagnostics (errors, warnings, hints) across the entire workspace",mimeType:"text/markdown"},async()=>{try{let r=await t(),o=new Map;for(let c of r){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 a=[];for(let[c,l]of o)a.push(`## ${c}
6
+ ${L(l)}`);return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:a.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}}
8
+ `)}]}}catch(r){return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:`Error: ${r instanceof Error?r.message:String(r)}`}]}}});}n.onDiagnosticsChanged&&n.onDiagnosticsChanged(t=>{let r=m(t);s.server.sendResourceUpdated({uri:`diagnostics://${r}`}),n.getWorkspaceDiagnostics&&s.server.sendResourceUpdated({uri:"diagnostics://workspace"});});}function re(s,n,i){let e=new ResourceTemplate("outline://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("outline",e,{description:"Document outline (symbols like classes, functions, variables) for a specific file. Use the file path after outline://",mimeType:"text/markdown"},async(t,r)=>{try{let o=r.path,a=m(o),c=await n.provideDocumentSymbols(a),l=$(c);return {contents:[{uri:`outline://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`outline://${r.path}`,mimeType:"text/markdown",text:a}]}}});}function ne(s,n,i,e){let t=n.previewAndApplyEdits?.bind(n)??n.applyEdits?.bind(n);t&&s.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 r=>{e?.onInput?.(r);try{let o=m(r.uri),a=A(r.start_hash),c=r.end_hash?A(r.end_hash):a,d=(await i(o)).split(/\r?\n/);if(a.line<1||a.line>d.length)throw new Error(`Start line ${a.line} is out of range (file has ${d.length} lines)`);if(c.line<a.line||c.line>d.length)throw new Error(`End line ${c.line} is out of range (file has ${d.length} lines)`);let f=d[a.line-1],x=k(f);if(x!==a.hash)throw new Error(`Hash mismatch at line ${a.line}: expected "${a.hash}", got "${x}". File has changed since last read.`);let P=d[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 I={start:{line:a.line-1,character:0},end:{line:c.line-1,character:P.length}},T={id:z(),uri:o,edits:[{range:I,newText:r.replace_text}],description:r.description},y=await t(T)?{success:!0,message:"Edit successfully applied and saved."}:{success:!1,message:"Edit rejected by user."};return e?.onOutput?.(y),h(y)}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:a}],structuredContent:{success:false,message:a},isError:true}}});}function ie(s,n){let{readFile:i,readDirectory:e}=n,t=new ResourceTemplate("files://{+path}",{list:void 0,complete:{path:_(e)}});s.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(r,o)=>{let a=r.toString();try{let c=o.path,l,d=c,f=c.indexOf("#");f!==-1&&(l=c.slice(f+1),d=c.slice(0,f));let{path:x,params:P}=V(d),{path:F,params:I}=V(l??"");l=l?F:void 0;let T=new URLSearchParams([...P.entries(),...I.entries()]),R=m(x),y=ye(l),G=T.get("pattern");try{let D=await i(R),b=H(D);if(y&&(b=b.filter(w=>w.num>=y.start&&w.num<=y.end)),G){let w=new RegExp(G);b=b.filter(de=>w.test(de.text));}return {contents:[{uri:a,mimeType:"text/plain",text:M(b)}]}}catch{let D=await e(R);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(D)}]}}}catch(c){let l=`Error: ${c instanceof Error?c.message:String(c)}`;return {contents:[{uri:a,mimeType:"text/plain",text:l}]}}}),n.onFileChanged&&n.onFileChanged(r=>{let o=m(r);s.server.sendResourceUpdated({uri:`files://${o}`});});}function oe(s,n,i){s.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 e=>{i?.onInput?.(e);try{let t=e.case_sensitive??!1,r=e.exact_match??!1,o=e.regex_mode??!1,a=await n.globalFind(e.query,{caseSensitive:t,exactMatch:r,regexMode:o});return i?.onOutput?.({count:a.length,matches:a}),h({count:a.length,matches:a})}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{error:r},isError:true}}});}function se(s,n,i){let e=new ResourceTemplate("outlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("outlinks",e,{description:"Outgoing links from a specific file. Use the file path after outlinks://",mimeType:"application/json"},async(r,o)=>{try{let a=o.path,c=m(a),l=await n.resolveOutlinks(c);return {contents:[{uri:`outlinks://${a}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(a){let c=`Error: ${a instanceof Error?a.message:String(a)}`;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});s.registerResource("backlinks",t,{description:"Incoming links (backlinks) to a specific file. Use the file path after backlinks://",mimeType:"application/json"},async(r,o)=>{try{let a=o.path,c=m(a),l=await n.resolveBacklinks(c);return {contents:[{uri:`backlinks://${a}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(a){let c=`Error: ${a instanceof Error?a.message:String(a)}`;return {contents:[{uri:`backlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});}function ae(s,n,i){s.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 e=await n.getLinkStructure();return i?.onOutput?.({links:e}),h({links:e})}catch(e){let t=`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function ce(s,n,i){s.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 e=>{i?.onInput?.(e);try{let t=m(e.path),r=m(e.link_to);await n.addLink(t,e.pattern,r);let o={success:!0,message:"Link added successfully."};return i?.onOutput?.(o),h(o)}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{success:false,message:r},isError:true}}});}function ue(s,n,i){s.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$1.array(z$1.object({path:z$1.string(),value:z$1.unknown()}))}},async e=>{i?.onInput?.(e);try{let t=e.path?m(e.path):void 0,r=await n.getFrontmatterStructure(e.property,t);return i?.onOutput?.({matches:r}),h({matches:r})}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{error:r},isError:true}}});}function le(s,n,i){s.registerTool("set_frontmatter",{description:"Set a frontmatter property on a document. Use null to remove the property.",inputSchema:K,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async e=>{i?.onInput?.(e);try{let t=m(e.path),r=e.value===null?void 0:e.value;await n.setFrontmatter(t,e.property,r);let o={success:!0,message:"Frontmatter updated successfully."};return i?.onOutput?.(o),h(o)}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{success:false,message:r},isError:true}}});}function pe(s,n,i){let e=new ResourceTemplate("frontmatter://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("frontmatter",e,{description:"Frontmatter metadata for a specific file. Use the file path after frontmatter://",mimeType:"application/json"},async(t,r)=>{try{let o=r.path,a=m(o),c=await n.getFrontmatter(a);return {contents:[{uri:`frontmatter://${o}`,mimeType:"application/json",text:JSON.stringify(c,null,2)}]}}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`frontmatter://${r.path}`,mimeType:"application/json",text:JSON.stringify({error:a})}]}}});}function ve(s){if(s.length<=0)throw new Error("providers array must not be empty");if(s.length===1&&s[0])return s[0];let n=s[0];if(!n)throw new Error("The first item of providers array must be defined");if("readFile"in n&&"readDirectory"in n){let i=s,e={readFile:t=>n.readFile(t),readDirectory:t=>n.readDirectory(t)};return i.some(t=>t.onFileChanged)&&(e.onFileChanged=t=>{for(let r of i)r.onFileChanged?.(t);}),e}if("applyEdits"in n||"previewAndApplyEdits"in n)return n;if("provideDefinition"in n){let i=s,e={async provideDefinition(t,r){return (await Promise.all(i.map(a=>a.provideDefinition(t,r)))).flat()}};return i.some(t=>t.provideTypeDefinition)&&(e.provideTypeDefinition=async(t,r)=>{let o=i.filter(c=>c.provideTypeDefinition);return (await Promise.all(o.map(c=>c.provideTypeDefinition(t,r)))).flat()}),e}if("provideReferences"in n){let i=s;return {async provideReferences(e,t){return (await Promise.all(i.map(o=>o.provideReferences(e,t)))).flat()}}}if("provideCallHierarchy"in n){let i=s;return {async provideCallHierarchy(e,t,r){return (await Promise.all(i.map(a=>a.provideCallHierarchy(e,t,r)))).flat()}}}if("provideDiagnostics"in n){let i=s,e={async provideDiagnostics(t){return (await Promise.all(i.map(o=>o.provideDiagnostics(t)))).flat()}};return i.some(t=>t.getWorkspaceDiagnostics)&&(e.getWorkspaceDiagnostics=async()=>{let t=i.filter(o=>o.getWorkspaceDiagnostics);return (await Promise.all(t.map(o=>o.getWorkspaceDiagnostics()))).flat()}),i.some(t=>t.onDiagnosticsChanged)&&(e.onDiagnosticsChanged=t=>{for(let r of i)r.onDiagnosticsChanged?.(t);}),e}if("provideDocumentSymbols"in n){let i=s;return {async provideDocumentSymbols(e){return (await Promise.all(i.map(r=>r.provideDocumentSymbols(e)))).flat()}}}if("globalFind"in n){let i=s;return {async globalFind(e,t){return (await Promise.all(i.map(o=>o.globalFind(e,t)))).flat()}}}if("getLinkStructure"in n){let i=s;return {async getLinkStructure(){return (await Promise.all(i.map(t=>t.getLinkStructure()))).flat()},async resolveOutlinks(e){return (await Promise.all(i.map(r=>r.resolveOutlinks(e)))).flat()},async resolveBacklinks(e){return (await Promise.all(i.map(r=>r.resolveBacklinks(e)))).flat()},async addLink(e,t,r){await Promise.all(i.map(o=>o.addLink(e,t,r)));}}}if("getFrontmatterStructure"in n){let i=s;return {async getFrontmatterStructure(e,t){return (await Promise.all(i.map(o=>o.getFrontmatterStructure(e,t)))).flat()},async getFrontmatter(e){let t=await Promise.all(i.map(r=>r.getFrontmatter(e)));return Object.assign({},...t)},async setFrontmatter(e,t,r){await Promise.all(i.map(o=>o.setFrontmatter(e,t,r)));}}}return n}function $e(s,n,i){let e=Array.isArray(n)?ve(n):n,t=i?.fileAccess,r=t?_(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 e&&"readDirectory"in e){ie(s,e);return}if("applyEdits"in e||"previewAndApplyEdits"in e){if(!t)throw new Error("fileAccess is required in options when installing an EditProvider");let a=i;ne(s,e,t.readFile,{onInput:a?.onEditInput,onOutput:a?.onEditOutput});return}if("provideDefinition"in e){let a=e,c=i,l=o();X(s,a,l,{onInput:c?.onDefinitionInput,onOutput:c?.onDefinitionOutput}),a.provideTypeDefinition&&Y(s,a,l,{onInput:c?.onTypeDefinitionInput,onOutput:c?.onTypeDefinitionOutput});return}if("provideReferences"in e){let a=i;Z(s,e,o(),{onInput:a?.onReferencesInput,onOutput:a?.onReferencesOutput});return}if("provideCallHierarchy"in e){let a=i;ee(s,e,o(),{onInput:a?.onCallHierarchyInput,onOutput:a?.onCallHierarchyOutput});return}if("provideDiagnostics"in e){te(s,e,r);return}if("provideDocumentSymbols"in e){re(s,e,r);return}if("globalFind"in e){let a=i;oe(s,e,{onInput:a?.onGlobalFindInput,onOutput:a?.onGlobalFindOutput});return}if("getLinkStructure"in e){let a=e,c=i;ae(s,a,{onOutput:c?.onLinkStructureOutput}),ce(s,a,{onInput:c?.onAddLinkInput,onOutput:c?.onAddLinkOutput}),se(s,a,r);return}if("getFrontmatterStructure"in e){let a=e,c=i;ue(s,a,{onInput:c?.onFrontmatterStructureInput,onOutput:c?.onFrontmatterStructureOutput}),le(s,a,{onInput:c?.onSetFrontmatterInput,onOutput:c?.onSetFrontmatterOutput}),pe(s,a,r);return}}
9
9
  export{$e as install};
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@opticlm/connector",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
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",
4
5
  "type": "module",
5
6
  "types": "dist/index.d.ts",
6
7
  "exports": {
@@ -17,18 +18,19 @@
17
18
  "default": "./dist/mcp/index.js"
18
19
  }
19
20
  },
20
- "files": ["dist/**/*.js", "dist/**/*.d.ts", "LICENSE"],
21
- "scripts": {
22
- "build": "tsup",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "test:coverage": "vitest run --coverage",
26
- "lint": "biome check .",
27
- "lint:fix": "biome check --write .",
28
- "format": "biome format --write .",
29
- "prepublishOnly": "npm run build"
30
- },
31
- "keywords": ["mcp", "lsp", "language-server", "ide", "vscode", "jetbrains"],
21
+ "files": [
22
+ "dist/**/*.js",
23
+ "dist/**/*.d.ts",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "mcp",
28
+ "lsp",
29
+ "language-server",
30
+ "ide",
31
+ "vscode",
32
+ "jetbrains"
33
+ ],
32
34
  "author": "EFLKumo",
33
35
  "license": "MIT",
34
36
  "repository": {
@@ -54,5 +56,14 @@
54
56
  },
55
57
  "engines": {
56
58
  "node": ">=18.0.0"
59
+ },
60
+ "scripts": {
61
+ "build": "tsup",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "test:coverage": "vitest run --coverage",
65
+ "lint": "biome check .",
66
+ "lint:fix": "biome check --write .",
67
+ "format": "biome format --write ."
57
68
  }
58
- }
69
+ }