@likec4/language-server 1.32.2 → 1.34.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 (77) hide show
  1. package/README.md +27 -1
  2. package/dist/Rpc.d.ts +2 -0
  3. package/dist/Rpc.js +24 -6
  4. package/dist/ast.d.ts +6 -5
  5. package/dist/ast.js +3 -0
  6. package/dist/bundled.mjs +3194 -2547
  7. package/dist/formatting/LikeC4Formatter.d.ts +1 -0
  8. package/dist/formatting/LikeC4Formatter.js +25 -1
  9. package/dist/generated/ast.d.ts +21 -12
  10. package/dist/generated/ast.js +21 -9
  11. package/dist/generated/grammar.js +1 -1
  12. package/dist/index.d.ts +0 -1
  13. package/dist/index.js +1 -2
  14. package/dist/lsp/SemanticTokenProvider.js +1 -1
  15. package/dist/mcp/{LikeC4MCPServerFactory.d.ts → NoopLikeC4MCPServer.d.ts} +1 -9
  16. package/dist/mcp/interfaces.d.ts +11 -0
  17. package/dist/mcp/interfaces.js +0 -0
  18. package/dist/mcp/sseserver/MCPServer.d.ts +1 -1
  19. package/dist/mcp/sseserver/MCPServer.js +5 -5
  20. package/dist/mcp/sseserver/MCPServerFactory.js +30 -58
  21. package/dist/mcp/sseserver/{with-mcp-server.d.ts → WithMCPServer.d.ts} +1 -1
  22. package/dist/mcp/tools/_common.d.ts +68 -0
  23. package/dist/mcp/tools/_common.js +14 -0
  24. package/dist/mcp/tools/list-projects.d.ts +6 -0
  25. package/dist/mcp/tools/list-projects.js +31 -0
  26. package/dist/mcp/tools/open-view.d.ts +10 -0
  27. package/dist/mcp/tools/open-view.js +29 -0
  28. package/dist/mcp/tools/read-element.d.ts +10 -0
  29. package/dist/mcp/tools/read-element.js +135 -0
  30. package/dist/mcp/tools/read-project-elements.d.ts +8 -0
  31. package/dist/mcp/tools/read-project-elements.js +93 -0
  32. package/dist/mcp/tools/read-project-summary.d.ts +8 -0
  33. package/dist/mcp/tools/read-project-summary.js +68 -0
  34. package/dist/mcp/tools/read-view.d.ts +10 -0
  35. package/dist/mcp/tools/read-view.js +164 -0
  36. package/dist/mcp/tools/search-element.d.ts +8 -0
  37. package/dist/mcp/tools/search-element.js +105 -0
  38. package/dist/mcp/utils.d.ts +17 -34
  39. package/dist/mcp/utils.js +41 -101
  40. package/dist/model/builder/MergedSpecification.js +8 -4
  41. package/dist/model/index.d.ts +1 -0
  42. package/dist/model/index.js +1 -0
  43. package/dist/model/model-parser-where.d.ts +1 -0
  44. package/dist/model/model-parser-where.js +30 -24
  45. package/dist/model/model-parser.d.ts +140 -0
  46. package/dist/model/parser/Base.d.ts +30 -2
  47. package/dist/model/parser/Base.js +54 -3
  48. package/dist/model/parser/DeploymentModelParser.d.ts +17 -1
  49. package/dist/model/parser/DeploymentModelParser.js +24 -21
  50. package/dist/model/parser/DeploymentViewParser.d.ts +17 -1
  51. package/dist/model/parser/DeploymentViewParser.js +15 -6
  52. package/dist/model/parser/FqnRefParser.d.ts +17 -1
  53. package/dist/model/parser/FqnRefParser.js +42 -16
  54. package/dist/model/parser/GlobalsParser.d.ts +17 -1
  55. package/dist/model/parser/ImportsParser.d.ts +14 -0
  56. package/dist/model/parser/ModelParser.d.ts +17 -1
  57. package/dist/model/parser/ModelParser.js +22 -14
  58. package/dist/model/parser/PredicatesParser.d.ts +17 -1
  59. package/dist/model/parser/SpecificationParser.d.ts +14 -0
  60. package/dist/model/parser/SpecificationParser.js +16 -11
  61. package/dist/model/parser/ValueConverter.d.ts +4 -0
  62. package/dist/model/parser/ValueConverter.js +12 -0
  63. package/dist/model/parser/ViewsParser.d.ts +17 -1
  64. package/dist/model/parser/ViewsParser.js +21 -7
  65. package/dist/module.d.ts +5 -4
  66. package/dist/module.js +6 -7
  67. package/dist/protocol.d.ts +16 -0
  68. package/dist/protocol.js +4 -0
  69. package/dist/validation/property-checks.js +1 -1
  70. package/dist/views/configurable-layouter.js +22 -28
  71. package/dist/views/likec4-views.d.ts +5 -0
  72. package/dist/views/likec4-views.js +6 -1
  73. package/package.json +20 -19
  74. package/dist/mcp/LikeC4MCPTools.d.ts +0 -96
  75. package/dist/mcp/LikeC4MCPTools.js +0 -290
  76. /package/dist/mcp/{LikeC4MCPServerFactory.js → NoopLikeC4MCPServer.js} +0 -0
  77. /package/dist/mcp/sseserver/{with-mcp-server.js → WithMCPServer.js} +0 -0
