@likec4/language-server 1.37.0 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/LikeC4LanguageServices.d.ts +7 -4
  2. package/dist/LikeC4LanguageServices.js +12 -2
  3. package/dist/browser.d.ts +1 -1
  4. package/dist/browser.js +1 -1
  5. package/dist/bundled.mjs +4263 -3104
  6. package/dist/empty.d.ts +2 -0
  7. package/dist/empty.js +1 -0
  8. package/dist/filesystem/ChokidarWatcher.d.ts +14 -0
  9. package/dist/filesystem/ChokidarWatcher.js +64 -0
  10. package/dist/filesystem/FileSystemWatcher.d.ts +19 -0
  11. package/dist/filesystem/FileSystemWatcher.js +11 -0
  12. package/dist/filesystem/LikeC4FileSystem.d.ts +5 -0
  13. package/dist/filesystem/LikeC4FileSystem.js +56 -0
  14. package/dist/filesystem/index.d.ts +20 -0
  15. package/dist/filesystem/index.js +16 -0
  16. package/dist/index.d.ts +18 -4
  17. package/dist/index.js +23 -10
  18. package/dist/mcp/{sseserver/MCPServerFactory.d.ts → MCPServerFactory.d.ts} +1 -1
  19. package/dist/mcp/MCPServerFactory.js +69 -0
  20. package/dist/mcp/NoopLikeC4MCPServer.d.ts +4 -10
  21. package/dist/mcp/NoopLikeC4MCPServer.js +5 -10
  22. package/dist/mcp/interfaces.d.ts +7 -5
  23. package/dist/mcp/interfaces.js +4 -0
  24. package/dist/mcp/server/StdioLikeC4MCPServer.d.ts +16 -0
  25. package/dist/mcp/server/StdioLikeC4MCPServer.js +43 -0
  26. package/dist/mcp/{sseserver/MCPServer.d.ts → server/StreamableLikeC4MCPServer.d.ts} +3 -2
  27. package/dist/mcp/server/StreamableLikeC4MCPServer.js +156 -0
  28. package/dist/mcp/server/WithMCPServer.d.ts +2 -0
  29. package/dist/mcp/server/WithMCPServer.js +57 -0
  30. package/dist/mcp/tools/_common.d.ts +24 -5
  31. package/dist/mcp/tools/_common.js +31 -3
  32. package/dist/mcp/tools/find-relationships.d.ts +13 -0
  33. package/dist/mcp/tools/find-relationships.js +151 -0
  34. package/dist/mcp/tools/list-projects.js +40 -12
  35. package/dist/mcp/tools/open-view.d.ts +4 -3
  36. package/dist/mcp/tools/open-view.js +37 -14
  37. package/dist/mcp/tools/{read-project-elements.d.ts → read-deployment.d.ts} +6 -3
  38. package/dist/mcp/tools/read-deployment.js +130 -0
  39. package/dist/mcp/tools/read-element.d.ts +4 -3
  40. package/dist/mcp/tools/read-element.js +114 -51
  41. package/dist/mcp/tools/read-project-summary.d.ts +3 -2
  42. package/dist/mcp/tools/read-project-summary.js +139 -32
  43. package/dist/mcp/tools/read-view.d.ts +4 -3
  44. package/dist/mcp/tools/read-view.js +146 -105
  45. package/dist/mcp/tools/search-element.js +81 -30
  46. package/dist/mcp/utils.js +7 -4
  47. package/dist/module.d.ts +9 -9
  48. package/dist/module.js +24 -29
  49. package/dist/protocol.d.ts +1 -1
  50. package/dist/protocol.js +1 -1
  51. package/dist/test/testServices.js +3 -2
  52. package/dist/workspace/ProjectsManager.js +5 -2
  53. package/dist/workspace/WorkspaceManager.d.ts +9 -2
  54. package/dist/workspace/WorkspaceManager.js +30 -39
  55. package/package.json +14 -10
  56. package/dist/LikeC4FileSystem.d.ts +0 -14
  57. package/dist/LikeC4FileSystem.js +0 -39
  58. package/dist/mcp/sseserver/MCPServer.js +0 -80
  59. package/dist/mcp/sseserver/MCPServerFactory.js +0 -50
  60. package/dist/mcp/sseserver/WithMCPServer.d.ts +0 -9
  61. package/dist/mcp/sseserver/WithMCPServer.js +0 -53
  62. package/dist/mcp/tools/read-project-elements.js +0 -93
