{"id":47908468,"url":"https://github.com/opticlm/connector","last_synced_at":"2026-04-08T10:01:37.844Z","repository":{"id":331429464,"uuid":"1123971562","full_name":"OpticLM/connector","owner":"OpticLM","description":"A TypeScript SDK bridges IDE's LSP capabilities with MCP, designed for who building AI coding agents.","archived":false,"fork":false,"pushed_at":"2026-04-03T13:39:10.000Z","size":361,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T17:52:49.734Z","etag":null,"topics":["lsp","mcp","mcp-server","modelcontextprotocol","npm","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpticLM.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-28T03:17:57.000Z","updated_at":"2026-04-03T13:30:33.000Z","dependencies_parsed_at":"2026-01-09T05:11:51.973Z","dependency_job_id":null,"html_url":"https://github.com/OpticLM/connector","commit_stats":null,"previous_names":["opticlm/mcp-lspdriver-ts","opticlm/connector"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/OpticLM/connector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpticLM%2Fconnector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpticLM%2Fconnector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpticLM%2Fconnector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpticLM%2Fconnector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpticLM","download_url":"https://codeload.github.com/OpticLM/connector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpticLM%2Fconnector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31388169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T04:26:24.776Z","status":"ssl_error","status_checked_at":"2026-04-04T04:23:34.147Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lsp","mcp","mcp-server","modelcontextprotocol","npm","typescript"],"created_at":"2026-04-04T05:01:19.300Z","updated_at":"2026-04-04T05:01:22.458Z","avatar_url":"https://github.com/OpticLM.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @opticlm/connector\n\nProvides 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.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [MCP Tools](#mcp-tools)\n- [Tool Callbacks](#tool-callbacks)\n- [MCP Resources](#mcp-resources)\n- [Auto-Complete for File Paths](#auto-complete-for-file-paths)\n- [Subscription and Change Notifications](#subscription-and-change-notifications)\n- [Symbol Resolution](#symbol-resolution)\n- [Pipe IPC (Out-of-Process)](#pipe-ipc-out-of-process)\n- [LSP Client (Built-in)](#lsp-client-built-in)\n- [Requirements](#requirements)\n- [License](#license)\n\n## Installation\n\n```bash\nnpm install @opticlm/connector\n# or\npnpm add @opticlm/connector\n```\n\n## Quick Start\n\nProviders 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.\n\nYou 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.\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { install } from '@opticlm/connector/mcp'\nimport * as fs from 'fs/promises'\n\n// 1. Create your MCP server\nconst server = new McpServer({\n  name: 'my-ide-mcp-server',\n  version: '1.0.0'\n})\n\n// 2. Implement File Access\nconst fileAccess = {\n  readFile: async (uri: string) =\u003e {\n    return await fs.readFile(uri, 'utf-8')\n  },\n  readDirectory: (uri: string) =\u003e yourIDE.workspace.readDirectory(uri),\n}\n\n// 3. Implement Edit Provider\nconst edit = {\n  // Show diff in your IDE and get user approval\n  previewAndApplyEdits: async (operation) =\u003e {\n    return await showDiffDialog(operation)\n  },\n}\n\n// 4. Implement LSP Capability Providers\nconst definition = {\n  provideDefinition: async (uri, position) =\u003e {\n    return await lspClient.getDefinition(uri, position)\n  },\n}\n\nconst diagnostics = {\n  provideDiagnostics: async (uri) =\u003e {\n    return await lspClient.getDiagnostics(uri)\n  },\n  getWorkspaceDiagnostics: async () =\u003e {\n    return await lspClient.getWorkspaceDiagnostics()\n  },\n  onDiagnosticsChanged: (callback) =\u003e {\n    yourIDE.onDiagnosticsChanged((uri) =\u003e callback(uri))\n  },\n}\n\nconst outline = {\n  provideDocumentSymbols: async (uri) =\u003e {\n    return await lspClient.getDocumentSymbols(uri)\n  },\n}\n\n// 5. Install providers onto the server\n//    fileAccess is installed first; others receive it as an option when needed\ninstall(server, fileAccess)\ninstall(server, edit, { fileAccess })\ninstall(server, definition, { fileAccess })\ninstall(server, diagnostics, { fileAccess })\ninstall(server, outline, { fileAccess })\n\n// You can also pass an array to merge multiple providers of the same type:\n// install(server, [definition, anotherDefinition], { fileAccess })\n// install(server, [diagnostics, anotherDiagnostics], { fileAccess })\n\n// 6. Connect to transport (you control the server lifecycle)\nconst transport = new StdioServerTransport()\nawait server.connect(transport)\n```\n\nEach `install()` call is independent — only install the providers your IDE actually supports. The `fileAccess` option is required for providers that read files (edit, definition, references, hierarchy) and is used optionally by others for path auto-complete.\n\n## MCP Tools\n\nThe SDK automatically registers tools based on which providers you install:\n\n### `goto_definition`\n\nNavigate to the definition of a symbol.\n\n### `find_references`\n\nFind all references to a symbol.\n\n### `find_file_references`\n\nFind all references to a file across the workspace (e.g., all files that import or link to the given file).\n\nOnly registered when your `ReferencesProvider` implements the optional `provideFileReferences` method.\n\n### `call_hierarchy`\n\nGet call hierarchy for a function or method.\n\n### `apply_edit`\n\nApply a text edit to a file using hashline references (requires user approval).\n\nThe `files://` resource returns file content in **hashline format** — each line is prefixed with `\u003cline\u003e:\u003chash\u003e|`, where the hash is a 2-char CRC16 digest of the line's content. To edit a file, reference lines by these hashes. If the file has changed since the last read, the hashes won't match and the edit is rejected, preventing stale overwrites.\n\n### `global_find`\n\nSearch for text across the entire workspace.\n\n### `get_link_structure`\n\nGet all links in the workspace, showing relationships between documents.\n\n### `add_link`\n\nAdd a link to a document by finding a text pattern and replacing it with a link.\n\n### `get_frontmatter_structure`\n\nGet frontmatter property values across documents.\n\n### `set_frontmatter`\n\nSet a frontmatter property on a document.\n\n## Tool Callbacks\n\nEach provider with tools accepts optional `onInput` and `onOutput` callbacks in its install options. These fire synchronously around each tool invocation — `onInput` before processing, `onOutput` after a successful result (not on errors).\n\nUse them for logging, telemetry, or testing:\n\n```typescript\nimport { install } from '@opticlm/connector/mcp'\n\n// EditProvider — apply_edit\ninstall(server, editProvider, {\n  fileAccess,\n  onEditInput: (input) =\u003e {\n    console.log('edit requested:', input.uri, input.description)\n  },\n  onEditOutput: (output) =\u003e {\n    console.log('edit result:', output.success, output.message)\n  },\n})\n\n// DefinitionProvider — goto_definition + goto_type_definition\ninstall(server, definitionProvider, {\n  fileAccess,\n  onDefinitionInput: (input) =\u003e log('goto_definition', input),\n  onDefinitionOutput: (output) =\u003e log('goto_definition result', output.snippets.length),\n  onTypeDefinitionInput: (input) =\u003e log('goto_type_definition', input),\n  onTypeDefinitionOutput: (output) =\u003e log('goto_type_definition result', output.snippets.length),\n})\n```\n\n## MCP Resources\n\nThe SDK automatically registers resources based on which providers you install:\n\n### `diagnostics://{path}`\n\nGet diagnostics (errors, warnings) for a specific file.\n\n**Resource URI Pattern:** `diagnostics://{+path}`\n\n**Example:** `diagnostics://src/main.ts`\n\nReturns diagnostics formatted as markdown with location, severity, and message information.\n\n**Subscription Support:** If your `DiagnosticsProvider` implements `onDiagnosticsChanged`, these resources become subscribable. When diagnostics change, the driver sends resource update notifications.\n\n### `diagnostics://workspace`\n\nGet diagnostics across the entire workspace.\n\n**Resource URI:** `diagnostics://workspace`\n\nOnly available if your `DiagnosticsProvider` implements the optional `getWorkspaceDiagnostics()` method.\n\nReturns workspace diagnostics grouped by file, formatted as markdown.\n\n**Subscription Support:** If your `DiagnosticsProvider` implements `onDiagnosticsChanged`, this resource becomes subscribable.\n\n### `outline://{path}`\n\nGet the document outline (symbol tree) for a file.\n\n**Resource URI Pattern:** `outline://{+path}`\n\n**Example:** `outline://src/components/Button.tsx`\n\nReturns document symbols formatted as a hierarchical markdown outline, including:\n- Symbol names and kinds (class, function, method, etc.)\n- Source locations\n- Nested children (e.g., methods within classes)\n\nNo subscription support for this resource (read-only).\n\n### `files://{path}`\n\nFor directories: returns directory children (git-ignored files excluded, similar to `ls`). For files: returns content in **hashline format** with optional line range and regex filtering.\n\n**Hashline format:** Each line is prefixed with `\u003cline\u003e:\u003chash\u003e|`, where `\u003cline\u003e` is the 1-based line number and `\u003chash\u003e` is a 2-char CRC16 hex digest of the line content. For example:\n\n```\n1:a3|function hello() {\n2:f1|  return \"world\"\n3:0e|}\n```\n\nThese hashes serve as content-addressed anchors for the `apply_edit` tool — if the file changes between read and edit, the hash mismatch is detected and the edit is safely rejected.\n\n**Resource URI Pattern:** `files://{+path}`\n\n**Example:** `files://src`, `files://src/index.ts`, `files://src/index.ts#L1-L2`, `files://src/index.ts?pattern=^import`, `files://src/index.ts?pattern=TODO#L10-L50`\n\nNo subscription support for this resource (read-only).\n\n### `outlinks://{path}`\n\nGet outgoing links from a specific file.\n\n**Resource URI Pattern:** `outlinks://{+path}`\n\n**Example:** `outlinks://notes/index.md`\n\nReturns a JSON array of links originating from the specified document.\n\nNo subscription support for this resource (read-only).\n\n### `backlinks://{path}`\n\nGet incoming links (backlinks) to a specific file.\n\n**Resource URI Pattern:** `backlinks://{+path}`\n\n**Example:** `backlinks://notes/topic-a.md`\n\nReturns a JSON array of links pointing to the specified document.\n\nNo subscription support for this resource (read-only).\n\n### `frontmatter://{path}`\n\nGet frontmatter metadata for a specific file.\n\n**Resource URI Pattern:** `frontmatter://{+path}`\n\n**Example:** `frontmatter://notes/index.md`\n\nReturns a JSON object containing all frontmatter properties and values for the document.\n\nNo subscription support for this resource (read-only).\n\n## Auto-Complete for File Paths\n\nAll resource templates with a `{+path}` variable (`files://`, `diagnostics://`, `outline://`, `outlinks://`, `backlinks://`, `frontmatter://`) support MCP auto-completion. When an MCP client calls `completion/complete` with a partial file path, the SDK uses `readDirectory` from your `FileAccessProvider` to suggest matching entries.\n\n- Completion is case-insensitive and splits input into a directory and prefix (e.g., `src/ser` reads `src/` and filters by `ser`)\n- If `readDirectory` fails (e.g., the directory doesn't exist), an empty list is returned\n- Results are capped at 100 items by the MCP SDK\n\nThis works automatically — no additional configuration is needed.\n\n## Subscription and Change Notifications\n\nProviders can implement optional `onDiagnosticsChanged` and `onFileChanged` callbacks to make resources subscribable:\n\n```typescript\nimport { install } from '@opticlm/connector/mcp'\n\ninstall(server, {\n  readFile: async (uri) =\u003e { /* ... */ },\n  readDirectory: async (path) =\u003e { /* ... */ },\n  onFileChanged: (callback) =\u003e {\n    yourIDE.onFileChanged((uri) =\u003e callback(uri))\n  },\n})\n\ninstall(server, {\n  provideDiagnostics: async (uri) =\u003e { /* ... */ },\n  getWorkspaceDiagnostics: async () =\u003e { /* ... */ },\n  onDiagnosticsChanged: (callback) =\u003e {\n    yourIDE.onDiagnosticsChanged((uri) =\u003e callback(uri))\n  },\n})\n```\n\nWhen diagnostics or files change, call the registered callback with the affected file URI. The driver will send MCP resource update notifications to subscribers.\n\n## Symbol Resolution\n\nThe SDK uses a robust algorithm to handle imprecise LLM positioning:\n\n1. Target the `lineHint` (converting 1-based to 0-based)\n2. Search for `symbolName` in that line\n3. **Robustness Fallback**: If not found, scan +/- 2 lines (configurable)\n4. Use `orderHint` to select the Nth occurrence if needed\n\nConfigure the search radius via the `resolverConfig` option:\n\n```typescript\ninstall(server, definitionProvider, {\n  fileAccess,\n  resolverConfig: {\n    lineSearchRadius: 5,  // Default: 2\n  },\n})\n```\n\n## Pipe IPC (Out-of-Process)\n\nWhen the MCP server runs in a separate process from the IDE plugin (e.g., spawned via stdio transport), the Pipe IPC layer lets the two communicate over a named pipe.\n\n**IDE plugin side** — expose providers:\n\n```typescript\nimport { servePipe } from '@opticlm/connector/pipe'\n\nconst server = await servePipe({\n  pipeName: 'my-ide-lsp',\n  fileAccess: { /* ... */ },\n  definition: { /* ... */ },\n  // ...\n  // Add only the providers your IDE supports\n})\n// server.pipePath        — the resolved pipe path\n// server.connectionCount — number of connected clients\n// await server.close()   — shut down\n```\n\n**MCP server side** — connect and install proxy providers:\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { connectPipe } from '@opticlm/connector/pipe'\nimport { install } from '@opticlm/connector/mcp'\n\nconst conn = await connectPipe({\n  pipeName: 'my-ide-lsp',\n  connectTimeout: 5000,  // optional, default 5000ms\n})\n\n// conn exposes proxy providers as named fields:\n// conn.fileAccess, conn.definition, conn.diagnostics, etc.\n// conn.availableMethods lists all methods the server exposes\n\nconst mcpServer = new McpServer({ name: 'my-mcp', version: '1.0.0' })\n\nif (conn.fileAccess) install(mcpServer, conn.fileAccess)\nif (conn.definition \u0026\u0026 conn.fileAccess)\n  install(mcpServer, conn.definition, { fileAccess: conn.fileAccess })\n// ...\n// Install whichever proxy providers are available\n\n// When done:\nconn.disconnect()\n```\n\nThe handshake automatically discovers which providers the server exposes and builds typed proxies. Change notifications are forwarded as push notifications to all connected clients. Multiple clients can connect to the same pipe simultaneously.\n\n## LSP Client (Built-in)\n\nFor standalone MCP servers that need to communicate directly with an LSP server (e.g., when not running inside an IDE plugin), `LspClient` spawns a language server process and automatically creates capability providers based on the server's reported capabilities.\n\n```typescript\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createLspClient } from '@opticlm/connector'\nimport { install } from '@opticlm/connector/mcp'\nimport * as fs from 'fs/promises'\n\n// 1. Create and start the LSP client\nconst lsp = createLspClient({\n  command: 'typescript-language-server',\n  args: ['--stdio'],\n  workspacePath: '/path/to/project',\n  readFile: (path) =\u003e fs.readFile(path, 'utf-8'),\n})\n\nawait lsp.start()\n\n// 2. Wire providers into MCP\nconst server = new McpServer({ name: 'my-mcp', version: '1.0.0' })\n\nconst fileAccess = {\n  readFile: (uri: string) =\u003e fs.readFile(uri, 'utf-8'),\n  readDirectory: async () =\u003e [],\n}\n\n// Providers are automatically created based on server capabilities\ninstall(server, fileAccess)\nif (lsp.definition) install(server, lsp.definition, { fileAccess })\n\nconst transport = new StdioServerTransport()\nawait server.connect(transport)\n```\n\n### `LspClientOptions`\n\n```typescript\ninterface LspClientOptions {\n  command: string               // LSP server command to spawn\n  args?: string[]               // Command arguments (e.g., ['--stdio'])\n  workspacePath: string         // Absolute path to the workspace root\n  readFile: (path: string) =\u003e Promise\u003cstring\u003e  // File reader for document sync\n  env?: Record\u003cstring, string\u003e  // Additional environment variables\n  initializationOptions?: unknown  // LSP initializationOptions\n  documentIdleTimeout?: number  // Auto-close open docs after ms (default: 30000)\n  requestTimeout?: number       // Timeout for LSP requests in ms (default: 30000)\n}\n```\n\n### How It Works\n\n1. `start()` spawns the LSP server process and performs the initialize/initialized handshake\n2. The server's `ServerCapabilities` response determines which providers are created\n3. Providers that the server does not support remain `undefined`\n4. Documents are automatically opened/closed with the server on demand, with an idle timeout for cleanup\n\n### Lifecycle\n\n```typescript\nconst lsp = createLspClient({ /* ... */ })\n\nlsp.getState()  // 'idle'\nawait lsp.start()\nlsp.getState()  // 'running'\n\n// Use lsp.definition, lsp.references, etc.\n\nawait lsp.stop()\nlsp.getState()  // 'dead'\n```\n\n## Requirements\n\n- Node.js \u003e= 18.0.0\n- TypeScript \u003e= 5.7.0\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopticlm%2Fconnector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopticlm%2Fconnector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopticlm%2Fconnector/lists"}