package/dist/module.js CHANGED
@@ -25,17 +25,14 @@ import {
25
25
  LikeC4HoverProvider,
26
26
  LikeC4SemanticTokenProvider
27
27
  } from "./lsp/index.js";
28
- import {
29
- NoopLikeC4MCPServer,
30
- NoopLikeC4MCPServerFactory
31
- } from "./mcp/LikeC4MCPServerFactory.js";
32
- import { DefaultLikeC4MCPTools } from "./mcp/LikeC4MCPTools.js";
28
+ import { NoopLikeC4MCPServer, NoopLikeC4MCPServerFactory } from "./mcp/NoopLikeC4MCPServer.js";
33
29
  import {
34
30
  DefaultLikeC4ModelBuilder,
35
31
  DeploymentsIndex,
36
32
  FqnIndex,
37
33
  LikeC4ModelLocator,
38
- LikeC4ModelParser
34
+ LikeC4ModelParser,
35
+ LikeC4ValueConverter
39
36
  } from "./model/index.js";
40
37
  import { LikeC4ModelChanges } from "./model-change/ModelChanges.js";
41
38
  import {
@@ -79,7 +76,6 @@ export const LikeC4Module = {
79
76
  ValidatedWorkspaceCache: (services) => new WorkspaceCache(services.shared, DocumentState.Validated),
80
77
  Rpc: bind(Rpc),
81
78
  mcp: {
82
- Tools: bind(DefaultLikeC4MCPTools),
83
79
  Server: bind(NoopLikeC4MCPServer),
84
80
  ServerFactory: bind(NoopLikeC4MCPServerFactory)
85
81
  },
@@ -116,6 +112,9 @@ export const LikeC4Module = {
116
112
  NameProvider: bind(LikeC4NameProvider),
117
113
  ScopeComputation: bind(LikeC4ScopeComputation),
118
114
  ScopeProvider: bind(LikeC4ScopeProvider)
115
+ },
116
+ parser: {
117
+ ValueConverter: bind(LikeC4ValueConverter)
119
118
  }
120
119
  };
121
120
  export function createCustomLanguageServices(context, module, module2, module3) {
@@ -5,6 +5,19 @@ export declare namespace DidChangeModelNotification {
5
5
  const type: NotificationType<string>;
6
6
  type Type = typeof type;
7
7
  }
8
+ /**
9
+ * When server requests to open a likec4 preview panel
10
+ * (available only in the editor).
11
+ * (not the best place, but seems to be working)
12
+ */
13
+ export declare namespace DidRequestOpenViewNotification {
14
+ type Params = {
15
+ viewId: ViewId;
16
+ projectId: ProjectId;
17
+ };
18
+ const type: NotificationType<Params>;
19
+ type Type = typeof type;
20
+ }
8
21
  /**
9
22
  * Request to fetch the computed model data
10
23
  * If LSP has multiple projects, the projectId is required.
@@ -175,9 +188,12 @@ export declare namespace FetchTelemetryMetrics {
175
188
  type Res = {
176
189
  metrics: null | {
177
190
  elementKinds: number;
191
+ deploymentKinds: number;
178
192
  relationshipKinds: number;
179
193
  tags: number;
194
+ customColors: number;
180
195
  elements: number;
196
+ deploymentNodes: number;
181
197
  relationships: number;
182
198
  views: number;
183
199
  projects: number;
package/dist/protocol.js CHANGED
@@ -3,6 +3,10 @@ export var DidChangeModelNotification;
3
3
  ((DidChangeModelNotification2) => {
4
4
  DidChangeModelNotification2.type = new NotificationType("likec4/onDidChangeModel");
5
5
  })(DidChangeModelNotification || (DidChangeModelNotification = {}));
6
+ export var DidRequestOpenViewNotification;
7
+ ((DidRequestOpenViewNotification2) => {
8
+ DidRequestOpenViewNotification2.type = new NotificationType("likec4/onRequestOpenView");
9
+ })(DidRequestOpenViewNotification || (DidRequestOpenViewNotification = {}));
6
10
  export var FetchComputedModel;
7
11
  ((FetchComputedModel2) => {
8
12
  FetchComputedModel2.req = new RequestType("likec4/fetchComputedModel");
@@ -48,7 +48,7 @@ export const notesPropertyRuleChecks = (_) => {
48
48
  export const colorLiteralRuleChecks = (_) => {
49
49
  return (node, accept) => {
50
50
  if (node.$type === "HexColor") {
51
- if (node.hex === void 0) {
51
+ if (node.hex === void 0 || isString(node.hex) && !node.hex.match(/^[a-fA-F0-9]+$/)) {
52
52
  accept("error", `Invalid HEX`, {
53
53
  node,
54
54
  property: "hex"
@@ -15,41 +15,35 @@ export const ConfigurableLayouter = {
15
15
  likec4: {
16
16
  Layouter(services) {
17
17
  logger.debug("Creating ConfigurableLayouter");
18
- const wasmAdapter = new GraphvizWasmAdapter();
19
- const layouter = new QueueGraphvizLayoter({
20
- graphviz: wasmAdapter
21
- });
22
- const langId = services.LanguageMetaData.languageId;
18
+ const layouter = new QueueGraphvizLayoter();
23
19
  services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
24
20
  logger.debug("Configuration update: {update}", { update });
25
- if (update.section === langId) {
26
- try {
27
- const { mode, path } = update.configuration.graphviz ?? {
28
- mode: "wasm",
29
- path: ""
30
- };
31
- if (mode === "wasm") {
32
- layouter.changePort(wasmAdapter);
33
- logger.info("use graphviz wasm");
34
- return;
35
- }
21
+ if (update.section !== services.LanguageMetaData.languageId) {
22
+ logger.debug(`Ignoring configuration update as it is not for ${services.LanguageMetaData.languageId}`);
23
+ return;
24
+ }
25
+ try {
26
+ const { mode, path } = update.configuration.graphviz ?? {
27
+ mode: "wasm",
28
+ path: ""
29
+ };
30
+ if (mode !== "wasm") {
36
31
  let binaryPath = isEmpty(path) ? graphvizBinPath() : path;
37
- if (binaryPath === null) {
38
- layouter.changePort(wasmAdapter);
39
- logger.warn(`No Graphviz binaries found on PATH, use graphviz wasm`);
40
- services.shared.lsp.Connection?.window.showWarningMessage(
41
- "No Graphviz binaries found on PATH, set path to binaries in settings."
42
- );
32
+ if (!isEmpty(binaryPath)) {
33
+ layouter.changePort(new GraphvizBinaryAdapter(binaryPath));
34
+ logger.info`use graphviz binary: ${binaryPath}`;
43
35
  return;
44
36
  }
45
- layouter.changePort(new GraphvizBinaryAdapter(binaryPath));
46
- logger.info`use graphviz binary: ${binaryPath}`;
47
- } catch (error) {
48
- logger.error("Failed to update configuration", { error });
37
+ logger.warn(`No Graphviz binaries found on PATH, use graphviz wasm`);
38
+ services.shared.lsp.Connection?.window.showWarningMessage(
39
+ "No Graphviz binaries found on PATH, set path to binaries in settings."
40
+ );
49
41
  }
50
- return;
42
+ layouter.changePort(new GraphvizWasmAdapter());
43
+ logger.info("use graphviz wasm");
44
+ } catch (error) {
45
+ logger.error("Failed to update configuration", { error });
51
46
  }
52
- logger.warn("Unexpected configuration update: {update}", { update });
53
47
  });
54
48
  return layouter;
55
49
  }
@@ -18,6 +18,7 @@ export interface LikeC4Views {
18
18
  layoutView(viewId: ViewId, projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<GraphvizOut | null>;
19
19
  diagrams(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<DiagramView>>;
20
20
  viewsAsGraphvizOut(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<GraphvizSvgOut>>;
21
+ openView(viewId: ViewId, projectId?: ProjectId | undefined): Promise<void>;
21
22
  }
22
23
  export declare class DefaultLikeC4Views implements LikeC4Views {
23
24
  private services;
@@ -31,5 +32,9 @@ export declare class DefaultLikeC4Views implements LikeC4Views {
31
32
  layoutView(viewId: ViewId, projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<GraphvizOut | null>;
32
33
  diagrams(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<DiagramView>>;
33
34
  viewsAsGraphvizOut(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<GraphvizSvgOut>>;
35
+ /**
36
+ * Open a view in the preview panel.
37
+ */
38
+ openView(viewId: ViewId, projectId: ProjectId): Promise<void>;
34
39
  }
35
40
  export {};
@@ -53,7 +53,6 @@ export class DefaultLikeC4Views {
53
53
  },
54
54
  onError: (task, error) => {
55
55
  logger.warn(`Fail layout view ${task.view.id}`, { error });
56
- this.cache.delete(task.view);
57
56
  }
58
57
  });
