@likec4/language-server 1.37.0 → 1.38.1
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/dist/LikeC4LanguageServices.d.ts +8 -4
- package/dist/LikeC4LanguageServices.js +12 -2
- package/dist/Rpc.js +2 -2
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/bundled.mjs +4259 -3106
- package/dist/empty.d.ts +2 -0
- package/dist/empty.js +1 -0
- package/dist/filesystem/ChokidarWatcher.d.ts +14 -0
- package/dist/filesystem/ChokidarWatcher.js +64 -0
- package/dist/filesystem/FileSystemWatcher.d.ts +19 -0
- package/dist/filesystem/FileSystemWatcher.js +11 -0
- package/dist/filesystem/LikeC4FileSystem.d.ts +5 -0
- package/dist/filesystem/LikeC4FileSystem.js +56 -0
- package/dist/filesystem/index.d.ts +20 -0
- package/dist/filesystem/index.js +16 -0
- package/dist/index.d.ts +18 -4
- package/dist/index.js +23 -10
- package/dist/mcp/{sseserver/MCPServerFactory.d.ts → MCPServerFactory.d.ts} +1 -1
- package/dist/mcp/MCPServerFactory.js +69 -0
- package/dist/mcp/NoopLikeC4MCPServer.d.ts +4 -10
- package/dist/mcp/NoopLikeC4MCPServer.js +5 -10
- package/dist/mcp/interfaces.d.ts +7 -5
- package/dist/mcp/interfaces.js +4 -0
- package/dist/mcp/server/StdioLikeC4MCPServer.d.ts +16 -0
- package/dist/mcp/server/StdioLikeC4MCPServer.js +43 -0
- package/dist/mcp/{sseserver/MCPServer.d.ts → server/StreamableLikeC4MCPServer.d.ts} +3 -2
- package/dist/mcp/server/StreamableLikeC4MCPServer.js +156 -0
- package/dist/mcp/server/WithMCPServer.d.ts +2 -0
- package/dist/mcp/server/WithMCPServer.js +57 -0
- package/dist/mcp/tools/_common.d.ts +24 -5
- package/dist/mcp/tools/_common.js +31 -3
- package/dist/mcp/tools/find-relationships.d.ts +13 -0
- package/dist/mcp/tools/find-relationships.js +151 -0
- package/dist/mcp/tools/list-projects.js +40 -12
- package/dist/mcp/tools/open-view.d.ts +4 -3
- package/dist/mcp/tools/open-view.js +37 -14
- package/dist/mcp/tools/{read-project-elements.d.ts → read-deployment.d.ts} +6 -3
- package/dist/mcp/tools/read-deployment.js +130 -0
- package/dist/mcp/tools/read-element.d.ts +4 -3
- package/dist/mcp/tools/read-element.js +114 -51
- package/dist/mcp/tools/read-project-summary.d.ts +3 -2
- package/dist/mcp/tools/read-project-summary.js +139 -32
- package/dist/mcp/tools/read-view.d.ts +4 -3
- package/dist/mcp/tools/read-view.js +146 -105
- package/dist/mcp/tools/search-element.js +81 -30
- package/dist/mcp/utils.js +7 -4
- package/dist/model/builder/MergedSpecification.d.ts +1 -1
- package/dist/model/builder/buildModel.d.ts +1 -1
- package/dist/module.d.ts +9 -9
- package/dist/module.js +24 -29
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.js +1 -1
- package/dist/test/testServices.js +3 -2
- package/dist/workspace/LangiumDocuments.d.ts +4 -1
- package/dist/workspace/LangiumDocuments.js +15 -0
- package/dist/workspace/ProjectsManager.d.ts +1 -1
- package/dist/workspace/ProjectsManager.js +18 -19
- package/dist/workspace/WorkspaceManager.d.ts +9 -2
- package/dist/workspace/WorkspaceManager.js +30 -39
- package/package.json +16 -12
- package/dist/LikeC4FileSystem.d.ts +0 -14
- package/dist/LikeC4FileSystem.js +0 -39
- package/dist/mcp/sseserver/MCPServer.js +0 -80
- package/dist/mcp/sseserver/MCPServerFactory.js +0 -50
- package/dist/mcp/sseserver/WithMCPServer.d.ts +0 -9
- package/dist/mcp/sseserver/WithMCPServer.js +0 -53
- 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 {
|
|
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
|
|
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
|
|
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:
|
|
83
|
-
ServerFactory: bind(
|
|
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
|
|
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
|
-
|
|
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
|
-
...
|
|
155
|
+
...NoMCPServer,
|
|
156
|
+
...NoopFileSystem,
|
|
146
157
|
...context
|
|
147
158
|
};
|
|
148
159
|
return inject(
|
|
149
160
|
createDefaultSharedModule(moduleContext),
|
|
150
161
|
LikeC4GeneratedSharedModule,
|
|
151
|
-
|
|
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) {
|
package/dist/protocol.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
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,
|
|
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(
|
|
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;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { NonEmptyArray, ProjectId } from '@likec4/core';
|
|
2
|
-
import {
|
|
2
|
+
import type { LangiumDocument, Stream } from 'langium';
|
|
3
|
+
import { DefaultLangiumDocuments } from 'langium';
|
|
3
4
|
import { type LikeC4LangiumDocument } from '../ast';
|
|
4
5
|
import type { LikeC4SharedServices } from '../module';
|
|
5
6
|
export declare class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
7
|
protected services: LikeC4SharedServices;
|
|
8
|
+
protected compare: any;
|
|
7
9
|
constructor(services: LikeC4SharedServices);
|
|
10
|
+
addDocument(document: LangiumDocument): void;
|
|
8
11
|
/**
|
|
9
12
|
* Returns all user documents, excluding built-in documents.
|
|
10
13
|
*/
|
|
@@ -1,12 +1,27 @@
|
|
|
1
|
+
import { compareNaturalHierarchically } from "@likec4/core/utils";
|
|
1
2
|
import { DefaultLangiumDocuments } from "langium";
|
|
2
3
|
import { groupBy, prop } from "remeda";
|
|
3
4
|
import { isLikeC4LangiumDocument } from "../ast.js";
|
|
4
5
|
import { isLikeC4Builtin } from "../likec4lib.js";
|
|
6
|
+
const compare = compareNaturalHierarchically("/", true);
|
|
7
|
+
const ensureOrder = (a, b) => compare(a.uri.path, b.uri.path);
|
|
5
8
|
export class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
9
|
constructor(services) {
|
|
7
10
|
super(services);
|
|
8
11
|
this.services = services;
|
|
9
12
|
}
|
|
13
|
+
compare = compareNaturalHierarchically("/", true);
|
|
14
|
+
addDocument(document) {
|
|
15
|
+
const uriString = document.uri.toString();
|
|
16
|
+
if (this.documentMap.has(uriString)) {
|
|
17
|
+
throw new Error(`A document with the URI '${uriString}' is already present.`);
|
|
18
|
+
}
|
|
19
|
+
const docs = [...this.documentMap.values(), document].sort(ensureOrder);
|
|
20
|
+
this.documentMap.clear();
|
|
21
|
+
for (const doc of docs) {
|
|
22
|
+
this.documentMap.set(doc.uri.toString(), doc);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
/**
|
|
11
26
|
* Returns all user documents, excluding built-in documents.
|
|
12
27
|
*/
|
|
@@ -5,7 +5,7 @@ import type { Tagged } from 'type-fest';
|
|
|
5
5
|
import { ProjectConfig } from '../config';
|
|
6
6
|
import type { LikeC4SharedServices } from '../module';
|
|
7
7
|
/**
|
|
8
|
-
* A tagged string that represents a project folder
|
|
8
|
+
* A tagged string that represents a project folder URI
|
|
9
9
|
* Always has trailing slash.
|
|
10
10
|
*/
|
|
11
11
|
export type ProjectFolder = Tagged<string, 'ProjectFolder'>;
|
|
@@ -10,23 +10,26 @@ import PQueue from "p-queue";
|
|
|
10
10
|
import picomatch from "picomatch";
|
|
11
11
|
import { hasAtLeast, isNullish, isTruthy, map, pickBy, pipe, prop, sortBy } from "remeda";
|
|
12
12
|
import {
|
|
13
|
-
hasProtocol,
|
|
14
13
|
joinRelativeURL,
|
|
15
|
-
normalizeURL,
|
|
16
14
|
parseFilename,
|
|
17
15
|
withoutProtocol,
|
|
18
|
-
withProtocol,
|
|
19
16
|
withTrailingSlash
|
|
20
17
|
} from "ufo";
|
|
21
18
|
import { parseConfigJson, validateConfig } from "../config/index.js";
|
|
22
19
|
import { logger as mainLogger } from "../logger.js";
|
|
23
20
|
const logger = mainLogger.getChild("ProjectsManager");
|
|
24
|
-
|
|
25
|
-
if (URI.isUri(
|
|
26
|
-
|
|
21
|
+
function normalizeUri(uri) {
|
|
22
|
+
if (URI.isUri(uri)) {
|
|
23
|
+
return uri.toString();
|
|
24
|
+
} else if (typeof uri === "string") {
|
|
25
|
+
return uri.startsWith("file://") ? uri : URI.file(uri).toString();
|
|
26
|
+
} else {
|
|
27
|
+
return uri.uri.toString();
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
}
|
|
30
|
+
export function ProjectFolder(folder) {
|
|
31
|
+
folder = normalizeUri(folder);
|
|
32
|
+
return withTrailingSlash(folder);
|
|
30
33
|
}
|
|
31
34
|
export class ProjectsManager {
|
|
32
35
|
constructor(services) {
|
|
@@ -115,7 +118,7 @@ export class ProjectsManager {
|
|
|
115
118
|
}
|
|
116
119
|
ensureProjectId(projectId) {
|
|
117
120
|
if (projectId === ProjectsManager.DefaultProjectId) {
|
|
118
|
-
return
|
|
121
|
+
return this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
|
|
119
122
|
}
|
|
120
123
|
if (projectId) {
|
|
121
124
|
invariant(this.projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
|
|
@@ -131,7 +134,7 @@ export class ProjectsManager {
|
|
|
131
134
|
}
|
|
132
135
|
checkIfExcluded(document) {
|
|
133
136
|
if (typeof document === "string" || URI.isUri(document)) {
|
|
134
|
-
let docUriAsString =
|
|
137
|
+
let docUriAsString = normalizeUri(document);
|
|
135
138
|
const project = this.findProjectForDocument(docUriAsString);
|
|
136
139
|
return project.exclude ? project.exclude(withoutProtocol(docUriAsString)) : false;
|
|
137
140
|
}
|
|
@@ -201,6 +204,9 @@ ${loggable(error)}`
|
|
|
201
204
|
let mustReset = !!project && !deepEqual(project.config, config);
|
|
202
205
|
let id;
|
|
203
206
|
if (!project) {
|
|
207
|
+
if (this.projectIdToFolder.has(config.name)) {
|
|
208
|
+
logger.warn`Project "${config.name}" already exists, generating unique ID`;
|
|
209
|
+
}
|
|
204
210
|
id = this.uniqueProjectId(config.name);
|
|
205
211
|
project = {
|
|
206
212
|
id,
|
|
@@ -241,14 +247,7 @@ ${loggable(error)}`
|
|
|
241
247
|
return project;
|
|
242
248
|
}
|
|
243
249
|
belongsTo(document) {
|
|
244
|
-
|
|
245
|
-
if (typeof document === "string") {
|
|
246
|
-
documentUri = hasProtocol(document) ? document : withProtocol(document, "file://");
|
|
247
|
-
} else if (URI.isUri(document)) {
|
|
248
|
-
documentUri = document.toString();
|
|
249
|
-
} else {
|
|
250
|
-
documentUri = document.uri.toString();
|
|
251
|
-
}
|
|
250
|
+
const documentUri = normalizeUri(document);
|
|
252
251
|
return this.findProjectForDocument(documentUri).id;
|
|
253
252
|
}
|
|
254
253
|
async reloadProjects(token) {
|
|
@@ -272,7 +271,7 @@ ${loggable(error)}`
|
|
|
272
271
|
const configFiles = [];
|
|
273
272
|
for (const folder of folders) {
|
|
274
273
|
try {
|
|
275
|
-
const files = await this.services.workspace.FileSystemProvider.
|
|
274
|
+
const files = await this.services.workspace.FileSystemProvider.scanProjectFiles(URI.parse(folder.uri));
|
|
276
275
|
for (const file of files) {
|
|
277
276
|
if (file.isFile && this.isConfigFile(file.uri)) {
|
|
278
277
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
19
|
-
|
|
22
|
+
async performStartup(folders) {
|
|
23
|
+
this.folders ??= folders;
|
|
24
|
+
const configFiles = [];
|
|
20
25
|
for (const folder of folders) {
|
|
21
26
|
try {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
4
|
+
"version": "1.38.1",
|
|
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.
|
|
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
|
-
"@
|
|
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
|
-
"@
|
|
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
|
-
"
|
|
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,10 +119,11 @@
|
|
|
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",
|
|
122
|
-
"turbo": "2.5.
|
|
126
|
+
"turbo": "2.5.6",
|
|
123
127
|
"type-fest": "^4.41.0",
|
|
124
128
|
"typescript": "5.9.2",
|
|
125
129
|
"ufo": "1.6.1",
|
|
@@ -133,17 +137,17 @@
|
|
|
133
137
|
"vscode-uri": "3.1.0",
|
|
134
138
|
"which": "^5.0.0",
|
|
135
139
|
"zod": "3.25.67",
|
|
136
|
-
"@likec4/core": "1.
|
|
137
|
-
"@likec4/
|
|
138
|
-
"@likec4/
|
|
139
|
-
"@likec4/
|
|
140
|
-
"@likec4/
|
|
140
|
+
"@likec4/core": "1.38.1",
|
|
141
|
+
"@likec4/layouts": "1.38.1",
|
|
142
|
+
"@likec4/tsconfig": "1.38.1",
|
|
143
|
+
"@likec4/icons": "1.38.1",
|
|
144
|
+
"@likec4/log": "1.38.1"
|
|
141
145
|
},
|
|
142
146
|
"scripts": {
|
|
143
147
|
"typecheck": "tsc -b --verbose",
|
|
144
148
|
"build": "unbuild",
|
|
145
149
|
"pack": "pnpm pack",
|
|
146
|
-
"pregenerate": "rm -f src/generated/*",
|
|
150
|
+
"pregenerate": "rm -f src/generated/* || true",
|
|
147
151
|
"watch:langium": "langium generate --watch",
|
|
148
152
|
"watch:ts": "tsc --watch",
|
|
149
153
|
"generate": "langium generate && tsx scripts/generate-icons.ts",
|
|
@@ -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 {};
|
package/dist/LikeC4FileSystem.js
DELETED
|
@@ -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
|
-
}
|