package/dist/module.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { GraphvizWasmAdapter, QueueGraphvizLayoter } from "@likec4/layouts";
2
2
  import {
3
3
  DocumentState,
4
- EmptyFileSystem,
5
4
  inject,
6
5
  WorkspaceCache
7
6
  } from "langium";
@@ -10,6 +9,9 @@ import {
10
9
  createDefaultSharedModule
11
10
  } from "langium/lsp";
12
11
  import { LikeC4DocumentationProvider } from "./documentation/index.js";
12
+ import {
13
+ NoopFileSystem
14
+ } from "./filesystem/index.js";
13
15
  import { LikeC4Formatter } from "./formatting/LikeC4Formatter.js";
14
16
  import {
15
17
  LikeC4GeneratedModule,
@@ -25,7 +27,10 @@ import {
25
27
  LikeC4HoverProvider,
26
28
  LikeC4SemanticTokenProvider
27
29
  } from "./lsp/index.js";
28
- import { NoopLikeC4MCPServer, NoopLikeC4MCPServerFactory } from "./mcp/NoopLikeC4MCPServer.js";
30
+ import {
31
+ NoMCPServer
32
+ } from "./mcp/interfaces.js";
33
+ import { LikeC4MCPServerFactory } from "./mcp/MCPServerFactory.js";
29
34
  import {
30
35
  DefaultLikeC4ModelBuilder,
31
36
  DeploymentsIndex,
@@ -54,7 +59,7 @@ import {
54
59
  LikeC4WorkspaceManager,
55
60
  ProjectsManager
56
61
  } from "./workspace/index.js";
57
- const LikeC4SharedModule = {
62
+ const createLikeC4SharedModule = (context) => ({
58
63
  lsp: {
59
64
  NodeKindProvider: (services) => new NodeKindProvider(services),
60
65
  WorkspaceSymbolProvider: (services) => new WorkspaceSymbolProvider(services)
@@ -63,13 +68,15 @@ const LikeC4SharedModule = {
63
68
  IndexManager: (services) => new IndexManager(services),
64
69
  LangiumDocuments: (services) => new LangiumDocuments(services),
65
70
  ProjectsManager: (services) => new ProjectsManager(services),
66
- WorkspaceManager: (services) => new LikeC4WorkspaceManager(services)
71
+ WorkspaceManager: (services) => new LikeC4WorkspaceManager(services),
72
+ FileSystemProvider: (services) => context.fileSystemProvider(services),
73
+ FileSystemWatcher: (services) => context.fileSystemWatcher(services)
67
74
  }
68
- };
75
+ });
69
76
  function bind(Type) {
70
77
  return (services) => new Type(services);
71
78
  }
72
- export const LikeC4Module = {
79
+ export const createLikeC4Module = (context) => ({
73
80
  documentation: {
74
81
  DocumentationProvider: bind(LikeC4DocumentationProvider)
75
82
  },
@@ -79,8 +86,8 @@ export const LikeC4Module = {
79
86
  ValidatedWorkspaceCache: (services) => new WorkspaceCache(services.shared, DocumentState.Validated),
80
87
  Rpc: bind(Rpc),
81
88
  mcp: {
82
- Server: bind(NoopLikeC4MCPServer),
83
- ServerFactory: bind(NoopLikeC4MCPServerFactory)
89
+ Server: (services) => context.mcpServer(services),
90
+ ServerFactory: bind(LikeC4MCPServerFactory)
84
91
  },
85
92
  likec4: {
86
93
  LanguageServices: bind(DefaultLikeC4LanguageServices),
@@ -119,13 +126,16 @@ export const LikeC4Module = {
119
126
  parser: {
120
127
  ValueConverter: bind(LikeC4ValueConverter)
121
128
  }
122
- };
123
- export function createCustomLanguageServices(context, module, module2, module3) {
129
+ });
130
+ export function createLanguageServices(context, module, module2, module3) {
124
131
  const shared = createSharedServices(context);
125
132
  const modules = [
126
133
  createDefaultModule({ shared }),
127
134
  LikeC4GeneratedModule,
128
- LikeC4Module,
135
+ createLikeC4Module({
136
+ ...NoMCPServer,
137
+ ...context
138
+ }),
129
139
  module,
130
140
  module2,
131
141
  module3
@@ -142,30 +152,15 @@ export function createCustomLanguageServices(context, module, module2, module3)
142
152
  }
143
153
  export function createSharedServices(context = {}) {
144
154
  const moduleContext = {
145
- ...EmptyFileSystem,
155
+ ...NoMCPServer,
156
+ ...NoopFileSystem,
146
157
  ...context
147
158
  };
148
159
  return inject(
149
160
  createDefaultSharedModule(moduleContext),
150
161
  LikeC4GeneratedSharedModule,
151
- LikeC4SharedModule
152
- );
153
- }
154
- export function createLanguageServices(context = {}) {
155
- const shared = createSharedServices(context);
156
- const likec4 = inject(
157
- createDefaultModule({ shared }),
158
- LikeC4GeneratedModule,
159
- LikeC4Module
162
+ createLikeC4SharedModule(moduleContext)
160
163
  );
161
- shared.ServiceRegistry.register(likec4);
162
- registerValidationChecks(likec4);
163
- if (!context.connection) {
164
- shared.workspace.ConfigurationProvider.initialized({});
165
- } else {
166
- likec4.Rpc.init();
167
- }
168
- return { shared, likec4 };
169
164
  }
170
165
  function _merge(target, source) {
171
166
  if (source) {
@@ -1,5 +1,5 @@
1
1
  import type { ComputedLikeC4ModelData, ComputedView, DeploymentFqn, DiagramView, Fqn, LayoutedLikeC4ModelData, NonEmptyArray, ProjectId, RelationId, ViewChange, ViewId } from '@likec4/core';
2
- import { NotificationType, RequestType, RequestType0 } from 'vscode-languageserver-protocol';
2
+ import { NotificationType, RequestType, RequestType0 } from 'vscode-jsonrpc';
3
3
  import type { DiagnosticSeverity, DocumentUri, Location, Position, Range, URI } from 'vscode-languageserver-types';
4
4
  import type { ProjectConfig } from './config';
5
5
  export declare namespace DidChangeModelNotification {
package/dist/protocol.js CHANGED
@@ -1,4 +1,4 @@
1
- import { NotificationType, RequestType, RequestType0 } from "vscode-languageserver-protocol";
1
+ import { NotificationType, RequestType, RequestType0 } from "vscode-jsonrpc";
2
2
  export var DidChangeModelNotification;
3
3
  ((DidChangeModelNotification2) => {
4
4
  DidChangeModelNotification2.type = new NotificationType("likec4/onDidChangeModel");
@@ -1,12 +1,13 @@
1
- import { DocumentState, EmptyFileSystem, TextDocument, UriUtils } from "langium";
1
+ import { DocumentState, TextDocument, UriUtils } from "langium";
2
2
  import * as assert from "node:assert";
3
3
  import { entries } from "remeda";
4
4
  import stripIndent from "strip-indent";
5
5
  import { DiagnosticSeverity } from "vscode-languageserver-types";
6
6
  import { URI, Utils } from "vscode-uri";
7
+ import { NoopFileSystem } from "../filesystem/index.js";
7
8
  import { createLanguageServices } from "../module.js";
8
9
  export function createTestServices(workspace = "file:///test/workspace") {
9
- const services = createLanguageServices(EmptyFileSystem).likec4;
10
+ const services = createLanguageServices(NoopFileSystem).likec4;
10
11
  const metaData = services.LanguageMetaData;
11
12
  const langiumDocuments = services.shared.workspace.LangiumDocuments;
12
13
  const documentBuilder = services.shared.workspace.DocumentBuilder;
@@ -115,7 +115,7 @@ export class ProjectsManager {
115
115
  }
116
116
  ensureProjectId(projectId) {
117
117
  if (projectId === ProjectsManager.DefaultProjectId) {
118
- return projectId;
118
+ return this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
119
119
  }
120
120
  if (projectId) {
121
121
  invariant(this.projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
@@ -201,6 +201,9 @@ ${loggable(error)}`
201
201
  let mustReset = !!project && !deepEqual(project.config, config);
202
202
  let id;
203
203
  if (!project) {
204
+ if (this.projectIdToFolder.has(config.name)) {
205
+ logger.warn`Project "${config.name}" already exists, generating unique ID`;
206
+ }
204
207
  id = this.uniqueProjectId(config.name);
205
208
  project = {
206
209
  id,
@@ -272,7 +275,7 @@ ${loggable(error)}`
272
275
  const configFiles = [];
273
276
  for (const folder of folders) {
274
277
  try {
275
- const files = await this.services.workspace.FileSystemProvider.readDirectory(URI.parse(folder.uri));
278
+ const files = await this.services.workspace.FileSystemProvider.scanProjectFiles(URI.parse(folder.uri));
276
279
  for (const file of files) {
277
280
  if (file.isFile && this.isConfigFile(file.uri)) {
278
281
  configFiles.push(file);
@@ -1,12 +1,19 @@
1
- import type { FileSelector, FileSystemNode, LangiumDocument } from 'langium';
1
+ import type { BuildOptions, FileSelector, FileSystemNode, LangiumDocument, LangiumDocumentFactory } from 'langium';
2
2
  import { DefaultWorkspaceManager } from 'langium';
3
3
  import type { WorkspaceFolder } from 'vscode-languageserver';
4
4
  import { URI } from 'vscode-uri';
5
+ import type { FileSystemProvider } from '../filesystem';
5
6
  import type { LikeC4SharedServices } from '../module';
6
7
  export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
7
8
  private services;
8
- private documentFactory;
9
+ protected readonly documentFactory: LangiumDocumentFactory;
10
+ protected readonly fileSystemProvider: FileSystemProvider;
11
+ initialBuildOptions: BuildOptions;
9
12
  constructor(services: LikeC4SharedServices);
13
+ /**
14
+ * First load all project config files, then load all documents in the workspace.
15
+ */
16
+ protected performStartup(folders: WorkspaceFolder[]): Promise<LangiumDocument[]>;
10
17
  /**
11
18
  * Load all additional documents that shall be visible in the context of the given workspace
12
19
  * folders and add them to the collector. This can be used to include built-in libraries of
@@ -8,58 +8,49 @@ export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
8
8
  super(services);
9
9
  this.services = services;
10
10
  this.documentFactory = services.workspace.LangiumDocumentFactory;
11
+ this.fileSystemProvider = services.workspace.FileSystemProvider;
11
12
  }
12
13
  documentFactory;
14
+ fileSystemProvider;
15
+ initialBuildOptions = {
16
+ eagerLinking: true,
17
+ validation: true
18
+ };
13
19
  /**
14
- * Load all additional documents that shall be visible in the context of the given workspace
15
- * folders and add them to the collector. This can be used to include built-in libraries of
16
- * your language, which can be either loaded from provided files or constructed in memory.
20
+ * First load all project config files, then load all documents in the workspace.
17
21
  */
18
- async loadAdditionalDocuments(folders, collector) {
19
- const projects = this.services.workspace.ProjectsManager;
22
+ async performStartup(folders) {
23
+ this.folders ??= folders;
24
+ const configFiles = [];
20
25
  for (const folder of folders) {
21
26
  try {
22
- const content = await this.fileSystemProvider.readDirectory(URI.parse(folder.uri));
23
- for (const entry of content) {
24
- try {
25
- await projects.loadConfigFile(entry);
26
- } catch (error) {
27
- logError(error);
28
- }
29
- }
27
+ const uri = URI.parse(folder.uri);
28
+ const found = await this.fileSystemProvider.scanProjectFiles(uri);
29
+ configFiles.push(...found);
30
+ this.services.workspace.FileSystemWatcher.watch(uri.fsPath);
31
+ } catch (error) {
32
+ logError(error);
33
+ }
34
+ }
35
+ const projects = this.services.workspace.ProjectsManager;
36
+ for (const entry of configFiles) {
37
+ try {
38
+ await projects.loadConfigFile(entry);
30
39
  } catch (error) {
31
40
  logError(error);
32
41
  }
33
42
  }
43
+ return await super.performStartup(folders);
44
+ }
45
+ /**
46
+ * Load all additional documents that shall be visible in the context of the given workspace
47
+ * folders and add them to the collector. This can be used to include built-in libraries of
48
+ * your language, which can be either loaded from provided files or constructed in memory.
49
+ */
50
+ async loadAdditionalDocuments(folders, collector) {
34
51
  collector(this.documentFactory.fromString(BuiltIn.Content, URI.parse(BuiltIn.Uri)));
35
52
  await super.loadAdditionalDocuments(folders, collector);
36
53
  }
37
- // /**
38
- // * We override the default implementation to process project config files during the traversal.
39
- // * This is necessary to ensure that the project config files are loaded and processed correctly.
40
- // */
41
- // protected override async traverseFolder(
42
- // workspaceFolder: WorkspaceFolder,
43
- // folderPath: URI,
44
- // fileExtensions: string[],
45
- // collector: (document: LangiumDocument) => void,
46
- // ): Promise<void> {
47
- // // Then load other files
48
- // for (const entry of nonConfigFiles) {
49
- // try {
50
- // if (this.includeEntry(workspaceFolder, entry, fileExtensions)) {
51
- // if (entry.isDirectory) {
52
- // await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector)
53
- // } else if (entry.isFile) {
54
- // const document = await this.langiumDocuments.getOrCreateDocument(entry.uri)
55
- // collector(document)
56
- // }
57
- // }
58
- // } catch (error) {
59
- // logError(error)
60
- // }
61
- // }
62
- // }
63
54
  /**
64
55
  * Determine whether the given folder entry shall be included while indexing the workspace.
65
56
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.37.0",
4
+ "version": "1.38.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -89,21 +89,24 @@
89
89
  "access": "public"
90
90
  },
91
91
  "dependencies": {
92
- "@hpcc-js/wasm-graphviz": "1.10.0"
92
+ "@hpcc-js/wasm-graphviz": "1.11.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/chroma-js": "^3.1.1",
96
96
  "@types/natural-compare-lite": "^1.4.2",
97
- "@modelcontextprotocol/sdk": "^1.13.2",
97
+ "@types/vscode": "^1.84.0",
98
+ "@modelcontextprotocol/sdk": "^1.17.2",
98
99
  "@msgpack/msgpack": "^3.1.2",
99
100
  "@smithy/util-base64": "^4.0.0",
100
- "@types/express": "^5.0.3",
101
+ "@hono/node-server": "^1.14.4",
101
102
  "@types/node": "~20.19.2",
102
103
  "@types/picomatch": "^4.0.2",
103
104
  "@types/which": "^3.0.4",
105
+ "chokidar": "^4.0.3",
104
106
  "chroma-js": "^3.1.2",
107
+ "defu": "^6.1.4",
105
108
  "esm-env": "^1.2.2",
106
- "express": "^5.1.0",
109
+ "hono": "^4.9.0",
107
110
  "fast-equals": "^5.2.2",
108
111
  "fdir": "6.4.6",
109
112
  "indent-string": "^5.0.0",
@@ -116,6 +119,7 @@
116
119
  "p-timeout": "6.1.4",
117
120
  "picomatch": "^4.0.3",
118
121
  "pretty-ms": "^9.2.0",
122
+ "fetch-to-node": "^2.1.0",
119
123
  "remeda": "^2.23.1",
120
124
  "strip-indent": "^4.0.0",
121
125
  "tsx": "4.20.3",
@@ -133,11 +137,11 @@
133
137
  "vscode-uri": "3.1.0",
134
138
  "which": "^5.0.0",
135
139
  "zod": "3.25.67",
136
- "@likec4/core": "1.37.0",
137
- "@likec4/icons": "1.37.0",
138
- "@likec4/layouts": "1.37.0",
139
- "@likec4/log": "1.37.0",
140
- "@likec4/tsconfig": "1.37.0"
140
+ "@likec4/log": "1.38.0",
141
+ "@likec4/icons": "1.38.0",
142
+ "@likec4/core": "1.38.0",
143
+ "@likec4/tsconfig": "1.38.0",
144
+ "@likec4/layouts": "1.38.0"
141
145
  },
142
146
  "scripts": {
143
147
  "typecheck": "tsc -b --verbose",
@@ -1,14 +0,0 @@
1
- import { type FileSystemNode, URI } from 'langium';
2
- import { NodeFileSystemProvider } from 'langium/node';
3
- export declare const LikeC4FileSystem: {
4
- fileSystemProvider: () => SymLinkTraversingFileSystemProvider;
5
- };
6
- /**
7
- * A file system provider that follows symbolic links.
8
- * @see https://github.com/likec4/likec4/pull/1213
9
- */
10
- declare class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
11
- readFile(uri: URI): Promise<string>;
12
- readDirectory(folderPath: URI): Promise<FileSystemNode[]>;
13
- }
14
- export {};
@@ -1,39 +0,0 @@
1
- import { fdir } from "fdir";
2
- import { URI } from "langium";
3
- import { NodeFileSystemProvider } from "langium/node";
4
- import { LikeC4LanguageMetaData } from "./generated/module.js";
5
- import { Content, isLikeC4Builtin } from "./likec4lib.js";
6
- import { logError } from "./logger.js";
7
- import { ProjectsManager } from "./workspace/ProjectsManager.js";
8
- export const LikeC4FileSystem = {
9
- fileSystemProvider: () => new SymLinkTraversingFileSystemProvider()
10
- };
11
- const SearchExtension = [
12
- ...LikeC4LanguageMetaData.fileExtensions,
13
- ...ProjectsManager.ConfigFileNames
14
- ];
15
- const hasExtension = (path) => SearchExtension.some((ext) => path.endsWith(ext));
16
- class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
17
- async readFile(uri) {
18
- if (isLikeC4Builtin(uri)) {
19
- return Promise.resolve(Content);
20
- }
21
- return await super.readFile(uri);
22
- }
23
- async readDirectory(folderPath) {
24
- const entries = [];
25
- try {
26
- const crawled = await new fdir().withSymlinks({ resolvePaths: false }).withFullPaths().filter(hasExtension).crawl(folderPath.fsPath).withPromise();
27
- for (const path of crawled) {
28
- entries.push({
29
- isFile: true,
30
- isDirectory: false,
31
- uri: URI.file(path)
32
- });
33
- }
34
- } catch (error) {
35
- logError(error);
36
- }
37
- return entries;
38
- }
39
- }
@@ -1,80 +0,0 @@
1
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
2
- import express from "express";
3
- import { logger } from "../utils.js";
4
- export class SSELikeC4MCPServer {
5
- constructor(services) {
6
- this.services = services;
7
- }
8
- // Store transports by session ID to send notifications
9
- transports = {};
10
- server = void 0;
11
- _port = 33335;
12
- get isStarted() {
13
- return this.server?.listening === true;
14
- }
15
- get port() {
16
- return this._port;
17
- }
18
- async dispose() {
19
- await this.stop();
20
- }
21
- async start(port = 33335) {
22
- if (this.server) {
23
- if (this.port === port) {
24
- return;
25
- }
26
- await this.stop();
27
- }
28
- logger.info("Starting MCP server on port {port}", { port });
29
- this._port = port;
30
- const mcp = this.services.mcp.ServerFactory.create();
31
- const app = express();
32
- app.get("/sse", async (_, res) => {
33
- const transport = new SSEServerTransport("/messages", res);
34
- this.transports[transport.sessionId] = transport;
35
- logger.debug`SSE connection established, sessionId: ${transport.sessionId}`;
36
- res.on("close", () => {
37
- delete this.transports[transport.sessionId];
38
- });
39
- await mcp.connect(transport);
40
- });
41
- app.post("/messages", async (req, res) => {
42
- const sessionId = req.query["sessionId"];
43
- const transport = this.transports[sessionId];
44
- if (transport) {
45
- logger.debug`SSE message received, sessionId: ${sessionId}`;
46
- await transport.handlePostMessage(req, res);
47
- } else {
48
- res.status(400).send("No transport found for sessionId");
49
- }
50
- });
51
- return new Promise((resolve, reject) => {
52
- this.server = app.listen(this._port, (err) => {
53
- if (err) {
54
- reject(err);
55
- return;
56
- }
57
- logger.info("MCP server listening on port {port}", { port: this._port });
58
- resolve();
59
- });
60
- });
61
- }
62
- async stop() {
63
- this.transports = {};
64
- const server = this.server;
65
- if (!server) {
66
- return;
67
- }
68
- logger.info("Stopping MCP server");
69
- this.server = void 0;
70
- return new Promise((resolve) => {
71
- server.close((err) => {
72
- if (err) {
73
- logger.error("Failed to stop MCP server", { err });
74
- }
75
- logger.info("MCP server stopped");
76
- resolve();
77
- });
78
- });
79
- }
80
- }
@@ -1,50 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import packageJson from "../../../package.json" with { type: "json" };
3
- import { listProjects } from "../tools/list-projects.js";
4
- import { openView } from "../tools/open-view.js";
5
- import { readElement } from "../tools/read-element.js";
6
- import { readProjectElements } from "../tools/read-project-elements.js";
7
- import { readProjectSummary } from "../tools/read-project-summary.js";
8
- import { readView } from "../tools/read-view.js";
9
- import { searchElement } from "../tools/search-element.js";
10
- export class LikeC4MCPServerFactory {
11
- constructor(services) {
12
- this.services = services;
13
- }
14
- create(options) {
15
- const isInEditor = this.services.shared.lsp.Connection !== void 0;
16
- const mcp = new McpServer({
17
- name: "LikeC4",
18
- version: packageJson.version
19
- }, {
20
- instructions: `Provides access to LikeC4 model.
21
- Available tools:
22
- - list-projects: List all available LikeC4 projects in the workspace
23
- - read-project-summary: to understand project specifications (what element kinds, tags, metadata keys are available) and available project views
24
- - read-project-elements: list all elements in the project
25
- - search-element: Search for LikeC4 element by partial match of id, title, kind, shape or tags
26
- - read-element: all information about the element (includes source location)
27
- - read-view: all information about the view (includes source location)
28
- ${isInEditor ? "- open-view: opens the panel in the editor with the LikeC4 view" : ""}
29
-
30
- Documentation for LikeC4 is available at https://likec4.dev/llms-full.txt
31
- `,
32
- ...options,
33
- capabilities: {
34
- tools: {},
35
- resources: {},
36
- ...options?.capabilities
37
- }
38
- });
39
- mcp.registerTool(...listProjects(this.services.likec4.LanguageServices));
40
- mcp.registerTool(...readProjectSummary(this.services.likec4.LanguageServices));
41
- mcp.registerTool(...readProjectElements(this.services.likec4.LanguageServices));
42
- mcp.registerTool(...readElement(this.services.likec4.LanguageServices));
43
- mcp.registerTool(...readView(this.services.likec4.LanguageServices));
44
- mcp.registerTool(...searchElement(this.services.likec4.LanguageServices));
45
- if (isInEditor) {
46
- mcp.registerTool(...openView(this.services.likec4.LanguageServices));
47
- }
48
- return mcp;
49
- }
50
- }
@@ -1,9 +0,0 @@
1
- import type { LikeC4Services } from '../../module';
2
- import type { LikeC4MCPServer } from '../interfaces';
3
- import { LikeC4MCPServerFactory } from './MCPServerFactory';
4
- export declare const WithMCPServer: {
5
- mcp: {
6
- Server(services: LikeC4Services): LikeC4MCPServer;
7
- ServerFactory(services: LikeC4Services): LikeC4MCPServerFactory;
8
- };
9
- };
@@ -1,53 +0,0 @@
1
- import { loggable } from "@likec4/log";
2
- import { isError } from "remeda";
3
- import { logger } from "../../logger.js";
4
- import { SSELikeC4MCPServer } from "./MCPServer.js";
5
- import { LikeC4MCPServerFactory } from "./MCPServerFactory.js";
6
- export const WithMCPServer = {
7
- mcp: {
8
- Server(services) {
9
- logger.debug("Creating SSELikeC4MCPServer");
10
- const server = new SSELikeC4MCPServer(services);
11
- const langId = services.LanguageMetaData.languageId;
12
- const connection = services.shared.lsp.Connection;
13
- services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
14
- if (update.section !== langId) {
15
- logger.warn("Unexpected configuration update: {update}", { update });
16
- return;
17
- }
18
- logger.debug("Configuration update: {update}", { update });
19
- const {
20
- enabled = false,
21
- port = 33335
22
- } = update.configuration.mcp;
23
- if (!enabled) {
24
- server.stop();
25
- return;
26
- }
27
- Promise.resolve().then(() => server.start(port)).then(() => {
28
- connection?.telemetry?.logEvent({
29
- eventName: "mcp-server-started",
30
- mcpPort: port
31
- });
32
- }).catch((err) => {
33
- const message = isError(err) ? err.message : void 0;
34
- connection?.telemetry?.logEvent({
35
- eventName: "mcp-server-start-failed",
36
- mcpPort: port,
37
- ...message && { message }
38
- });
39
- logger.error("Failed to start LikeC4 MCP Server", { err });
40
- if (connection) {
41
- connection.window.showErrorMessage(`LikeC4: Failed to start MCP Server
42
-
43
- ${loggable(err)}`);
44
- }
45
- });
46
- });
47
- return server;
48
- },
49
- ServerFactory(services) {
50
- return new LikeC4MCPServerFactory(services);
51
- }
52
- }
53
- };