59
58
  }
@@ -135,6 +134,12 @@ export class DefaultLikeC4Views {
135
134
  cache.set(KEY, succeed);
136
135
  return succeed;
137
136
  }
137
+ /**
138
+ * Open a view in the preview panel.
139
+ */
140
+ async openView(viewId, projectId) {
141
+ await this.services.Rpc.openView({ viewId, projectId });
142
+ }
138
143
  // async overviewGraph(): Promise<OverviewGraph> {
139
144
  // const KEY = 'OverviewGraph'
140
145
  // const cache = this.services.ValidatedWorkspaceCache as WorkspaceCache<string, OverviewGraph>
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.32.2",
4
+ "version": "1.34.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -20,7 +20,7 @@
20
20
  "directory": "packages/language-server"
21
21
  },
22
22
  "engines": {
23
- "node": ">=20.19.1"
23
+ "node": ">=20.19.3"
24
24
  },
25
25
  "engineStrict": true,
26
26
  "type": "module",
@@ -89,33 +89,33 @@
89
89
  "access": "public"
90
90
  },
91
91
  "dependencies": {
92
- "@hpcc-js/wasm-graphviz": "1.7.0"
92
+ "@hpcc-js/wasm-graphviz": "1.8.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/chroma-js": "^3.1.1",
96
- "@modelcontextprotocol/sdk": "^1.12.1",
97
- "@msgpack/msgpack": "^3.1.1",
96
+ "@modelcontextprotocol/sdk": "^1.13.2",
97
+ "@msgpack/msgpack": "^3.1.2",
98
98
  "@smithy/util-base64": "^4.0.0",
99
- "@types/express": "^5.0.2",
100
- "@types/node": "~20.19.0",
99
+ "@types/express": "^5.0.3",
100
+ "@types/node": "~20.19.1",
101
101
  "@types/picomatch": "^4.0.0",
102
102
  "@types/which": "^3.0.4",
103
103
  "chroma-js": "^3.1.2",
104
104
  "esm-env": "^1.2.2",
105
105
  "express": "^5.1.0",
106
106
  "fast-equals": "^5.2.2",
107
- "fdir": "^6.4.4",
107
+ "fdir": "6.4.6",
108
108
  "indent-string": "^5.0.0",
109
109
  "json5": "^2.2.3",
110
110
  "langium": "3.5.0",
111
111
  "langium-cli": "3.5.0",
112
112
  "natural-compare-lite": "^1.4.0",
113
- "p-debounce": "^4.0.0",
114
- "p-queue": "^8.1.0",
115
- "p-timeout": "^6.1.4",
113
+ "p-debounce": "4.0.0",
114
+ "p-queue": "8.1.0",
115
+ "p-timeout": "6.1.4",
116
116
  "picomatch": "^4.0.2",
117
117
  "pretty-ms": "^9.2.0",
118
- "remeda": "^2.23.0",
118
+ "remeda": "^2.23.1",
119
119
  "strip-indent": "^4.0.0",
120
120
  "tsx": "4.19.4",
121
121
  "turbo": "2.5.4",
@@ -124,19 +124,19 @@
124
124
  "ufo": "^1.6.1",
125
125
  "unbuild": "3.5.0",
126
126
  "valibot": "^1.1.0",
127
- "vitest": "3.2.3",
127
+ "vitest": "3.2.4",
128
128
  "vscode-jsonrpc": "8.2.0",
129
129
  "vscode-languageserver": "9.0.1",
130
130
  "vscode-languageserver-protocol": "3.17.5",
131
131
  "vscode-languageserver-types": "3.17.5",
132
132
  "vscode-uri": "3.1.0",
133
133
  "which": "^5.0.0",
134
- "zod": "^3.25.64",
135
- "@likec4/icons": "1.32.2",
136
- "@likec4/core": "1.32.2",
137
- "@likec4/tsconfig": "1.32.2",
138
- "@likec4/layouts": "1.32.2",
139
- "@likec4/log": "1.32.2"
134
+ "zod": "3.25.67",
135
+ "@likec4/core": "1.34.0",
136
+ "@likec4/icons": "1.34.0",
137
+ "@likec4/layouts": "1.34.0",
138
+ "@likec4/log": "1.34.0",
139
+ "@likec4/tsconfig": "1.34.0"
140
140
  },
141
141
  "scripts": {
142
142
  "typecheck": "tsc -b --verbose",
@@ -148,6 +148,7 @@
148
148
  "generate": "langium generate && tsx scripts/generate-icons.ts",
149
149
  "dev": "run-p \"watch:*\"",
150
150
  "lint": "run -T eslint src/ --fix",
151
+ "lint:package": "pnpx publint ./package.tgz",
151
152
  "clean": "pnpm rimraf dist contrib lib src/generated src/generated-lib",
152
153
  "test": "vitest run --no-isolate",
153
154
  "test-dbg": "vitest run --no-isolate -t formating",
@@ -1,96 +0,0 @@
1
- import { type ProjectId } from '@likec4/core';
2
- import { z } from 'zod';
3
- import type { LikeC4Services } from '../module';
4
- export declare namespace LikeC4MCPTools {
5
- const instructions = "This server provides access to LikeC4 model.\n\nKey capabilities:\n- List all available LikeC4 projects in the workspace\n- Search for LikeC4 project and return its summary, that includes specifications, all elements and views\n- Search for LikeC4 element by title\n- Read details about LikeC4 element by id\n- Read details about LikeC4 view by id\n\n";
6
- const listProjects: {
7
- name: string;
8
- description: string;
9
- };
10
- const readProjectSummary: {
11
- name: string;
12
- description: string;
13
- paramsSchema: {
14
- project: z.ZodOptional<z.ZodString>;
15
- };
16
- };
17
- const searchElement: {
18
- name: string;
19
- description: string;
20
- paramsSchema: {
21
- search: z.ZodString;
22
- };
23
- };
24
- const readElement: {
25
- name: string;
26
- description: string;
27
- paramsSchema: {
28
- id: z.ZodString;
29
- project: z.ZodOptional<z.ZodString>;
30
- };
31
- };
32
- const readView: {
33
- name: string;
34
- description: string;
35
- paramsSchema: {
36
- id: z.ZodString;
37
- project: z.ZodOptional<z.ZodString>;
38
- };
39
- };
40
- }
41
- export interface LikeC4MCPTools {
42
- listProjects(): Promise<string>;
43
- /**
44
- * Searches for LikeC4 project and returns its summary, specifications, elements and views
45
- *
46
- * @param project Project name (optional, will use default project if not specified)
47
- */
48
- readProjectSummary(project?: string): Promise<string>;
49
- /**
50
- * Searches for LikeC4 elements that have the search string in their names
51
- * Can be used to resolve projects for further requests (like read-element or read-project-summary)
52
- *
53
- * @param params.search non-empty string
54
- */
55
- searchElement(params: {
56
- search: string;
57
- }): Promise<string>;
58
- /**
59
- * Read details about LikeC4 element.
60
- *
61
- * @param params.id Element id (FQN)
62
- * @param params.project Project name (optional, will use default project if not specified)
63
- */
64
- readElement(params: {
65
- id: string;
66
- project?: string | undefined;
67
- }): Promise<string>;
68
- /**
69
- * Read details about LikeC4 view.
70
- *
71
- * @param params.id View id (FQN)
72
- * @param params.project Project name (optional, will use default project if not specified)
73
- */
74
- readView(params: {
75
- id: string;
76
- project?: string | undefined;
77
- }): Promise<string>;
78
- }
79
- export declare class DefaultLikeC4MCPTools implements LikeC4MCPTools {
80
- private services;
81
- private readonly languageServices;
82
- constructor(services: LikeC4Services);
83
- listProjects(): Promise<string>;
84
- readProjectSummary(_project?: ProjectId): Promise<string>;
85
- searchElement(params: {
86
- search: string;
87
- }): Promise<string>;
88
- readElement(params: {
89
- id: string;
90
- project?: string;
91
- }): Promise<string>;
92
- readView(params: {
93
- id: string;
94
- project?: string;
95
- }): Promise<string>;
96
- }
@@ -1,290 +0,0 @@
1
- import { _type } from "@likec4/core";
2
- import { loggable } from "@likec4/log";
3
- import { flatMap } from "remeda";
4
- import stripIndent from "strip-indent";
5
- import { z } from "zod";
6
- import { logger as mainLogger } from "../logger.js";
7
- import { toSingleLine } from "../model/parser/Base.js";
8
- import { ProjectsManager } from "../workspace/ProjectsManager.js";
9
- import { elementResource, modelViewResource } from "./utils.js";
10
- const logger = mainLogger.getChild("LikeC4MCPServices");
11
- function singleLine(str) {
12
- const res = toSingleLine(str)?.replaceAll('"', "'");
13
- return res ? `"${res}"` : "null";
14
- }
15
- function outputEach(iterator, ifEmpty, output) {
16
- const items = [...iterator];
17
- if (items.length === 0) {
18
- return [ifEmpty];
19
- }
20
- return flatMap(items, output);
21
- }
22
- export var LikeC4MCPTools;
23
- ((LikeC4MCPTools2) => {
24
- LikeC4MCPTools2.instructions = `This server provides access to LikeC4 model.
25
-
26
- Key capabilities:
27
- - List all available LikeC4 projects in the workspace
28
- - Search for LikeC4 project and return its summary, that includes specifications, all elements and views
29
- - Search for LikeC4 element by title
30
- - Read details about LikeC4 element by id
31
- - Read details about LikeC4 view by id
32
-
33
- `;
34
- LikeC4MCPTools2.listProjects = {
35
- name: "list-projects",
36
- description: "Lists all available LikeC4 projects in the workspace"
37
- };
38
- LikeC4MCPTools2.readProjectSummary = {
39
- name: "read-project-summary",
40
- description: stripIndent(`
41
- Searches for LikeC4 project and returns its summary, specifications, elements and views
42
-
43
- Args:
44
- project: Project name
45
- `),
46
- paramsSchema: {
47
- project: z.string().optional()
48
- }
49
- };
50
- LikeC4MCPTools2.searchElement = {
51
- name: "search-element",
52
- description: stripIndent(`
53
- Search for LikeC4 elements that have the search string in their names
54
- Can be used to resolve projects for further requests (like read-element or read-project-summary)
55
-
56
- Args:
57
- search: non-empty string
58
- `),
59
- paramsSchema: {
60
- search: z.string()
61
- }
62
- };
63
- LikeC4MCPTools2.readElement = {
64
- name: "read-element",
65
- description: stripIndent(`
66
- Read details about a LikeC4 element
67
-
68
- Args:
69
- id: Element id (FQN)
70
- project: Project name (optional, will use default project if not specified)
71
- `),
72
- paramsSchema: {
73
- id: z.string().min(1),
74
- project: z.string().optional()
75
- }
76
- };
77
- LikeC4MCPTools2.readView = {
78
- name: "read-view",
79
- description: stripIndent(`
80
- Read details about a LikeC4 view
81
-
82
- Args:
83
- id: View id
84
- project: Project name (optional, will use default project if not specified)
85
- `),
86
- paramsSchema: {
87
- id: z.string().min(1),
88
- project: z.string().optional()
89
- }
90
- };
91
- })(LikeC4MCPTools || (LikeC4MCPTools = {}));
92
- export class DefaultLikeC4MCPTools {
93
- constructor(services) {
94
- this.services = services;
95
- this.languageServices = services.likec4.LanguageServices;
96
- }
97
- languageServices;
98
- async listProjects() {
99
- const projects = await this.languageServices.projects();
100
- const response = [];
101
- for (const project of projects) {
102
- if (!project.documents) {
103
- continue;
104
- }
105
- response.push(
106
- `<likec4project>`,
107
- `id: "${project.id}"`,
108
- `folder: ${project.folder.toString()}`,
109
- "sources:",
110
- ...project.documents.map((d) => `- ${d.toString()}`),
111
- ""
112
- );
113
- try {
114
- const model = await this.languageServices.computedModel(project.id);
115
- const elements = [...model.elements()];
116
- if (elements.length > 0) {
117
- response.push(
118
- "elements:",
119
- ...elements.flatMap((el) => [
120
- `- id: ${el.id}`,
121
- ` kind: ${el.kind}`,
122
- ` title: ${singleLine(el.title)}`,
123
- ""
124
- ]),
125
- ""
126
- );
127
- }
128
- const views = [...model.views()];
129
- if (views.length > 0) {
130
- response.push(
131
- "views:",
132
- ...views.flatMap((v) => [
133
- `- id: ${v.id}`,
134
- ` title: ${singleLine(v.title)}`,
135
- ""
136
- ]),
137
- ""
138
- );
139
- }
140
- } catch (error) {
141
- logger.error(loggable(error));
142
- }
143
- response.push(`</likec4project>`);
144
- }
145
- if (response.length === 0) {
146
- response.push(
147
- `<likec4project>`,
148
- `id: "default"`,
149
- `folder: ${this.languageServices.workspaceUri.toString()}`,
150
- `</likec4project>`
151
- );
152
- }
153
- return response.join("\n");
154
- }
155
- async readProjectSummary(_project) {
156
- const projectId = _project ?? ProjectsManager.DefaultProjectId;
157
- const project = this.languageServices.projects().find((p) => p.id === projectId);
158
- if (!project) {
159
- return "Project not found";
160
- }
161
- const model = await this.languageServices.computedModel(project.id);
162
- const response = [
163
- `project: "${project.id}"`,
164
- `folder: ${project.folder.toString()}`
165
- ];
166
- if (project.documents) {
167
- response.push(
168
- "sources:",
169
- ...project.documents.map((d) => `- ${d.toString()}`),
170
- ""
171
- );
172
- }
173
- response.push("<specifications>");
174
- const elementKinds = Object.keys(model.$model.specification.elements);
175
- if (elementKinds.length > 0) {
176
- response.push(
177
- "element kinds:",
178
- ...elementKinds.map((kind) => `- ${kind}`),
179
- ""
180
- );
181
- }
182
- const relationshipKinds = Object.keys(model.$model.specification.relationships);
183
- if (relationshipKinds.length > 0) {
184
- response.push(
185
- "relationship kinds:",
186
- ...relationshipKinds.map((kind) => `- ${kind}`),
187
- ""
188
- );
189
- }
190
- const deploymentKinds = Object.keys(model.$model.specification.deployments);
191
- if (deploymentKinds.length > 0) {
192
- response.push(
193
- "deployment node kinds:",
194
- ...deploymentKinds.map((kind) => `- ${kind}`),
195
- ""
196
- );
197
- }
198
- const tags = model.tags;
199
- if (tags.length > 0) {
200
- response.push(
201
- "tags:",
202
- ...tags.map((t) => `- ${t}`),
203
- ""
204
- );
205
- }
206
- response.push(
207
- "</specifications>",
208
- ""
209
- );
210
- response.push(
211
- "<elements>",
212
- ...outputEach(model.elements(), "No elements", (el) => [
213
- `- id: ${el.id}`,
214
- ...el.parent ? [
215
- ` parentId: ${el.parent.id}`
216
- ] : [],
217
- ` kind: ${el.kind}`,
218
- ` shape: ${el.shape}`,
219
- ` title: ${singleLine(el.title)}`,
220
- ` description: ${singleLine(el.description)}`,
221
- ` technology: ${singleLine(el.technology)}`,
222
- ` tags: ${JSON.stringify(el.tags)}`,
223
- ""
224
- ]),
225
- "</elements>",
226
- "<views>",
227
- ...outputEach(model.views(), "No views", (v) => [
228
- `- id: ${v.id}`,
229
- ` viewType: ${v[_type]}`,
230
- ` title: ${singleLine(v.title)}`,
231
- ""
232
- ]),
233
- "</views>"
234
- );
235
- return response.join("\n");
236
- }
237
- async searchElement(params) {
238
- const search = params.search.toLowerCase();
239
- const found = [];
240
- for (const project of this.languageServices.projects()) {
241
- try {
242
- const model = await this.languageServices.computedModel(project.id);
243
- const elements = [...model.elements()].filter((el) => el.title.toLowerCase().includes(search));
244
- if (elements.length > 0) {
245
- found.push(
246
- "<project>",
247
- `project: "${project.id}"`,
248
- "found:",
249
- ...elements.flatMap((el) => [
250
- `- id: ${el.id}`,
251
- ` kind: ${el.kind}`,
252
- ` title: ${singleLine(el.title)}`,
253
- ""
254
- ]),
255
- "</project>"
256
- );
257
- }
258
- } catch (error) {
259
- logger.error(loggable(error));
260
- }
261
- }
262
- return found.length > 0 ? found.join("\n") : "No elements with this name found";
263
- }
264
- async readElement(params) {
265
- const projectId = params.project ?? ProjectsManager.DefaultProjectId;
266
- const project = this.languageServices.projects().find((p) => p.id === projectId);
267
- if (!project) {
268
- return `Project "${projectId}" not found`;
269
- }
270
- const model = await this.languageServices.computedModel(project.id);
271
- const element = model.findElement(params.id);
272
- if (!element) {
273
- return `Element "${params.id}" not found in project "${projectId}"`;
274
- }
275
- return JSON.stringify(elementResource(this.languageServices, element, project.id));
276
- }
277
- async readView(params) {
278
- const projectId = params.project ?? ProjectsManager.DefaultProjectId;
279
- const project = this.languageServices.projects().find((p) => p.id === projectId);
280
- if (!project) {
281
- return `Project "${projectId}" not found`;
282
- }
283
- const model = await this.languageServices.computedModel(project.id);
284
- const view = model.findView(params.id);
285
- if (!view) {
286
- return `View "${params.id}" not found in project "${projectId}"`;
287
- }
288
- return JSON.stringify(modelViewResource(this.languageServices, view, project.id));
289
- }
290
- }