@likec4/language-server 1.48.0 → 1.50.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 (75) hide show
  1. package/browser/package.json +2 -2
  2. package/browser-worker/package.json +2 -2
  3. package/dist/THIRD-PARTY-LICENSES.md +178 -0
  4. package/dist/_chunks/ConfigurableLayouter.mjs +1 -1956
  5. package/dist/_chunks/LikeC4FileSystem.mjs +3 -0
  6. package/dist/_chunks/WithMCPServer.mjs +1154 -0
  7. package/dist/_chunks/libs/@msgpack/msgpack.mjs +1 -805
  8. package/dist/_chunks/libs/eventemitter3.mjs +1 -243
  9. package/dist/_chunks/libs/fast-equals.mjs +1 -446
  10. package/dist/_chunks/libs/p-queue.mjs +1 -449
  11. package/dist/_chunks/libs/parse-ms.mjs +1 -36
  12. package/dist/_chunks/libs/picomatch.mjs +1 -1673
  13. package/dist/_chunks/libs/pretty-ms.mjs +1 -80
  14. package/dist/_chunks/libs/remeda.mjs +2 -482
  15. package/dist/_chunks/libs/strip-indent.mjs +1 -15
  16. package/dist/_chunks/libs/ufo.mjs +1 -166
  17. package/dist/_chunks/likec4lib.mjs +2 -9
  18. package/dist/_chunks/{LikeC4LanguageServices.d.mts → module.d.mts} +2101 -732
  19. package/dist/_chunks/module.mjs +34 -28
  20. package/dist/_chunks/noop.mjs +1 -0
  21. package/dist/_chunks/protocol.d.mts +6 -2
  22. package/dist/_chunks/rolldown-runtime.mjs +1 -42
  23. package/dist/_chunks/utils.mjs +1 -0
  24. package/dist/_chunks/workspace.mjs +1 -0
  25. package/dist/browser/index.d.mts +15 -0
  26. package/dist/browser/index.mjs +1 -0
  27. package/dist/browser/worker.mjs +1 -0
  28. package/dist/bundled.d.mts +1 -2
  29. package/dist/bundled.mjs +1 -51
  30. package/dist/filesystem/index.d.mts +2 -3
  31. package/dist/filesystem/index.mjs +1 -3
  32. package/dist/index.d.mts +1 -2
  33. package/dist/index.mjs +1 -48
  34. package/dist/likec4lib.d.mts +10 -3
  35. package/dist/likec4lib.mjs +1 -4
  36. package/dist/mcp/index.d.mts +2 -3
  37. package/dist/mcp/index.mjs +1 -3
  38. package/dist/module.d.mts +2 -3
  39. package/dist/module.mjs +1 -3
  40. package/dist/protocol.mjs +1 -3
  41. package/filesystem/package.json +4 -0
  42. package/mcp/package.json +4 -0
  43. package/module/package.json +4 -0
  44. package/package.json +79 -56
  45. package/LICENSE +0 -21
  46. package/dist/LikeC4LanguageServices.d.mts +0 -4
  47. package/dist/LikeC4LanguageServices.mjs +0 -3
  48. package/dist/_chunks/LikeC4LanguageServices.mjs +0 -725
  49. package/dist/_chunks/ast.d.mts +0 -1444
  50. package/dist/_chunks/ast.mjs +0 -2375
  51. package/dist/_chunks/ast2.mjs +0 -176
  52. package/dist/_chunks/filesystem.mjs +0 -58
  53. package/dist/_chunks/grammar.mjs +0 -8
  54. package/dist/_chunks/icons.mjs +0 -5211
  55. package/dist/_chunks/libs/@hono/node-server.mjs +0 -436
  56. package/dist/_chunks/libs/hono.mjs +0 -1829
  57. package/dist/_chunks/mcp.mjs +0 -33
  58. package/dist/_chunks/module2.mjs +0 -6576
  59. package/dist/_chunks/protocol.mjs +0 -78
  60. package/dist/ast.d.mts +0 -4
  61. package/dist/ast.mjs +0 -4
  62. package/dist/browser-worker.mjs +0 -6
  63. package/dist/browser.d.mts +0 -11
  64. package/dist/browser.mjs +0 -27
  65. package/dist/common-exports.d.mts +0 -4
  66. package/dist/common-exports.mjs +0 -5
  67. package/dist/generated/ast.d.mts +0 -2
  68. package/dist/generated/ast.mjs +0 -3
  69. package/dist/generated/grammar.d.mts +0 -6
  70. package/dist/generated/grammar.mjs +0 -3
  71. package/dist/generated/module.d.mts +0 -14
  72. package/dist/generated/module.mjs +0 -3
  73. package/dist/generated-lib/icons.d.mts +0 -4
  74. package/dist/generated-lib/icons.mjs +0 -3
  75. /package/dist/{browser-worker.d.mts → browser/worker.d.mts} +0 -0
@@ -0,0 +1,1154 @@
1
+ import{Er as e}from"./utils.mjs";import{h as t}from"./libs/remeda.mjs";import{n}from"./workspace.mjs";import{ifilter as r,invariant as i,isSameHierarchy as a}from"@likec4/core/utils";import{loggable as o}from"@likec4/log";import{invariant as s}from"@likec4/core";import{isDeploymentNodeModel as c,modelConnection as l}from"@likec4/core/model";import{URI as u}from"vscode-uri";import{McpServer as d}from"@modelcontextprotocol/sdk/server/mcp.js";import*as f from"zod/v3";import{StdioServerTransport as p}from"@modelcontextprotocol/sdk/server/stdio.js";import{MemoryEventStore as m,StreamableHTTPTransport as h}from"@hono/mcp";import{serve as g}from"@hono/node-server";import{Hono as _}from"hono";import{cors as v}from"hono/cors";const y=e.getChild(`mcp`);function likec4Tool(e,t){let{name:n,description:r,...i}=e;return e=>[n,{description:r?.trim()??``,...i},mkcallTool(n,e,t)]}function mkcallTool(e,t,n){let r=n.bind(null,t);return(async function(t,n){y.debug(`Calling tool {name}, args: {args}`,{name:e,args:t});try{let e=await r.call(null,t,n);return typeof e==`string`?{content:[{type:`text`,text:e}]}:{content:[{type:`text`,text:JSON.stringify(e)}],structuredContent:e}}catch(t){return y.error(`Tool ${e} failed`,{err:t}),{content:[{type:`text`,text:o(t)}],isError:!0}}})}var b=`1.50.0`;const x=f.object({name:f.string().describe(`Project identifier`),title:f.string().optional().describe(`Human-readable project title`),contactPerson:f.string().optional().describe(`Maintainer contact information`),metadata:f.record(f.string(),f.unknown()).optional().describe(`Custom project metadata as key-value pairs`),extends:f.union([f.string(),f.array(f.string())]).optional().describe(`Style inheritance paths`),exclude:f.array(f.string()).optional().describe(`File exclusion patterns`),include:f.object({paths:f.array(f.string()).describe(`Include paths`),maxDepth:f.number().describe(`Maximum directory depth`),fileThreshold:f.number().describe(`File threshold`)}).optional().describe(`Include configuration`),manualLayouts:f.object({outDir:f.string().describe(`Output directory for manual layouts`)}).optional().describe(`Manual layouts configuration`),styles:f.object({hasTheme:f.boolean().describe(`Whether theme customization is defined`),hasDefaults:f.boolean().describe(`Whether default style values are defined`),hasCustomCss:f.boolean().describe(`Whether custom CSS is defined`)}).optional().describe(`Simplified styles configuration (boolean flags)`)});function serializeConfig(e){let t={name:e.name};return e.title!=null&&(t.title=e.title),e.contactPerson!=null&&(t.contactPerson=e.contactPerson),e.metadata&&(t.metadata=e.metadata),e.extends&&(t.extends=e.extends),e.exclude&&(t.exclude=e.exclude),e.include&&(t.include={paths:e.include.paths||[],maxDepth:e.include.maxDepth??3,fileThreshold:e.include.fileThreshold??30}),e.manualLayouts&&(t.manualLayouts={outDir:e.manualLayouts.outDir??`.likec4`}),e.styles&&(t.styles={hasTheme:!!e.styles.theme,hasDefaults:!!e.styles.defaults,hasCustomCss:!!e.styles.customCss}),t}const S=f.object({id:f.string().describe(`Element id (FQN)`),name:f.string().describe(`Element name`),kind:f.string().describe(`Element kind`),title:f.string(),tags:f.array(f.string()),metadata:f.record(f.union([f.string(),f.array(f.string())])),includedInViews:f.array(f.object({id:f.string().describe(`View id`),title:f.string().describe(`View title`),type:f.enum([`element`,`deployment`,`dynamic`]).describe(`View type`)})).describe(`Views that include this element`)});function serializeElement(e){return{id:e.id,name:e.name,kind:e.kind,title:e.title,tags:[...e.tags],metadata:e.getMetadata(),includedInViews:includedInViews(e.views())}}function traverseGraph(e,t,n,r,i,a){s(e.findElement(t),`Element "${t}" not found`);let o=new Set,c={},l=0,u=!1,d=[{elementId:t,depth:0}];for(;d.length>0;){let{elementId:t,depth:s}=d.shift();if(s>i||o.has(t))continue;if(o.size>=a){u=!0;break}let f=e.findElement(t);if(!f)continue;o.add(t),l=Math.max(l,s);let p=(n===`incoming`?[...f.incoming(r)]:[...f.outgoing(r)]).map(e=>{let t={elementId:n===`incoming`?e.source.id:e.target.id};return e.title&&(t.relationshipLabel=e.title),e.technology&&(t.technology=e.technology),t});c[t]={...serializeElement(f),neighbors:p,depth:s};for(let e of p)o.has(e.elementId)||d.push({elementId:e.elementId,depth:s+1})}for(let e of Object.values(c))e.neighbors=e.neighbors.filter(e=>e.elementId in c);return{target:t,totalNodes:o.size,maxDepth:l,truncated:u,nodes:c}}const C=f.object({path:f.string().describe(`Path to the file`),range:f.object({start:f.object({line:f.number(),character:f.number()}),end:f.object({line:f.number(),character:f.number()})}).describe(`Range in the file`)}).nullable(),w=f.string().refine(e=>!0).optional().default(n.DefaultProjectId).describe(`Project id (optional, will use "default" if not specified)`),T=f.array(f.object({id:f.string().describe(`View id`),title:f.string().describe(`View title`),type:f.enum([`element`,`deployment`,`dynamic`]).describe(`View type`)})),includedInViews=e=>[...e].map(e=>({id:e.id,title:e.titleOrId,type:e.$view._type})),mkLocate=(e,t)=>n=>{try{let r=e.locate({projectId:t,...n});return r?{path:u.parse(r.uri).fsPath,range:r.range}:null}catch(e){return y.debug(`Failed to locate {params}`,{error:e,params:n}),null}},E=S.extend({description:f.string().nullable().describe(`Element description`),technology:f.string().nullable().describe(`Element technology`),shape:f.string().describe(`Rendered shape`),color:f.string().describe(`Rendered color`),children:f.array(f.string()).describe(`Direct child element ids`),incomingCount:f.number().describe(`Number of incoming relationships`),outgoingCount:f.number().describe(`Number of outgoing relationships`)}),D=likec4Tool({name:`batch-read-elements`,description:`
2
+ Read details of multiple elements in a single call, reducing round-trips.
3
+ Returns a compact summary for each element including metadata, description, technology, shape, children, and relationship counts.
4
+
5
+ Request:
6
+ - ids: string[] — array of element ids (FQNs) to read (max 50)
7
+ - project: string (optional) — project id. Defaults to "default" if omitted.
8
+
9
+ Response (JSON object):
10
+ - elements: Array of element details, each with:
11
+ - id: string — element id (FQN)
12
+ - name: string — element name
13
+ - kind: string — element kind
14
+ - title: string — human-readable title
15
+ - description: string|null — optional description
16
+ - technology: string|null — optional technology
17
+ - tags: string[] — assigned tags
18
+ - metadata: Record<string, string | string[]> — element metadata
19
+ - shape: string — rendered shape
20
+ - color: string — rendered color
21
+ - children: string[] — direct child element ids
22
+ - incomingCount: number — number of incoming relationships
23
+ - outgoingCount: number — number of outgoing relationships
24
+ - includedInViews: View[] — views that include this element
25
+ - notFound: string[] — ids that were not found in the project
26
+
27
+ View (object) fields:
28
+ - id: string — view identifier
29
+ - title: string — view title
30
+ - type: "element" | "deployment" | "dynamic"
31
+
32
+ Notes:
33
+ - Read-only, idempotent, no side effects.
34
+ - Safe to call repeatedly.
35
+ - Maximum 50 element ids per call.
36
+ - Elements not found are listed in notFound array (not an error).
37
+ - More efficient than multiple read-element calls when you need summary data for many elements.
38
+
39
+ Example response:
40
+ {
41
+ "elements": [
42
+ {
43
+ "id": "shop.frontend",
44
+ "name": "frontend",
45
+ "kind": "container",
46
+ "title": "Frontend",
47
+ "description": "User-facing web app",
48
+ "technology": "React",
49
+ "tags": ["public"],
50
+ "metadata": { "owner": "web-team" },
51
+ "shape": "browser",
52
+ "color": "#2F80ED",
53
+ "children": ["shop.frontend.auth"],
54
+ "incomingCount": 2,
55
+ "outgoingCount": 3,
56
+ "includedInViews": [
57
+ { "id": "system-overview", "title": "System Overview", "type": "element" }
58
+ ]
59
+ }
60
+ ],
61
+ "notFound": []
62
+ }
63
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Batch read elements`},inputSchema:{ids:f.array(f.string()).min(1).max(50).describe(`Array of element ids (FQNs) to read (max 50)`),project:w},outputSchema:{elements:f.array(E),notFound:f.array(f.string()).describe(`Element ids that were not found`)}},async(e,t)=>{s(t.ids.length<=50,`Maximum 50 element ids per call`);let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=[],a=[];for(let e of t.ids){let t=r.findElement(e);if(!t){a.push(e);continue}i.push({...serializeElement(t),description:t.description.text,technology:t.technology,shape:t.shape,color:t.color,children:[...t.children()].map(e=>e.id),incomingCount:t.allIncoming.size,outgoingCount:t.allOutgoing.size})}return{elements:i,notFound:a}}),O=f.object({id:f.string(),kind:f.string(),title:f.string(),description:f.string().nullable(),technology:f.string().nullable(),shape:f.string(),color:f.string()}),k=f.object({element1:O,element2:O,propertyDiffs:f.array(f.object({property:f.string().describe(`Property name`),element1Value:f.string().nullable().describe(`Value in element1`),element2Value:f.string().nullable().describe(`Value in element2`)})).describe(`Properties that differ between the two elements`),tags:f.object({onlyInElement1:f.array(f.string()).describe(`Tags present only in element1`),onlyInElement2:f.array(f.string()).describe(`Tags present only in element2`),common:f.array(f.string()).describe(`Tags present in both elements`)}),metadata:f.object({onlyInElement1:f.record(f.union([f.string(),f.array(f.string())])).describe(`Metadata keys only in element1`),onlyInElement2:f.record(f.union([f.string(),f.array(f.string())])).describe(`Metadata keys only in element2`),different:f.array(f.object({key:f.string(),element1Value:f.union([f.string(),f.array(f.string())]),element2Value:f.union([f.string(),f.array(f.string())])})).describe(`Metadata keys present in both but with different values`),common:f.record(f.union([f.string(),f.array(f.string())])).describe(`Metadata keys with identical values in both`)}),relationships:f.object({incomingOnlyElement1:f.number().describe(`Count of unique source elements sending to element1 only`),incomingOnlyElement2:f.number().describe(`Count of unique source elements sending to element2 only`),incomingShared:f.number().describe(`Count of unique source elements sending to both`),outgoingOnlyElement1:f.number().describe(`Count of unique target elements receiving from element1 only`),outgoingOnlyElement2:f.number().describe(`Count of unique target elements receiving from element2 only`),outgoingShared:f.number().describe(`Count of unique target elements receiving from both`)})}),A=likec4Tool({name:`element-diff`,description:`
64
+ Compare two elements side-by-side, showing differences in properties, tags, metadata, and relationships.
65
+
66
+ Request:
67
+ - element1Id: string — first element id (FQN)
68
+ - element2Id: string — second element id (FQN)
69
+ - project: string (optional) — project id. Defaults to "default" if omitted.
70
+
71
+ Response (JSON object):
72
+ - element1: object — snapshot of first element (id, kind, title, description, technology, shape, color)
73
+ - element2: object — snapshot of second element
74
+ - propertyDiffs: Array of { property, element1Value, element2Value } — properties that differ
75
+ - tags: object
76
+ - onlyInElement1: string[] — tags only in element1
77
+ - onlyInElement2: string[] — tags only in element2
78
+ - common: string[] — tags in both
79
+ - metadata: object
80
+ - onlyInElement1: Record — metadata keys only in element1
81
+ - onlyInElement2: Record — metadata keys only in element2
82
+ - different: Array of { key, element1Value, element2Value } — keys present in both but with different values
83
+ - common: Record — metadata keys with identical values in both
84
+ - relationships: object — relationship count comparison
85
+ - incomingOnlyElement1/incomingOnlyElement2/incomingShared
86
+ - outgoingOnlyElement1/outgoingOnlyElement2/outgoingShared
87
+
88
+ Notes:
89
+ - Read-only, idempotent, no side effects.
90
+ - Safe to call repeatedly.
91
+ - Both elements must exist in the same project.
92
+ - Useful for comparing similar nodes to understand why they have different configurations.
93
+
94
+ Example response:
95
+ {
96
+ "element1": { "id": "planner.nodeA", "kind": "cgf-node", "title": "nodeA", ... },
97
+ "element2": { "id": "planner.nodeB", "kind": "cgf-node", "title": "nodeB", ... },
98
+ "propertyDiffs": [
99
+ { "property": "title", "element1Value": "nodeA :dwNodeTypeA", "element2Value": "nodeB :dwNodeTypeB" }
100
+ ],
101
+ "tags": {
102
+ "onlyInElement1": ["target_asil_qm"],
103
+ "onlyInElement2": ["target_asil_asil_b"],
104
+ "common": ["is_in_dag", "process_camera_master"]
105
+ },
106
+ "metadata": {
107
+ "onlyInElement1": {},
108
+ "onlyInElement2": {},
109
+ "different": [
110
+ { "key": "target_asil", "element1Value": "QM", "element2Value": "ASIL-B" }
111
+ ],
112
+ "common": { "host": "machine0" }
113
+ },
114
+ "relationships": {
115
+ "incomingOnlyElement1": 2,
116
+ "incomingOnlyElement2": 1,
117
+ "incomingShared": 3,
118
+ "outgoingOnlyElement1": 0,
119
+ "outgoingOnlyElement2": 1,
120
+ "outgoingShared": 2
121
+ }
122
+ }
123
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Compare two elements`},inputSchema:{element1Id:f.string().describe(`First element id (FQN)`),element2Id:f.string().describe(`Second element id (FQN)`),project:w},outputSchema:k.shape},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.element1Id);s(i,`Element "${t.element1Id}" not found in project "${n}"`);let a=r.findElement(t.element2Id);s(a,`Element "${t.element2Id}" not found in project "${n}"`);let o=[],c=[{name:`kind`,get:e=>e.kind},{name:`title`,get:e=>e.title},{name:`description`,get:e=>e.description.text},{name:`technology`,get:e=>e.technology},{name:`shape`,get:e=>e.shape},{name:`color`,get:e=>e.color}];for(let e of c){let t=e.get(i),n=e.get(a);t!==n&&o.push({property:e.name,element1Value:t,element2Value:n})}let l=new Set(i.tags),u=new Set(a.tags),d=[],f=[],p=[];for(let e of l)u.has(e)?d.push(e):f.push(e);for(let e of u)l.has(e)||p.push(e);let m=i.getMetadata(),h=a.getMetadata(),g=new Set([...Object.keys(m),...Object.keys(h)]),_={},v={},y=[],b={};for(let e of g){let t=m[e],n=h[e];t!==void 0&&n===void 0?_[e]=t:t===void 0&&n!==void 0?v[e]=n:t!==void 0&&n!==void 0&&(JSON.stringify(t)===JSON.stringify(n)?b[e]=t:y.push({key:e,element1Value:t,element2Value:n}))}let x=new Set([...i.incoming()].map(e=>e.source.id)),S=new Set([...a.incoming()].map(e=>e.source.id)),C=new Set([...i.outgoing()].map(e=>e.target.id)),w=new Set([...a.outgoing()].map(e=>e.target.id)),T=0,E=0,D=0;for(let e of x)S.has(e)?T++:E++;for(let e of S)x.has(e)||D++;let O=0,k=0,A=0;for(let e of C)w.has(e)?O++:k++;for(let e of w)C.has(e)||A++;return{element1:{id:i.id,kind:i.kind,title:i.title,description:i.description.text,technology:i.technology,shape:i.shape,color:i.color},element2:{id:a.id,kind:a.kind,title:a.title,description:a.description.text,technology:a.technology,shape:a.shape,color:a.color},propertyDiffs:o,tags:{onlyInElement1:f,onlyInElement2:p,common:d},metadata:{onlyInElement1:_,onlyInElement2:v,different:y,common:b},relationships:{incomingOnlyElement1:E,incomingOnlyElement2:D,incomingShared:T,outgoingOnlyElement1:k,outgoingOnlyElement2:A,outgoingShared:O}}}),j=likec4Tool({name:`find-relationship-paths`,description:`
124
+ Discover all paths (chains of relationships) between two elements, supporting multi-hop traversal.
125
+
126
+ Request:
127
+ - sourceId: string — source element FQN
128
+ - targetId: string — target element FQN
129
+ - maxDepth: number (optional, default: 3, max: 5) — maximum path length (number of hops)
130
+ - includeIndirect: boolean (optional, default: false) — include indirect (implied) relationships through nested elements
131
+ - project: string (optional) — project id. Defaults to "default" if omitted.
132
+
133
+ Algorithm:
134
+ - Uses breadth-first search (BFS) to find all paths
135
+ - Prevents cycles with visited set per path
136
+ - Paths are sorted by length (shortest first)
137
+ - Limited to 100 paths to avoid overwhelming responses
138
+
139
+ Response (JSON object):
140
+ - paths: Array of path objects, each with:
141
+ - length: number — number of hops in the path
142
+ - steps: Array<Step> — ordered sequence of relationships
143
+
144
+ Step (object) fields:
145
+ - source: string — source element FQN
146
+ - target: string — target element FQN
147
+ - relationship: object
148
+ - kind: string|null — relationship kind
149
+ - title: string|null — relationship title
150
+ - description: string|null — relationship description
151
+ - technology: string|null — relationship technology
152
+ - tags: string[] — relationship tags
153
+
154
+ Notes:
155
+ - Read-only, idempotent, no side effects.
156
+ - Safe to call repeatedly.
157
+ - Returns empty paths array if no paths exist.
158
+ - Rejects if source equals target.
159
+ - includeIndirect=false (default): only follows direct relationships on each element.
160
+ includeIndirect=true: also follows implied relationships through nested elements.
161
+ - maxDepth is capped at 5 to prevent excessive computation.
162
+ - Paths are discovered iteratively and sorted by length.
163
+
164
+ Example response:
165
+ {
166
+ "paths": [
167
+ {
168
+ "length": 1,
169
+ "steps": [
170
+ {
171
+ "source": "shop.frontend",
172
+ "target": "shop.backend",
173
+ "relationship": {
174
+ "kind": "uses",
175
+ "title": "Calls API",
176
+ "description": null,
177
+ "technology": "HTTPS",
178
+ "tags": []
179
+ }
180
+ }
181
+ ]
182
+ },
183
+ {
184
+ "length": 2,
185
+ "steps": [
186
+ {
187
+ "source": "shop.frontend",
188
+ "target": "shop.cache",
189
+ "relationship": {
190
+ "kind": "uses",
191
+ "title": "Reads from",
192
+ "description": null,
193
+ "technology": "Redis",
194
+ "tags": []
195
+ }
196
+ },
197
+ {
198
+ "source": "shop.cache",
199
+ "target": "shop.backend",
200
+ "relationship": {
201
+ "kind": "syncs-with",
202
+ "title": "Updates",
203
+ "description": null,
204
+ "technology": null,
205
+ "tags": []
206
+ }
207
+ }
208
+ ]
209
+ }
210
+ ]
211
+ }
212
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationship paths`},inputSchema:{sourceId:f.string().describe(`Source element FQN`),targetId:f.string().describe(`Target element FQN`),maxDepth:f.number().int().min(1).max(5).optional().default(3).describe(`Maximum path length (default: 3, max: 5)`),includeIndirect:f.boolean().optional().default(!1).describe(`Include indirect (implied) relationships through nested elements (default: false)`),project:w},outputSchema:{paths:f.array(f.object({length:f.number().describe(`Number of hops in the path`),steps:f.array(f.object({source:f.string().describe(`Source element FQN`),target:f.string().describe(`Target element FQN`),relationship:f.object({kind:f.string().nullable().describe(`Relationship kind`),title:f.string().nullable().describe(`Relationship title`),description:f.string().nullable().describe(`Relationship description`),technology:f.string().nullable().describe(`Relationship technology`),tags:f.array(f.string()).describe(`Relationship tags`)})})).describe(`Ordered sequence of relationships in the path`)}))}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.sourceId);s(i,`Source element "${t.sourceId}" not found in project "${n}"`);let a=r.findElement(t.targetId);s(a,`Target element "${t.targetId}" not found in project "${n}"`),s(i.id!==a.id,`Source and target must be different elements`);let o=t.maxDepth,c=t.includeIndirect?`all`:`direct`,l=[{elementId:i.id,path:[],visited:new Set([i.id])}],u=[];for(;l.length>0&&u.length<100;){let e=l.shift();if(e.path.length>=o)continue;let t=r.findElement(e.elementId);if(t)for(let n of t.outgoing(c)){let t=n.target.id;if(t===a.id){let t={source:n.source.id,target:n.target.id,relationship:{kind:n.kind,title:n.title,description:n.description.text,technology:n.technology,tags:[...n.tags]}};if(u.push({length:e.path.length+1,steps:[...e.path,t]}),u.length>=100)break;continue}if(e.visited.has(t))continue;let r={source:n.source.id,target:n.target.id,relationship:{kind:n.kind,title:n.title,description:n.description.text,technology:n.technology,tags:[...n.tags]}};l.push({elementId:t,path:[...e.path,r],visited:new Set([...e.visited,t])})}}return u.sort((e,t)=>e.length-t.length),{paths:u}}),M=f.object({id:f.string(),title:f.string(),kind:f.string()}),N=f.object({type:f.enum([`direct`,`indirect`]).describe(`Type of relationship, "direct" for direct relationships, "indirect" for relationships through nested elements`),source:M,target:M,kind:f.string().nullable().describe(`Relationship kind`),title:f.string().nullable().describe(`Relationship title`),description:f.string().nullable().describe(`Relationship description`),technology:f.string().nullable().describe(`Relationship technology`),tags:f.array(f.string()).describe(`Relationship tags`),includedInViews:T.describe(`Views that include this relationship`),sourceLocation:C}),P=likec4Tool({name:`find-relationships`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationships between two elements`},description:`
213
+ Find relationships between two LikeC4 elements within a project.
214
+
215
+ What it does:
216
+ - Finds both direct relationships (element1 ↔ element2) and indirect ones that arise via containment (e.g. via nested elements).
217
+ - Returns rich metadata for each relationship and where it appears in views.
218
+
219
+ Inputs:
220
+ - element1: string — Element ID (FQN)
221
+ - element2: string — Element ID (FQN)
222
+ - project: string (optional, defaults to "default") — Project id
223
+
224
+ Output:
225
+ - found: Relationship[]
226
+
227
+ Relationship (object) fields:
228
+ - type: "direct" | "indirect" — direct is between the specified endpoints; indirect is via nested elements
229
+ - source: Endpoint
230
+ - target: Endpoint
231
+ - kind: string|null — relationship kind from the model
232
+ - title: string|null — relationship title if provided
233
+ - description: string|null — relationship description text
234
+ - technology: string|null — relationship technology
235
+ - tags: string[] — relationship tags
236
+ - includedInViews: View[] — views where this relationship appears
237
+ - sourceLocation: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null
238
+
239
+ Endpoint (object) fields:
240
+ - id: string — Element ID (FQN)
241
+ - title: string — element title
242
+ - kind: string — element kind
243
+
244
+ View (object) fields:
245
+ - id: string — view identifier
246
+ - title: string — view title
247
+ - type: "element" | "deployment" | "dynamic"
248
+
249
+ Notes:
250
+ - Read-only, idempotent; does not mutate the model. May trigger UI navigation in supporting clients.
251
+ - The order of results is not guaranteed.
252
+
253
+ Example:
254
+ Request:
255
+ {
256
+ "element1": "shop.frontend",
257
+ "element2": "shop.backend",
258
+ "project": "default"
259
+ }
260
+
261
+ Response:
262
+ {
263
+ "found": [
264
+ {
265
+ "type": "direct",
266
+ "source": { "id": "shop.frontend", "title": "Frontend", "kind": "component" },
267
+ "target": { "id": "shop.backend", "title": "Backend", "kind": "component" },
268
+ "kind": "sync",
269
+ "title": "Calls",
270
+ "description": "Frontend calls Backend",
271
+ "technology": "HTTP",
272
+ "tags": ["public"],
273
+ "includedInViews": [
274
+ { "id": "system-overview", "title": "System Overview", "type": "element" }
275
+ ],
276
+ "sourceLocation": {
277
+ "path": "/abs/path/project/model.c4",
278
+ "range": { "start": { "line": 12, "character": 0 }, "end": { "line": 14, "character": 0 } }
279
+ }
280
+ }
281
+ ]
282
+ }
283
+ `,inputSchema:{element1:f.string().describe(`Element ID (FQN)`),element2:f.string().describe(`Element ID (FQN)`),project:w},outputSchema:{found:f.array(N)}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project);if(a(t.element1,t.element2))throw Error(`No relationships possible between parent-child`);let r=[],o=await e.computedModel(n),s=o.findElement(t.element1);i(s,`Element "${t.element1}" not found in project "${n}"`);let c=o.findElement(t.element2);i(c,`Element "${t.element2}" not found in project "${n}"`);let u=mkLocate(e,n),d=l.findConnection(s,c,`both`).flatMap(e=>[...e.relations]);for(let e of d){let t=e.source===s&&e.target===c||e.source===c&&e.target===s;r.push({type:t?`direct`:`indirect`,source:{id:e.source.id,title:e.source.title,kind:e.source.kind},target:{id:e.target.id,title:e.target.title,kind:e.target.kind},kind:e.kind,title:e.title,description:e.description.text,technology:e.technology,tags:[...e.tags],includedInViews:includedInViews(e.views()),sourceLocation:u({relation:e.id})})}return{found:r}}),F=likec4Tool({name:`list-projects`,description:`
284
+ List LikeC4 projects discoverable in the current workspace.
285
+
286
+ Request:
287
+ - No input parameters.
288
+
289
+ Response (JSON object):
290
+ - projects: Project[]
291
+
292
+ Project (object) fields:
293
+ - id: string — stable project identifier
294
+ - title: string — human-readable project title
295
+ - folder: string — absolute path to the project root
296
+ - sources: string[] — absolute file paths of related documents
297
+
298
+ Notes:
299
+ - Read-only, idempotent, no side effects.
300
+ - Safe to call repeatedly.
301
+
302
+ Example response:
303
+ {
304
+ "projects": [
305
+ {
306
+ "id": "docs",
307
+ "title": "Documentation",
308
+ "folder": "/abs/path/to/workspace/docs",
309
+ "sources": [
310
+ "/abs/path/to/workspace/docs/model/contexts.likec4",
311
+ "/abs/path/to/workspace/docs/model/relations.likec4"
312
+ ]
313
+ }
314
+ ]
315
+ }
316
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`List projects`},outputSchema:{projects:f.array(f.object({id:f.string(),title:f.string(),folder:f.string(),sources:f.array(f.string())}))}},async e=>({projects:e.projects().map(e=>({id:e.id,title:e.title,folder:e.folder.fsPath,sources:e.documents.map(e=>e.fsPath)}))})),I=likec4Tool({name:`open-view`,description:`
317
+ Open a LikeC4 view in the editor's preview panel.
318
+
319
+ Request:
320
+ - viewId: string — view id (name)
321
+ - project: string (optional) — project id. Defaults to "default" if omitted.
322
+
323
+ Response (JSON object):
324
+ - location: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null — source location of the view if available
325
+
326
+ Notes:
327
+ - Read-only and idempotent with respect to the project model. Triggers a UI action in the editor.
328
+ - Only one preview panel can be open at a time.
329
+
330
+ Example response:
331
+ {
332
+ "location": {
333
+ "path": "/abs/path/project/model.c4",
334
+ "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 30, "character": 0 } }
335
+ }
336
+ }
337
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Open view in preview panel`},inputSchema:{viewId:f.string().describe(`View id (name)`),project:w},outputSchema:{location:C}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findView(t.viewId);if(!r)throw Error(`View with ID '${t.viewId}' not found in project ${n}`);return await e.views.openView(r.id,n),{location:mkLocate(e,n)({view:r.id})}}),L=f.enum([`exact`,`contains`,`exists`]),R=likec4Tool({name:`query-by-metadata`,description:`
338
+ Search elements and deployment nodes by metadata key-value pairs with flexible matching modes.
339
+
340
+ Request:
341
+ - key: string — metadata key to filter by
342
+ - value: string (optional) — metadata value to match (ignored for 'exists' mode)
343
+ - matchMode: "exact" | "contains" | "exists" (optional, default: "exact")
344
+ - project: string (optional) — project id. Defaults to "default" if omitted.
345
+
346
+ Match Modes:
347
+ - exact: Value must match exactly (case-sensitive)
348
+ Example: key="owner", value="platform-team" matches only exact "platform-team"
349
+ - contains: Value contains the search string (case-insensitive)
350
+ Example: key="technology", value="aws" matches "AWS Lambda", "aws-s3", etc.
351
+ - exists: Element has the key (value parameter is ignored)
352
+ Example: key="owner" returns all elements with any "owner" metadata
353
+
354
+ Response (JSON object):
355
+ - results: Array of matching elements/deployment-nodes, each with:
356
+ - id: string — element/node id (FQN)
357
+ - name: string — element/node name
358
+ - kind: string — element/node kind
359
+ - title: string — human-readable title
360
+ - tags: string[] — assigned tags
361
+ - metadata: Record<string, string | string[]> — all element metadata
362
+ - matchedValue: string — the metadata value that matched (for reference)
363
+ - includedInViews: View[] — views that include this element
364
+
365
+ View (object) fields:
366
+ - id: string — view identifier
367
+ - title: string — view title
368
+ - type: "element" | "deployment" | "dynamic"
369
+
370
+ Notes:
371
+ - Read-only, idempotent, no side effects.
372
+ - Safe to call repeatedly.
373
+ - Handles both string and array metadata values.
374
+ - For array values, matches if any element in the array matches.
375
+ - Returns empty array if no matches found.
376
+ - Limited to 50 results to avoid overwhelming responses.
377
+ - Case-sensitive for exact mode, case-insensitive for contains mode.
378
+
379
+ Example response:
380
+ {
381
+ "results": [
382
+ {
383
+ "id": "shop.frontend",
384
+ "name": "frontend",
385
+ "kind": "container",
386
+ "title": "Frontend",
387
+ "tags": ["public"],
388
+ "metadata": {
389
+ "owner": "platform-team",
390
+ "tier": "critical"
391
+ },
392
+ "matchedValue": "platform-team",
393
+ "includedInViews": [
394
+ {
395
+ "id": "system-overview",
396
+ "title": "System Overview",
397
+ "type": "element"
398
+ }
399
+ ]
400
+ }
401
+ ]
402
+ }
403
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by metadata`},inputSchema:{key:f.string().describe(`Metadata key to filter by`),value:f.string().optional().describe(`Metadata value to match (ignored for exists mode)`),matchMode:L.optional().default(`exact`).describe(`Matching mode`),project:w},outputSchema:{results:f.array(S.extend({matchedValue:f.string().describe(`The metadata value that matched`)}))}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=t.matchMode,a=[],matches=(e,t,n)=>{let r=Array.isArray(e)?e:[e];switch(n){case`exists`:return!0;case`exact`:return t===void 0?!1:r.some(e=>e===t);case`contains`:{if(t===void 0)return!1;let e=t.toLowerCase();return r.some(t=>t.toLowerCase().includes(e))}default:return!1}},getMatchedValue=(e,t,n)=>{let r=Array.isArray(e)?e:[e];if(n===`exists`||t===void 0)return r[0]||``;if(n===`exact`)return r.find(e=>e===t)||r[0]||``;if(n===`contains`){let e=t.toLowerCase();return r.find(t=>t.toLowerCase().includes(e))||r[0]||``}return r[0]||``};for(let e of r.elements()){if(a.length>=50)break;let n=e.getMetadata();if(t.key in n){let r=n[t.key];r!==void 0&&matches(r,t.value,i)&&a.push({...serializeElement(e),matchedValue:getMatchedValue(r,t.value,i)})}}if(a.length<50)for(let e of r.deployment.elements()){if(a.length>=50)break;let n=e.getMetadata();if(t.key in n){let r=n[t.key];r!==void 0&&matches(r,t.value,i)&&a.push({...serializeElement(e),matchedValue:getMatchedValue(r,t.value,i)})}}return{results:a}}),z=likec4Tool({name:`query-by-tag-pattern`,description:`
404
+ Search elements by tag patterns using prefix or substring matching.
405
+ Useful for tag taxonomies with structured naming conventions (e.g., "schedule_*", "*_asil_*").
406
+
407
+ Request:
408
+ - pattern: string — tag pattern to match
409
+ - matchMode: "prefix" | "contains" | "suffix" (optional, default: "prefix")
410
+ - prefix: matches tags starting with the pattern (e.g., "target_asil" matches "target_asil_qm", "target_asil_asil_b")
411
+ - contains: matches tags containing the pattern anywhere (e.g., "asil" matches "target_asil_qm", "unit_asil_b")
412
+ - suffix: matches tags ending with the pattern (e.g., "_tbc" matches "target_asil_qm__tbc")
413
+ - project: string (optional) — project id. Defaults to "default" if omitted.
414
+
415
+ Response (JSON object):
416
+ - results: Array of matching elements, each with:
417
+ - id: string — element id (FQN)
418
+ - name: string — element name
419
+ - kind: string — element kind
420
+ - title: string — human-readable title
421
+ - tags: string[] — all assigned tags
422
+ - metadata: Record<string, string | string[]> — element metadata
423
+ - matchedTags: string[] — the specific tags that matched the pattern
424
+ - includedInViews: View[] — views that include this element
425
+ - truncated: boolean — true if results were truncated due to exceeding the 50-result limit
426
+ - matchedTagValues: string[] — all unique tag values that matched the pattern across all elements
427
+
428
+ View (object) fields:
429
+ - id: string — view identifier
430
+ - title: string — view title
431
+ - type: "element" | "deployment" | "dynamic"
432
+
433
+ Notes:
434
+ - Read-only, idempotent, no side effects.
435
+ - Safe to call repeatedly.
436
+ - Pattern matching is case-insensitive.
437
+ - Returns empty array if no matches found.
438
+ - Limited to 50 results.
439
+ - matchedTagValues provides a summary of all distinct matching tag values found.
440
+
441
+ Example response:
442
+ {
443
+ "results": [
444
+ {
445
+ "id": "top.planner.behaviorNode",
446
+ "name": "behaviorNode",
447
+ "kind": "cgf-node",
448
+ "title": "behaviorNode :dwBehaviorPlannerNode",
449
+ "tags": ["is_in_dag", "target_asil_qm", "process_camera_master"],
450
+ "metadata": {},
451
+ "matchedTags": ["target_asil_qm"],
452
+ "includedInViews": []
453
+ }
454
+ ],
455
+ "truncated": false,
456
+ "matchedTagValues": ["target_asil_qm", "target_asil_asil_b", "target_asil_qm__tbc"]
457
+ }
458
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tag pattern`},inputSchema:{pattern:f.string().min(1).describe(`Tag pattern to match`),matchMode:f.enum([`prefix`,`contains`,`suffix`]).optional().default(`prefix`).describe(`Pattern matching mode (default: prefix)`),project:w},outputSchema:{results:f.array(S.extend({matchedTags:f.array(f.string()).describe(`Tags that matched the pattern`)})),truncated:f.boolean().describe(`True if results were truncated`),matchedTagValues:f.array(f.string()).describe(`All unique tag values matching the pattern across all elements`)}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=t.pattern.toLowerCase(),matchesTag=e=>{let n=e.toLowerCase();switch(t.matchMode){case`prefix`:return n.startsWith(i);case`contains`:return n.includes(i);case`suffix`:return n.endsWith(i)}},a=[],o=!1,s=new Set;for(let e of r.elements()){let t=[...e.tags].filter(matchesTag);if(t.length>0){if(t.forEach(e=>s.add(e)),a.length>=50){o=!0;continue}a.push({...serializeElement(e),matchedTags:t})}}for(let e of r.deployment.elements()){if(!c(e))continue;let t=[...e.tags].filter(matchesTag);if(t.length>0){if(t.forEach(e=>s.add(e)),a.length>=50){o=!0;continue}a.push({...serializeElement(e),matchedTags:t})}}return{results:a,truncated:o,matchedTagValues:[...s].sort((e,t)=>e.localeCompare(t))}}),B=likec4Tool({name:`query-by-tags`,description:`
459
+ Advanced tag filtering with boolean logic (AND, OR, NOT).
460
+
461
+ Request:
462
+ - allOf: string[] (optional) — element must have ALL these tags (AND logic)
463
+ - anyOf: string[] (optional) — element must have ANY of these tags (OR logic)
464
+ - noneOf: string[] (optional) — element must have NONE of these tags (NOT logic)
465
+ - project: string (optional) — project id. Defaults to "default" if omitted.
466
+
467
+ Boolean Logic:
468
+ - All three conditions are combined with AND logic
469
+ - At least one condition must be specified
470
+ - Tags are case-sensitive
471
+
472
+ Example Queries:
473
+ - Public APIs: {"allOf": ["public", "api"]}
474
+ - Deprecated or legacy: {"anyOf": ["deprecated", "legacy"]}
475
+ - Public but not deprecated: {"allOf": ["public"], "noneOf": ["deprecated"]}
476
+ - Critical services not in migration: {"allOf": ["critical", "service"], "noneOf": ["migration", "deprecated"]}
477
+
478
+ Response (JSON object):
479
+ - results: Array of matching elements/deployment-nodes, each with:
480
+ - id: string — element/node id (FQN)
481
+ - name: string — element/node name
482
+ - kind: string — element/node kind
483
+ - title: string — human-readable title
484
+ - tags: string[] — assigned tags (for reference)
485
+ - metadata: Record<string, string | string[]> — element metadata
486
+ - includedInViews: View[] — views that include this element
487
+
488
+ View (object) fields:
489
+ - id: string — view identifier
490
+ - title: string — view title
491
+ - type: "element" | "deployment" | "dynamic"
492
+
493
+ Notes:
494
+ - Read-only, idempotent, no side effects.
495
+ - Safe to call repeatedly.
496
+ - Returns empty array if no matches found.
497
+ - Limited to 50 results to avoid overwhelming responses.
498
+ - Conflicting conditions (e.g., allOf and noneOf with same tag) will return no results.
499
+
500
+ Example response:
501
+ {
502
+ "results": [
503
+ {
504
+ "id": "shop.api",
505
+ "name": "api",
506
+ "kind": "container",
507
+ "title": "API Gateway",
508
+ "tags": ["public", "api", "critical"],
509
+ "metadata": {
510
+ "owner": "platform-team"
511
+ },
512
+ "includedInViews": [
513
+ {
514
+ "id": "system-overview",
515
+ "title": "System Overview",
516
+ "type": "element"
517
+ }
518
+ ]
519
+ }
520
+ ]
521
+ }
522
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tags`},inputSchema:{allOf:f.array(f.string()).optional().describe(`Element must have ALL these tags (AND)`),anyOf:f.array(f.string()).optional().describe(`Element must have ANY of these tags (OR)`),noneOf:f.array(f.string()).optional().describe(`Element must have NONE of these tags (NOT)`),project:w},outputSchema:{results:f.array(S),truncated:f.boolean().describe(`True if results were truncated due to exceeding the 50-result limit`)}},async(e,t)=>{s(t.allOf&&t.allOf.length>0||t.anyOf&&t.anyOf.length>0||t.noneOf&&t.noneOf.length>0,`At least one condition (allOf, anyOf, or noneOf) must be specified with at least one tag`);let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=[],a=!1,matchesTags=e=>{let n=e instanceof Set?e:new Set(e);return!(t.allOf&&t.allOf.length>0&&!t.allOf.every(e=>n.has(e))||t.anyOf&&t.anyOf.length>0&&!t.anyOf.some(e=>n.has(e))||t.noneOf&&t.noneOf.length>0&&t.noneOf.some(e=>n.has(e)))};for(let e of r.elements()){if(i.length>=50){a=!0;break}matchesTags(e.tags)&&i.push(serializeElement(e))}if(!a)for(let e of r.deployment.elements()){if(i.length>=50){a=!0;break}c(e)&&matchesTags(e.tags)&&i.push(serializeElement(e))}return{results:i,truncated:a}}),V=f.enum([`ancestors`,`descendants`,`siblings`,`children`,`parent`,`incomers`,`outgoers`]),H=likec4Tool({name:`query-graph`,description:`
523
+ Query element hierarchy and relationships in the architecture graph.
524
+
525
+ Request:
526
+ - elementId: string — element id (FQN) to query
527
+ - queryType: "ancestors" | "descendants" | "siblings" | "children" | "parent" | "incomers" | "outgoers"
528
+ - includeIndirect: boolean (optional, default: true) — for incomers/outgoers, include indirect relationships (through nested elements)
529
+ - project: string (optional) — project id. Defaults to "default" if omitted.
530
+
531
+ Query Types:
532
+ - ancestors: Returns all parent elements up to the root (hierarchical)
533
+ Example: shop.frontend.auth.service returns [shop.frontend.auth, shop.frontend, shop]
534
+ - descendants: Returns all child elements recursively (hierarchical)
535
+ Example: shop.frontend returns all nested elements like shop.frontend.auth, shop.frontend.auth.service
536
+ - siblings: Returns elements at the same hierarchy level with the same parent
537
+ Example: shop.frontend returns [shop.backend, shop.database] if they're siblings
538
+ - children: Returns direct child elements only (not recursive)
539
+ Example: shop returns [shop.frontend, shop.backend] but not shop.frontend.auth
540
+ - parent: Returns the direct parent element
541
+ Example: shop.frontend.auth returns shop.frontend
542
+ - incomers: Returns elements that have outgoing relationships to this element (single hop, not recursive).
543
+ For recursive upstream traversal, use query-incomers-graph instead.
544
+ includeIndirect=true: Includes relationships to nested children
545
+ Example: Elements that depend on this element
546
+ - outgoers: Returns elements that receive incoming relationships from this element (single hop, not recursive).
547
+ For recursive downstream traversal, use query-outgoers-graph instead.
548
+ includeIndirect=true: Includes relationships from nested children
549
+ Example: Elements this element depends on
550
+
551
+ Response (JSON object):
552
+ - results: Array of elements (max 100), each with:
553
+ - id: string — element id (FQN)
554
+ - name: string — element name
555
+ - kind: string — element kind
556
+ - title: string — human-readable title
557
+ - tags: string[] — assigned tags
558
+ - metadata: Record<string, string> — element metadata
559
+ - includedInViews: View[] — views that include this element
560
+ - truncated: boolean — true if results were truncated due to exceeding maximum limit (100)
561
+
562
+ View (object) fields:
563
+ - id: string — view identifier
564
+ - title: string — view title
565
+ - type: "element" | "deployment" | "dynamic"
566
+
567
+ Notes:
568
+ - Read-only, idempotent, no side effects.
569
+ - Safe to call repeatedly.
570
+ - For parent query on root element, returns empty array.
571
+ - For hierarchical queries (ancestors, descendants, siblings, children), includeIndirect is ignored.
572
+
573
+ Example response:
574
+ {
575
+ "results": [
576
+ {
577
+ "id": "shop.frontend",
578
+ "name": "frontend",
579
+ "kind": "container",
580
+ "title": "Frontend",
581
+ "tags": ["public"],
582
+ "metadata": { "owner": "web-team" },
583
+ "includedInViews": [
584
+ {
585
+ "id": "system-overview",
586
+ "title": "System Overview",
587
+ "type": "element"
588
+ }
589
+ ]
590
+ }
591
+ ],
592
+ "truncated": false
593
+ }
594
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query element graph`},inputSchema:{elementId:f.string().describe(`Element id (FQN) to query`),queryType:V.describe(`Type of graph query`),includeIndirect:f.boolean().optional().default(!0).describe(`For incomers/outgoers: include indirect relationships (default: true)`),project:w},outputSchema:{results:f.array(S),truncated:f.boolean().describe(`True if results were truncated due to exceeding maximum limit`)}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findElement(t.elementId);s(r,`Element "${t.elementId}" not found in project "${n}"`);let i=[],a=!1;switch(t.queryType){case`ancestors`:for(let e of r.ancestors()){if(i.length>=100){a=!0;break}i.push(serializeElement(e))}break;case`descendants`:for(let e of r.descendants()){if(i.length>=100){a=!0;break}i.push(serializeElement(e))}break;case`siblings`:for(let e of r.siblings()){if(i.length>=100){a=!0;break}i.push(serializeElement(e))}break;case`children`:for(let e of r.children()){if(i.length>=100){a=!0;break}i.push(serializeElement(e))}break;case`parent`:{let e=r.parent;e&&i.push(serializeElement(e));break}case`incomers`:{let e=t.includeIndirect?`all`:`direct`;for(let t of r.incomers(e)){if(i.length>=100){a=!0;break}i.push(serializeElement(t))}break}case`outgoers`:{let e=t.includeIndirect?`all`:`direct`;for(let t of r.outgoers(e)){if(i.length>=100){a=!0;break}i.push(serializeElement(t))}break}}return{results:i,truncated:a}}),U=f.object({elementId:f.string().describe(`ID of the incoming element`),relationshipLabel:f.string().optional().describe(`Label on the relationship`),technology:f.string().optional().describe(`Technology specified on the relationship`)}),W=likec4Tool({name:`query-incomers-graph`,description:`
595
+ Query the complete graph of all elements that provide input to the target element (recursive incomers/producers).
596
+
597
+ This tool performs a breadth-first traversal to discover all upstream dependencies - elements that directly or
598
+ indirectly provide input to the target element. It returns the complete subgraph in a single response,
599
+ making it much more efficient than repeated individual queries.
600
+
601
+ Request:
602
+ - elementId: string — target element id (FQN) to start from
603
+ - includeIndirect: boolean (optional, default: true) — include relationships through nested elements
604
+ - maxDepth: number (optional, default: 10, max: 50) — maximum traversal depth to prevent infinite recursion
605
+ - maxNodes: number (optional, default: 200, max: 2000) — maximum number of nodes to return
606
+ - project: string (optional) — project id. Defaults to "default" if omitted.
607
+
608
+ Response Structure:
609
+ {
610
+ "target": "element.id",
611
+ "totalNodes": number,
612
+ "maxDepth": number,
613
+ "truncated": boolean,
614
+ "nodes": {
615
+ "element.id": {
616
+ "id": "element.id",
617
+ "name": "name",
618
+ "kind": "kind",
619
+ "title": "title",
620
+ "tags": ["tag1", "tag2"],
621
+ "metadata": {},
622
+ "includedInViews": [...],
623
+ "incomers": [
624
+ {
625
+ "elementId": "id1",
626
+ "relationshipLabel": "uses",
627
+ "technology": "REST"
628
+ }
629
+ ],
630
+ "depth": number
631
+ }
632
+ }
633
+ }
634
+
635
+ Use Cases:
636
+ - Find all producers/dependencies for an element
637
+ - Trace data lineage upstream
638
+ - Identify root causes and dependencies
639
+ - Build complete dependency trees
640
+ - Answer "what feeds into this?" questions
641
+
642
+ Notes:
643
+ - Read-only, idempotent, no side effects
644
+ - Cycle detection prevents infinite loops
645
+ - Result size limited to maxNodes to prevent huge responses
646
+ - If truncated=true, increase maxNodes or reduce maxDepth to get more specific results
647
+
648
+ Example:
649
+ For a database element, this returns all services, APIs, and components that write to it,
650
+ plus all their dependencies, recursively up to maxDepth levels.
651
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete incomers graph`},inputSchema:{elementId:f.string().describe(`Target element id (FQN) to query incomers for`),includeIndirect:f.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:f.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:f.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:w},outputSchema:{target:f.string().describe(`Target element id`),totalNodes:f.number().describe(`Total number of nodes in the graph`),maxDepth:f.number().describe(`Maximum depth reached`),truncated:f.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:f.record(S.extend({incomers:f.array(U).describe(`Incoming relationships with details`),depth:f.number().describe(`Distance from target element (0 = target)`)}))}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n);s(r.findElement(t.elementId),`Element "${t.elementId}" not found in project "${n}"`);let i=t.includeIndirect?`all`:`direct`,a=traverseGraph(r,t.elementId,`incoming`,i,t.maxDepth,t.maxNodes),o={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;o[e]={...r,incomers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:o}}),G=f.object({elementId:f.string().describe(`ID of the outgoing element`),relationshipLabel:f.string().optional().describe(`Label on the relationship`),technology:f.string().optional().describe(`Technology specified on the relationship`)}),K=likec4Tool({name:`query-outgoers-graph`,description:`
652
+ Query the complete graph of all elements that receive output from the target element (recursive outgoers/consumers).
653
+
654
+ This tool performs a breadth-first traversal to discover all downstream dependencies - elements that directly or
655
+ indirectly consume output from the target element. It returns the complete subgraph in a single response,
656
+ making it much more efficient than repeated individual queries.
657
+
658
+ Request:
659
+ - elementId: string — target element id (FQN) to start from
660
+ - includeIndirect: boolean (optional, default: true) — include relationships through nested elements
661
+ - maxDepth: number (optional, default: 10, max: 50) — maximum traversal depth to prevent infinite recursion
662
+ - maxNodes: number (optional, default: 200, max: 2000) — maximum number of nodes to return
663
+ - project: string (optional) — project id. Defaults to "default" if omitted.
664
+
665
+ Response Structure:
666
+ {
667
+ "target": "element.id",
668
+ "totalNodes": number,
669
+ "maxDepth": number,
670
+ "truncated": boolean,
671
+ "nodes": {
672
+ "element.id": {
673
+ "id": "element.id",
674
+ "name": "name",
675
+ "kind": "kind",
676
+ "title": "title",
677
+ "tags": ["tag1", "tag2"],
678
+ "metadata": {},
679
+ "includedInViews": [...],
680
+ "outgoers": [
681
+ {
682
+ "elementId": "id1",
683
+ "relationshipLabel": "sends data to",
684
+ "technology": "Kafka"
685
+ }
686
+ ],
687
+ "depth": number
688
+ }
689
+ }
690
+ }
691
+
692
+ Use Cases:
693
+ - Find all consumers/dependents of an element
694
+ - Trace data lineage downstream
695
+ - Assess impact of changes (blast radius)
696
+ - Build complete consumer trees
697
+ - Answer "what depends on this?" questions
698
+
699
+ Notes:
700
+ - Read-only, idempotent, no side effects
701
+ - Cycle detection prevents infinite loops
702
+ - Result size limited to maxNodes to prevent huge responses
703
+ - If truncated=true, increase maxNodes or reduce maxDepth to get more specific results
704
+
705
+ Example:
706
+ For an API service, this returns all clients, services, and systems that consume its output,
707
+ plus all their consumers, recursively up to maxDepth levels.
708
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete outgoers graph`},inputSchema:{elementId:f.string().describe(`Target element id (FQN) to query outgoers for`),includeIndirect:f.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:f.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:f.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:w},outputSchema:{target:f.string().describe(`Target element id`),totalNodes:f.number().describe(`Total number of nodes in the graph`),maxDepth:f.number().describe(`Maximum depth reached`),truncated:f.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:f.record(S.extend({outgoers:f.array(G).describe(`Outgoing relationships with details`),depth:f.number().describe(`Distance from target element (0 = target)`)}))}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n);s(r.findElement(t.elementId),`Element "${t.elementId}" not found in project "${n}"`);let i=t.includeIndirect?`all`:`direct`,a=traverseGraph(r,t.elementId,`outgoing`,i,t.maxDepth,t.maxNodes),o={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;o[e]={...r,outgoers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:o}}),q=likec4Tool({name:`read-deployment`,description:`
709
+ Read details about a deployment node or a deployed instance in a LikeC4 project.
710
+
711
+ What it does:
712
+ - Returns metadata about a deployment entity (node or instance), including kind, tags, color/shape, children, which views include it, and its source location.
713
+
714
+ Inputs:
715
+ - id: string — Deployment id (FQN)
716
+ - project: string (optional, defaults to "default") — Project id
717
+
718
+ Output fields:
719
+ - type: "deployment-node" | "deployed-instance"
720
+ - id: string — Deployment id (FQN)
721
+ - kind: string — Deployment node kind, or element kind for deployed instances
722
+ - name: string — Name of the deployment entity
723
+ - title: string — Title of the deployment entity
724
+ - description: string|null — Description text
725
+ - technology: string|null — Technology info, if any
726
+ - tags: string[] — Tags assigned to this entity
727
+ - project: string — Project id
728
+ - metadata: Record<string, string>
729
+ - links: Array<{ title: string|null, url: string, relative: string|null }> — external links associated with this deployment entity
730
+ - shape: string — Rendered shape
731
+ - color: string — Rendered color
732
+ - children: string[] — Child deployment ids (empty for instances)
733
+ - includedInViews: View[] — Views that include this entity
734
+ - instanceof: { id: string, title: string, kind: string } | null — If type is "deployed-instance", the referenced element
735
+ - sourceLocation: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null
736
+
737
+ View (object) fields:
738
+ - id: string — view identifier
739
+ - title: string — view title
740
+ - type: "element" | "deployment" | "dynamic"
741
+
742
+ Notes:
743
+ - Read-only, idempotent; does not mutate the model.
744
+
745
+ Example request:
746
+ { "id": "k8s.cluster.frontend", "project": "default" }
747
+
748
+ Example response (deployed instance):
749
+ {
750
+ "type": "deployed-instance",
751
+ "id": "k8s.cluster.frontend",
752
+ "kind": "k8s.pod",
753
+ "name": "frontend",
754
+ "title": "Frontend Pod",
755
+ "description": null,
756
+ "technology": "Kubernetes",
757
+ "tags": ["prod"],
758
+ "project": "default",
759
+ "metadata": {},
760
+ "links": [],
761
+ "shape": "rectangle",
762
+ "color": "#2F80ED",
763
+ "children": [],
764
+ "includedInViews": [
765
+ { "id": "runtime-overview", "title": "Runtime Overview", "type": "deployment" }
766
+ ],
767
+ "instanceof": { "id": "shop.frontend", "title": "Frontend", "kind": "component" },
768
+ "sourceLocation": {
769
+ "path": "/abs/path/project/model.c4",
770
+ "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 25, "character": 0 } }
771
+ }
772
+ }
773
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read deployment entity`},inputSchema:{id:f.string().describe(`Deployment id (FQN)`),project:w},outputSchema:{type:f.enum([`deployment-node`,`deployed-instance`]),id:f.string().describe(`Deployment id (FQN)`),kind:f.string().describe(`Deployment node kind, or element kind for deployed instances`),name:f.string(),title:f.string(),description:f.string().nullable(),technology:f.string().nullable(),tags:f.array(f.string()),project:f.string(),metadata:f.record(f.union([f.string(),f.array(f.string())])),links:f.array(f.object({title:f.string().nullable().describe(`Optional link title`),url:f.string().describe(`Link URL`),relative:f.string().nullable().describe(`Relative path (if URL is relative to workspace root)`)})).describe(`External links associated with this deployment entity`),shape:f.string(),color:f.string(),children:f.array(f.string()).describe(`Children of this deployment node (Array of Deployment ids)`),includedInViews:T.describe(`Views that include this deployment node`),instanceof:f.object({id:f.string().describe(`Element ID (FQN)`),title:f.string(),kind:f.string()}).nullable().describe(`If type is "deployed-instance", the referenced element`),sourceLocation:C}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).deployment.findElement(t.id);s(r,`Deployment entity "${t.id}" not found in project "${n}"`);let i=mkLocate(e,n);return{type:r.isInstance()?`deployed-instance`:`deployment-node`,id:r.id,name:r.name,kind:r.kind,title:r.title,description:r.description.text,technology:r.technology,tags:[...r.tags],project:n,metadata:r.getMetadata(),links:(r.links??[]).map(e=>({title:e.title??null,url:e.url,relative:e.relative??null})),shape:r.shape,color:r.color,children:r.isInstance()?[]:[...r.children()].map(e=>e.id),includedInViews:includedInViews(r.views()),instanceof:r.isInstance()?{id:r.element.id,title:r.element.title,kind:r.element.kind}:null,sourceLocation:i({deployment:r.id})}}),J=likec4Tool({name:`read-element`,description:`
774
+ Read detailed information about a LikeC4 element.
775
+
776
+ Request:
777
+ - id: string — element id (FQN)
778
+ - project: string (optional) — project id. Defaults to "default" if omitted.
779
+
780
+ Response (JSON object):
781
+ - id: string — element id (FQN)
782
+ - name: string — element name
783
+ - kind: string — element kind
784
+ - title: string — human-readable title
785
+ - description: string|null — optional description
786
+ - technology: string|null — optional technology
787
+ - tags: string[] — assigned tags
788
+ - project: string — project id this element belongs to
789
+ - metadata: Record<string, string> — element metadata
790
+ - links: Array<{ title: string|null, url: string, relative: string|null }> — external links associated with this element
791
+ - shape: string — rendered shape
792
+ - color: string — rendered color
793
+ - children: string[] — ids (FQNs) of direct child elements
794
+ - defaultView: string|null — default view name if set
795
+ - includedInViews: View[] — views that include this element
796
+ - relationships: object — relationships of this element (direct and indirect)
797
+ - incoming: Array<{ source: { id: string, title: string, kind: string }, kind: string|null, target: string, title: string|null, description: string|null, technology: string|null, tags: string[] }>
798
+ - outgoing: Array<{ source: string, target: { id: string, title: string, kind: string }, kind: string|null, title: string|null, description: string|null, technology: string|null, tags: string[] }>
799
+ - deployedInstances: string[] — deployed instance ids (Deployment FQNs)
800
+ - sourceLocation: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null — source location if available
801
+
802
+ View (object) fields:
803
+ - id: string — view identifier
804
+ - title: string — view title
805
+ - type: "element" | "deployment" | "dynamic"
806
+
807
+ Notes:
808
+ - Read-only, idempotent, no side effects.
809
+ - Safe to call repeatedly.
810
+
811
+ Example response:
812
+ {
813
+ "id": "shop.frontend",
814
+ "name": "frontend",
815
+ "kind": "container",
816
+ "title": "Frontend",
817
+ "description": "User-facing web app",
818
+ "technology": "React",
819
+ "tags": ["public"],
820
+ "project": "default",
821
+ "metadata": { "owner": "web" },
822
+ "links": [
823
+ {
824
+ "title": "Documentation",
825
+ "url": "https://docs.example.com/frontend",
826
+ "relative": null
827
+ }
828
+ ],
829
+ "shape": "rounded-rectangle",
830
+ "color": "#2F80ED",
831
+ "children": ["shop.frontend.auth"],
832
+ "defaultView": "frontend-overview",
833
+ "includedInViews": [
834
+ {
835
+ "id": "frontend-overview",
836
+ "title": "Frontend Overview",
837
+ "type": "element"
838
+ }
839
+ ],
840
+ "relationships": {
841
+ "incoming": [
842
+ {
843
+ "source": { "id": "shop.api", "title": "API", "kind": "container" },
844
+ "kind": "uses",
845
+ "target": "shop.frontend",
846
+ "title": "Calls",
847
+ "description": null,
848
+ "technology": "HTTPS",
849
+ "tags": []
850
+ }
851
+ ],
852
+ "outgoing": []
853
+ },
854
+ "deployedInstances": ["k8s.cluster.frontend"],
855
+ "sourceLocation": {
856
+ "path": "/abs/path/project/model.c4",
857
+ "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 25, "character": 0 } }
858
+ }
859
+ }
860
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read element`},inputSchema:{id:f.string().describe(`Element id (FQN)`),project:w},outputSchema:{id:f.string().describe(`Element id (FQN)`),kind:f.string().describe(`Element kind`),name:f.string().describe(`Element name`),title:f.string(),description:f.string().nullable(),technology:f.string().nullable(),tags:f.array(f.string()),project:f.string(),metadata:f.record(f.union([f.string(),f.array(f.string())])),links:f.array(f.object({title:f.string().nullable().describe(`Optional link title`),url:f.string().describe(`Link URL`),relative:f.string().nullable().describe(`Relative path (if URL is relative to workspace root)`)})).describe(`External links associated with this element`),shape:f.string(),color:f.string(),children:f.array(f.string()).describe(`Children of this element (Array of FQNs)`),defaultView:f.string().nullable().describe(`Name of the default view of this element`),includedInViews:T.describe(`Views that include this element`),relationships:f.object({incoming:f.array(f.object({source:f.object({id:f.string(),title:f.string(),kind:f.string()}).describe(`Source element of this relationship`),kind:f.string().nullable().describe(`Relationship kind`),target:f.string().describe(`Target element id (FQN), either this element or nested element, if relationship is indirect`),title:f.string().nullable().describe(`Relationship title`),description:f.string().nullable().describe(`Relationship description`),technology:f.string().nullable().describe(`Relationship technology`),tags:f.array(f.string()).describe(`Relationship tags`)})).describe(`Incoming relationships of this element (direct and indirect, incoming to nested elements)`),outgoing:f.array(f.object({source:f.string().describe(`Source element id (FQN), either this element or nested element, if relationship is indirect`),target:f.object({id:f.string(),title:f.string(),kind:f.string()}).describe(`Target element of this relationship`),kind:f.string().nullable().describe(`Relationship kind`),title:f.string().nullable().describe(`Relationship title`),description:f.string().nullable().describe(`Relationship description`),technology:f.string().nullable().describe(`Relationship technology`),tags:f.array(f.string()).describe(`Relationship tags`)})).describe(`Outgoing relationships of this element (direct and indirect, outgoing from nested elements)`)}).describe(`Relationships of this element`),deployedInstances:f.array(f.string()).describe(`Deployed instances of this element (Array of Deployment FQNs)`),sourceLocation:C}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findElement(t.id);s(r,`Element "${t.id}" not found in project "${n}"`);let i=mkLocate(e,n);return{id:r.id,name:r.name,kind:r.kind,title:r.title,description:r.description.text,technology:r.technology,tags:[...r.tags],project:n,metadata:r.getMetadata(),links:(r.links??[]).map(e=>({title:e.title??null,url:e.url,relative:e.relative??null})),shape:r.shape,color:r.color,children:[...r.children()].map(e=>e.id),defaultView:r.defaultView?.id||null,includedInViews:includedInViews(r.views()),relationships:{incoming:[...r.incoming()].map(e=>({source:{id:e.source.id,title:e.source.title,kind:e.source.kind},kind:e.kind,target:e.target.id,title:e.title,description:e.description.text,technology:e.technology,tags:[...e.tags]})),outgoing:[...r.outgoing()].map(e=>({source:e.source.id,target:{id:e.target.id,title:e.target.title,kind:e.target.kind},kind:e.kind,title:e.title,description:e.description.text,technology:e.technology,tags:[...e.tags]}))},deployedInstances:[...r.deployments()].map(e=>e.id),sourceLocation:i({element:r.id})}}),Y=likec4Tool({name:`read-project-summary`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read project summary`},description:`
861
+ Request:
862
+ - project: string (optional) — project id. Defaults to "default" if omitted.
863
+
864
+ Response (JSON object):
865
+ - title: string — human-readable project title
866
+ - folder: string — absolute path to the project root
867
+ - sources: string[] — absolute file paths of model documents
868
+ - config: object — project configuration
869
+ - name: string — project identifier
870
+ - title?: string — human-readable title
871
+ - contactPerson?: string — maintainer contact
872
+ - metadata?: object — custom project metadata as key-value pairs
873
+ - extends?: string | string[] — style inheritance paths
874
+ - exclude?: string[] — file exclusion patterns
875
+ - include?: object — include configuration (paths, maxDepth, fileThreshold)
876
+ - manualLayouts?: object — manual layouts config (outDir)
877
+ - styles?: object — simplified styles (hasTheme, hasDefaults, hasCustomCss)
878
+ - specification: object
879
+ - elementKinds: string[] — all element kinds
880
+ - relationshipKinds: string[] — all relationship kinds
881
+ - deploymentKinds: string[] — all deployment kinds
882
+ - tags: string[] — all tags
883
+ - metadataKeys: string[] — used metadata keys
884
+ - elements: Element[] — list of elements
885
+ - deployments: Deployment[] — list of deployment entities
886
+ - views: View[] — list of views defined in the model
887
+
888
+ Element (object) fields:
889
+ - id: string — element id (FQN)
890
+ - kind: string — element kind
891
+ - title: string — element title
892
+ - tags: string[] — element tags
893
+
894
+ Deployment (object) fields:
895
+ - type = "deployment-node": { id: string, kind: string, title: string, tags: string[] }
896
+ - type = "deployed-instance": { id: string, title: string, tags: string[], referencedElementId: string }
897
+
898
+ View (object) fields:
899
+ - id: string — view identifier
900
+ - title: string — view title
901
+ - type: "element" | "deployment" | "dynamic"
902
+
903
+ Notes:
904
+ - Read-only, idempotent, no side effects.
905
+ - Safe to call repeatedly.
906
+
907
+ Example response:
908
+ {
909
+ "title": "Cloud Boutique",
910
+ "folder": "/abs/path/to/workspace/examples/cloud-system",
911
+ "sources": [
912
+ "/abs/path/to/workspace/examples/cloud-system/model.c4"
913
+ ],
914
+ "config": {
915
+ "name": "cloud-boutique",
916
+ "title": "Cloud Boutique",
917
+ "contactPerson": "admin@example.com"
918
+ },
919
+ "specification": {
920
+ "elementKinds": ["system", "container", "component"],
921
+ "relationshipKinds": ["uses", "depends-on"],
922
+ "deploymentKinds": ["node", "cluster"],
923
+ "tags": ["public", "internal"],
924
+ "metadataKeys": ["owner", "tier"]
925
+ },
926
+ "elements": [
927
+ {
928
+ "id": "shop.frontend",
929
+ "kind": "component",
930
+ "title": "Frontend",
931
+ "tags": ["public"]
932
+ }
933
+ ],
934
+ "deployments": [
935
+ {
936
+ "type": "deployment-node",
937
+ "id": "k8s.shop.frontend",
938
+ "kind": "cluster",
939
+ "title": "Frontend",
940
+ "tags": []
941
+ }
942
+ ],
943
+ "views": [
944
+ {
945
+ "id": "system-overview",
946
+ "title": "System Overview",
947
+ "type": "element"
948
+ }
949
+ ]
950
+ }
951
+ `,inputSchema:{project:w},outputSchema:{title:f.string(),folder:f.string(),sources:f.array(f.string()),config:x.describe(`Project configuration`),specification:f.object({elementKinds:f.array(f.string()),relationshipKinds:f.array(f.string()),deploymentKinds:f.array(f.string()),tags:f.array(f.string()),metadataKeys:f.array(f.string())}),elements:f.array(f.object({id:f.string(),kind:f.string(),title:f.string(),tags:f.array(f.string())})).describe(`List of elements in the project`),deployments:f.array(f.discriminatedUnion(`type`,[f.object({type:f.literal(`deployment-node`),id:f.string().describe(`Node ID`),kind:f.string().describe(`Deployment node kind`),title:f.string().describe(`Node title`),tags:f.array(f.string())}),f.object({type:f.literal(`deployed-instance`),id:f.string().describe(`Node ID`),title:f.string().describe(`Node title`),tags:f.array(f.string()),referencedElementId:f.string().describe(`Element ID (FQN)`)})])).describe(`List of deployment nodes and deployed instances in the project`),views:f.array(f.object({id:f.string(),title:f.string(),type:f.enum([`element`,`deployment`,`dynamic`])}))}},async(e,n)=>{let r=e.projectsManager.ensureProjectId(n.project),i=e.project(r),a=await e.computedModel(r);return{title:i.title,folder:i.folder.fsPath,sources:i.documents?.map(e=>e.fsPath)??[],config:serializeConfig(i.config),specification:{elementKinds:t(a.specification.elements),relationshipKinds:t(a.specification.relationships),deploymentKinds:t(a.specification.deployments),tags:[...a.tags],metadataKeys:a.specification.metadataKeys??[]},elements:[...a.elements()].filter(e=>!e.imported).map(e=>({id:e.id,kind:e.kind,title:e.title,tags:[...e.tags]})),deployments:[...a.deployment.elements()].map(e=>e.isInstance()?{type:`deployed-instance`,id:e.id,title:e.title,tags:[...e.tags],referencedElementId:e.element.id}:{type:`deployment-node`,id:e.id,kind:e.kind,title:e.title,tags:[...e.tags]}),views:[...a.views()].map(e=>({id:e.id,title:e.titleOrId,type:e.$view._type}))}}),modelRef=e=>e.hasElement()?e.element.id:e.hasDeployment()?e.deployment.id:null,X=f.discriminatedUnion(`type`,[f.object({type:f.literal(`element`),id:f.string().describe(`Node ID`),elementId:f.string().describe(`Element ID (FQN)`),kind:f.string().describe(`Element kind`),title:f.string().describe(`Node title`),description:f.string().nullable(),technology:f.string().nullable(),children:f.array(f.string()).describe(`Children nodes, array of node IDs`),shape:f.string().describe(`Rendered shape`),color:f.string().describe(`Rendered color`),tags:f.array(f.string())}),f.object({type:f.literal(`deployment-node`),id:f.string().describe(`Node ID`),deploymentId:f.string().describe(`Deployment entity ID (FQN)`),kind:f.string().describe(`Deployment kind`),title:f.string().describe(`Node title`),description:f.string().nullable(),technology:f.string().nullable(),children:f.array(f.string()).describe(`Children nodes, array of node IDs`),shape:f.string().describe(`Rendered shape`),color:f.string().describe(`Rendered color`),tags:f.array(f.string())}),f.object({type:f.literal(`deployed-instance`),id:f.string().describe(`Node ID`),deploymentId:f.string().describe(`Deployment entity ID (FQN)`),title:f.string().describe(`Node title`),description:f.string().nullable(),technology:f.string().nullable(),referencedElement:f.object({id:f.string().describe(`Element ID (FQN)`),kind:f.string().describe(`Element kind`),title:f.string().describe(`Element title`)}),shape:f.string().describe(`Rendered shape`),color:f.string().describe(`Rendered color`),tags:f.array(f.string())})]),Z=likec4Tool({name:`read-view`,description:`
952
+ Read detailed information about a LikeC4 view.
953
+
954
+ Request:
955
+ - viewId: string — view id (name)
956
+ - project: string (optional) — project id. Defaults to "default" if omitted.
957
+
958
+ Response (JSON object):
959
+ - id: string — view id
960
+ - type: "element" | "deployment" | "dynamic" — view type
961
+ - title: string — view title (falls back to id if not set)
962
+ - description: string|null — optional description
963
+ - tags: string[] — view tags
964
+ - project: string — project id this view belongs to
965
+ - nodes: Node[] — nodes included in the view
966
+ - edges: Edge[] — relationships between nodes
967
+ - sourceLocation: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null — source location if available
968
+
969
+ Node (discriminated union by "type"):
970
+ - type = "element": { id: string, elementId: string, kind: string, title: string, description: string|null, technology: string|null, children: string[], shape: string, color: string, tags: string[] }
971
+ - type = "deployment-node": { id: string, deploymentId: string, kind: string, title: string, description: string|null, technology: string|null, children: string[], shape: string, color: string, tags: string[] }
972
+ - type = "deployed-instance": { id: string, deploymentId: string, title: string, description: string|null, technology: string|null, referencedElement: { id: string, kind: string, title: string }, shape: string, color: string, tags: string[] }
973
+
974
+ Edge object:
975
+ - { source: string, target: string, label: string|null, description: string|null, technology: string|null, tags: string[] }
976
+
977
+ Notes:
978
+ - Read-only, idempotent, no side effects.
979
+
980
+ Example response:
981
+ {
982
+ "id": "system-overview",
983
+ "type": "element",
984
+ "title": "System Overview",
985
+ "description": null,
986
+ "tags": [],
987
+ "project": "default",
988
+ "nodes": [
989
+ { "type": "logical", "id": "n1", "elementId": "shop.frontend", "kind": "container", "title": "Frontend", "description": null, "technology": "React", "children": [], "shape": "rounded-rectangle", "color": "#2F80ED", "tags": [] }
990
+ ],
991
+ "edges": [
992
+ { "source": "n1", "target": "n2", "label": "calls", "description": null, "technology": "HTTPS", "tags": [] }
993
+ ],
994
+ "sourceLocation": {
995
+ "path": "/abs/path/project/model.c4",
996
+ "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 30, "character": 0 } }
997
+ }
998
+ }
999
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read view`},inputSchema:{viewId:f.string().describe(`View id (name)`),project:w},outputSchema:{id:f.string(),type:f.enum([`element`,`deployment`,`dynamic`]).describe(`View type`),title:f.string(),description:f.string().nullable(),tags:f.array(f.string()),project:f.string(),nodes:f.array(X),edges:f.array(f.object({source:f.string().describe(`Source node`),target:f.string().describe(`Target node`),label:f.string().nullable(),description:f.string().nullable(),technology:f.string().nullable(),tags:f.array(f.string())})).describe(`Edge represents relationship between nodes`),sourceLocation:C}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=e.project(n),i=(await e.computedModel(n)).findView(t.viewId);if(!i)throw Error(`View with ID '${t.viewId}' not found in project ${r.id}`);let a=mkLocate(e,r.id);return{id:i.id,type:i.$view._type,title:i.title??i.id,description:i.description.text,tags:[...i.tags],project:r.id,nodes:[...i.nodes()].flatMap(e=>{let t={id:e.id,title:e.title,description:e.description.text,technology:e.technology,shape:e.shape,color:e.color,tags:[...e.tags]};return e.hasDeployedInstance()?{...t,type:`deployed-instance`,deploymentId:e.deployment.id,referencedElement:{id:e.deployment.element.id,kind:e.deployment.element.kind,title:e.deployment.element.title}}:e.hasDeployment()?{...t,type:`deployment-node`,kind:e.deployment.kind,deploymentId:e.deployment.id,children:[...e.children()].map(e=>e.id)}:e.hasElement()?{...t,type:`element`,elementId:e.element.id,kind:e.element.kind,children:[...e.children()].flatMap(e=>modelRef(e)??[])}:[]}),edges:[...i.edges()].map(e=>({source:e.source.id,target:e.target.id,label:e.label,description:e.description.text,technology:e.technology,tags:[...e.tags]})),sourceLocation:a({view:i.id})}}),Q=f.array(f.discriminatedUnion(`type`,[f.object({type:f.literal(`element`),project:f.string().describe(`Project ID`),id:f.string().describe(`Element ID (FQN)`),name:f.string().describe(`Element name`),kind:f.string(),title:f.string(),technology:f.string().nullable(),shape:f.string(),includedInViews:T,metadata:f.record(f.union([f.string(),f.array(f.string())])),tags:f.array(f.string())}),f.object({type:f.literal(`deployment-node`),project:f.string().describe(`Project ID`),id:f.string().describe(`Deployment ID (FQN)`),name:f.string().describe(`Deployment name`),kind:f.string(),title:f.string(),technology:f.string().nullable(),shape:f.string(),includedInViews:T,metadata:f.record(f.union([f.string(),f.array(f.string())])),tags:f.array(f.string())})])),$=likec4Tool({name:`search-element`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Search elements`},description:`
1000
+ Search LikeC4 elements and deployment nodes across all projects.
1001
+
1002
+ Query syntax (case-insensitive):
1003
+ - kind:<value> filters by kind
1004
+ - shape:<value> filters by shape
1005
+ - meta:<key> filters by having metadata with the given key
1006
+ - #<value> matches assigned tags
1007
+ - <value> matches id (FQN) or title
1008
+
1009
+ Request:
1010
+ - search: string — at least 2 characters
1011
+
1012
+ Response (JSON object):
1013
+ - total: number - total number of results
1014
+ - found: Result[] - returns top 20 results
1015
+
1016
+ Result (discriminated union by "type"):
1017
+ - type = "element": { id: string, name: string, kind: string, title: string, technology: string|null, shape: string, project: string, includedInViews: View[], tags: string[], metadata: Record<string, string> }
1018
+ - type = "deployment-node": { id: string, name: string, kind: string, title: string, technology: string|null, shape: string, project: string, includedInViews: View[], tags: string[], metadata: Record<string, string> }
1019
+
1020
+ View (object) fields:
1021
+ - id: string — view identifier
1022
+ - title: string — view title
1023
+ - type: "element" | "deployment" | "dynamic"
1024
+
1025
+ Notes:
1026
+ - Read-only, idempotent.
1027
+ - Use results as input to other tools (e.g., read-element, read-view).
1028
+
1029
+ Example response:
1030
+ {
1031
+ "total": 1,
1032
+ "found": [
1033
+ {
1034
+ "type": "logical",
1035
+ "project": "default",
1036
+ "id": "shop.frontend",
1037
+ "name": "frontend",
1038
+ "kind": "container",
1039
+ "title": "Frontend",
1040
+ "technology": "React",
1041
+ "shape": "rectangle",
1042
+ "includedInViews": [
1043
+ {
1044
+ "id": "system-overview",
1045
+ "title": "System Overview",
1046
+ "type": "element"
1047
+ }
1048
+ ],
1049
+ "tags": ["public"],
1050
+ "metadata": {}
1051
+ }
1052
+ ]
1053
+ }
1054
+ `,inputSchema:{search:f.string().min(2,`Search must be at least 2 characters long`)},outputSchema:{total:f.number(),found:Q}},async(e,t)=>{let n=e.projects(),i=[],a=t.search.toLowerCase(),predicate;a.startsWith(`kind:`)?(a=a.slice(5),y.debug(`search by kind: {search}`,{search:a}),predicate=e=>e.kind.toLowerCase()===a):a.startsWith(`shape:`)?(a=a.slice(6),y.debug(`search by shape: {search}`,{search:a}),predicate=e=>e.shape.toLowerCase()===a):a.startsWith(`meta:`)?(a=a.slice(5),y.debug(`search by metadata: {search}`,{search:a}),predicate=e=>!!e.getMetadata(a)):a.startsWith(`#`)?(a=a.slice(1),y.debug(`search by tag: {search}`,{search:a}),predicate=e=>e.tags.some(e=>e.toLowerCase().includes(a))):(y.debug(`search by id/title: {search}`,{search:a}),predicate=e=>e.id.toLowerCase().includes(a)||e.title.toLowerCase().includes(a));for(let t of n)try{let n=await e.computedModel(t.id);for(let e of r(n.elements(),e=>!e.imported&&predicate(e)))i.push({type:`element`,project:t.id,id:e.id,name:e.name,kind:e.kind,title:e.title,technology:e.technology,shape:e.shape,tags:[...e.tags],metadata:e.getMetadata(),includedInViews:includedInViews(e.views())});for(let e of r(n.deployment.nodes(),predicate))i.push({type:`deployment-node`,project:t.id,id:e.id,name:e.name,kind:e.kind,title:e.title,technology:e.technology,shape:e.shape,tags:[...e.tags],metadata:e.getMetadata(),includedInViews:includedInViews(e.views())})}catch(e){y.error(`Error searching in project ${t.id}:`,{error:e})}return{total:i.length,found:i.slice(0,20)}}),ee=f.object({id:f.string().describe(`Element id (FQN)`),name:f.string().describe(`Element name`),kind:f.string().describe(`Element kind`),title:f.string().describe(`Human-readable title`),depth:f.number().describe(`Depth relative to the root element (1 = direct child)`),tags:f.array(f.string()).describe(`Assigned tags`),metadata:f.record(f.union([f.string(),f.array(f.string())])).describe(`Element metadata`),childCount:f.number().describe(`Number of direct children`),incomingCount:f.number().describe(`Number of incoming relationships`),outgoingCount:f.number().describe(`Number of outgoing relationships`)}),te=likec4Tool({name:`subgraph-summary`,description:`
1055
+ Get a compact, table-friendly summary of all descendants of a parent element.
1056
+ Returns each descendant with its depth, metadata, tags, and relationship counts in a single call.
1057
+ Much more efficient than calling read-element for each descendant individually.
1058
+
1059
+ Request:
1060
+ - elementId: string — parent element id (FQN) whose descendants to summarize
1061
+ - maxDepth: number (optional, default: 10, max: 20) — maximum depth of descendants to include
1062
+ - metadataKeys: string[] (optional) — if provided, only include these metadata keys in the response (reduces response size)
1063
+ - project: string (optional) — project id. Defaults to "default" if omitted.
1064
+
1065
+ Response (JSON object):
1066
+ - root: object — the root element summary
1067
+ - id: string — element id
1068
+ - kind: string — element kind
1069
+ - title: string — element title
1070
+ - childCount: number — number of direct children
1071
+ - descendants: Array of descendant summaries, each with:
1072
+ - id: string — element id (FQN)
1073
+ - name: string — element name
1074
+ - kind: string — element kind
1075
+ - title: string — human-readable title
1076
+ - depth: number — depth relative to root (1 = direct child)
1077
+ - tags: string[] — assigned tags
1078
+ - metadata: Record<string, string | string[]> — element metadata (filtered by metadataKeys if provided)
1079
+ - childCount: number — number of direct children
1080
+ - incomingCount: number — number of incoming relationships
1081
+ - outgoingCount: number — number of outgoing relationships
1082
+ - totalDescendants: number — total number of descendants (may differ from array length if truncated)
1083
+ - truncated: boolean — true if results were truncated due to exceeding the 200-result limit
1084
+ - truncatedByDepth: boolean — true if deeper descendants exist beyond maxDepth
1085
+
1086
+ Notes:
1087
+ - Read-only, idempotent, no side effects.
1088
+ - Safe to call repeatedly.
1089
+ - Limited to 200 descendants in the response.
1090
+ - Use metadataKeys to reduce response size when you only need specific metadata.
1091
+ - Descendants are returned in breadth-first order (closest to root first).
1092
+ - depth=1 means direct child, depth=2 means grandchild, etc.
1093
+
1094
+ Example response:
1095
+ {
1096
+ "root": {
1097
+ "id": "top.planner",
1098
+ "kind": "subsystem",
1099
+ "title": "Planner Subsystem",
1100
+ "childCount": 5
1101
+ },
1102
+ "descendants": [
1103
+ {
1104
+ "id": "top.planner.nodeA",
1105
+ "name": "nodeA",
1106
+ "kind": "cgf-node",
1107
+ "title": "nodeA :dwNodeTypeA",
1108
+ "depth": 1,
1109
+ "tags": ["is_in_dag", "target_asil_qm"],
1110
+ "metadata": { "target_asil": "QM", "safety_info_unit_asil": "QM" },
1111
+ "childCount": 0,
1112
+ "incomingCount": 3,
1113
+ "outgoingCount": 2
1114
+ }
1115
+ ],
1116
+ "totalDescendants": 5,
1117
+ "truncated": false,
1118
+ "truncatedByDepth": false
1119
+ }
1120
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Subgraph summary`},inputSchema:{elementId:f.string().describe(`Parent element id (FQN) whose descendants to summarize`),maxDepth:f.number().int().min(1).max(20).optional().default(10).describe(`Maximum depth of descendants to include (default: 10, max: 20)`),metadataKeys:f.array(f.string()).optional().describe(`If provided, only include these metadata keys in the response`),project:w},outputSchema:{root:f.object({id:f.string(),kind:f.string(),title:f.string(),childCount:f.number()}),descendants:f.array(ee),totalDescendants:f.number().describe(`Total number of descendants found`),truncated:f.boolean().describe(`True if results were truncated due to exceeding the limit`),truncatedByDepth:f.boolean().describe(`True if deeper descendants exist beyond maxDepth`)}},async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findElement(t.elementId);s(r,`Element "${t.elementId}" not found in project "${n}"`);let i=t.maxDepth,a=t.metadataKeys,o=[],c=0,l=!1,u=!1,d=[],f=[...r.children()];for(let e of f)d.push({element:e,depth:1});for(;d.length>0;){let{element:e,depth:t}=d.shift();if(t>i){u=!0;continue}c++;let n=[...e.children()];if(o.length<200){let r=e.getMetadata(),i=a?Object.fromEntries(a.filter(e=>e in r).map(e=>[e,r[e]])):r;o.push({id:e.id,name:e.name,kind:e.kind,title:e.title,depth:t,tags:[...e.tags],metadata:i,childCount:n.length,incomingCount:[...e.incoming()].length,outgoingCount:[...e.outgoing()].length})}else l=!0;for(let e of n)d.push({element:e,depth:t+1})}return{root:{id:r.id,kind:r.kind,title:r.title,childCount:f.length},descendants:o,totalDescendants:c,truncated:l,truncatedByDepth:u}});var MCPServerFactory=class{constructor(e){this.services=e}create(t){let n=this.services.shared.lsp.Connection!==void 0,r=new d({name:`LikeC4`,version:b},{instructions:`LikeC4 MCP – query and navigate LikeC4 models.
1121
+
1122
+ Conventions:
1123
+ - All tools are read-only and idempotent.
1124
+ - "project" is optional and defaults to "default".
1125
+
1126
+ Available tools:
1127
+ - list-projects — List all LikeC4 projects in the workspace.
1128
+ - read-project-summary — Project specification, configuration, all elements, deployment nodes and views. Input: { project? }.
1129
+ - search-element — Search elements and deployment nodes across all projects by id/title/kind/shape/tags/metadata. Input: { search }.
1130
+ - read-element — Full element details including relationships, includedInViews, deployedInstances, metadata and sourceLocation. Input: { id, project? }.
1131
+ - read-deployment — Details of a deployment node or deployed instance. Input: { id, project? }.
1132
+ - read-view — Full view details (nodes/edges) and sourceLocation. Input: { viewId, project? }.
1133
+ - find-relationships — Direct and indirect relationships between two elements in a project. Input: { element1, element2, project? }.
1134
+ - query-graph — Query element hierarchy (ancestors, descendants, siblings, children, parent) and relationships (incomers, outgoers). Input: { elementId, queryType, includeIndirect?, project? }.
1135
+ - query-incomers-graph — Get complete graph of all upstream dependencies/producers (recursive incomers). Much more efficient than repeated query-graph calls. Input: { elementId, includeIndirect?, maxDepth?, maxNodes?, project? }.
1136
+ - query-outgoers-graph — Get complete graph of all downstream consumers/dependents (recursive outgoers). Much more efficient than repeated query-graph calls. Input: { elementId, includeIndirect?, maxDepth?, maxNodes?, project? }.
1137
+ - query-by-metadata — Search elements by metadata key-value pairs with exact/contains/exists matching. Input: { key, value?, matchMode?, project? }.
1138
+ - query-by-tags — Advanced tag filtering with boolean logic (allOf, anyOf, noneOf). Input: { allOf?, anyOf?, noneOf?, project? }.
1139
+ - find-relationship-paths — Discover all paths (chains of relationships) between two elements with BFS traversal. Input: { sourceId, targetId, maxDepth?, includeIndirect?, project? }.
1140
+ - batch-read-elements — Read details of multiple elements in a single call, reducing round-trips. Input: { ids, project? }.
1141
+ - query-by-tag-pattern — Search elements by tag prefix/contains/suffix patterns. Input: { pattern, matchMode?, project? }.
1142
+ - element-diff — Compare two elements side-by-side showing differences in properties, tags, metadata, and relationships. Input: { element1Id, element2Id, project? }.
1143
+ - subgraph-summary — Compact summary of all descendants of a parent element with metadata, tags, and relationship counts. Input: { elementId, maxDepth?, metadataKeys?, project? }.
1144
+ ${n?`- open-view — Opens the LikeC4 view panel in the editor. Triggers UI; at most one preview panel at a time. Input: { viewId, project? }.`:``}
1145
+
1146
+ Instructions:
1147
+ - Identify the project first
1148
+ - Use "search-element" to find elements by id/title/kind/shape/tags/metadata and select the project
1149
+ - Use "read-project-summary" to find all elements and deployment nodes inside the project, what kinds, tags, metadata keys are available
1150
+ - Use "list-projects" to list all available projects
1151
+ - If response returns "sourceLocation", provide link to this location in the editor
1152
+
1153
+ Full documentation: https://likec4.dev/llms-full.txt
1154
+ `,enforceStrictCapabilities:!0,...t,capabilities:{tools:{},logging:{},...t?.capabilities}});return r.registerTool(...F(this.services.likec4.LanguageServices)),r.registerTool(...Y(this.services.likec4.LanguageServices)),r.registerTool(...J(this.services.likec4.LanguageServices)),r.registerTool(...q(this.services.likec4.LanguageServices)),r.registerTool(...Z(this.services.likec4.LanguageServices)),r.registerTool(...$(this.services.likec4.LanguageServices)),r.registerTool(...P(this.services.likec4.LanguageServices)),r.registerTool(...H(this.services.likec4.LanguageServices)),r.registerTool(...W(this.services.likec4.LanguageServices)),r.registerTool(...K(this.services.likec4.LanguageServices)),r.registerTool(...R(this.services.likec4.LanguageServices)),r.registerTool(...B(this.services.likec4.LanguageServices)),r.registerTool(...j(this.services.likec4.LanguageServices)),r.registerTool(...D(this.services.likec4.LanguageServices)),r.registerTool(...z(this.services.likec4.LanguageServices)),r.registerTool(...A(this.services.likec4.LanguageServices)),r.registerTool(...te(this.services.likec4.LanguageServices)),n&&r.registerTool(...I(this.services.likec4.LanguageServices)),r.server.onerror=t=>{e.error(o(t))},r}},StdioLikeC4MCPServer=class{transport=void 0;_mcp=void 0;constructor(e){this.services=e}get mcp(){if(!this._mcp)throw Error(`MCP server is not started`);return this._mcp}get isStarted(){return this.transport!==void 0}get port(){return NaN}async dispose(){await this.stop()}async start(){this.transport||(y.info(`Starting MCP stdio server`),this._mcp=this.services.mcp.ServerFactory.create(),this.transport=new p,await this._mcp.connect(this.transport),y.info(`LikeC4 MCP Server running on stdio`))}async stop(){if(this.transport)try{y.info(`Stopping MCP stdio server`),await this.transport.close(),this._mcp&&await this._mcp.close()}finally{this._mcp=void 0,this.transport=void 0}}};async function createHonoApp(e){let t=new _;t.use(`*`,v({origin:`*`,allowHeaders:[`Content-Type`,`mcp-session-id`,`Last-Event-ID`,`mcp-protocol-version`],exposeHeaders:[`mcp-session-id`,`mcp-protocol-version`]})),t.get(`/health`,e=>e.json({status:`ok`}));let n=e.create(),r=new h({eventStore:new m({})});return t.all(`/mcp`,async e=>(n.isConnected()||await n.connect(r),await r.handleRequest(e))),t.notFound(e=>(y.debug(`${e.req.method} ${e.req.url} not found`),e.json({jsonrpc:`2.0`,error:{code:-32e3,message:`Method not found.`},id:null},{status:404}))),t.onError((e,t)=>(y.error(o(e)),t.json({jsonrpc:`2.0`,error:{code:-32603,message:`Internal server error`},id:null},{status:500}))),t}async function startServer(e){let{factory:t,port:n}=e,r=await createHonoApp(t);return new Promise((e,t)=>{let i=g({fetch:r.fetch,hostname:`0.0.0.0`,port:n}).prependOnceListener(`error`,t).prependOnceListener(`listening`,()=>{i.removeListener(`error`,t),e(i.unref())})})}var StreamableLikeC4MCPServer=class{server=void 0;constructor(e,t=33335){this.services=e,this._port=t}get mcp(){throw Error(`StreamableLikeC4MCPServer has access to McpServer only during the request`)}get isStarted(){return this.server?.listening===!0}get port(){return this._port}async dispose(){await this.stop()}async start(e=this._port){if(this.server){if(this.port===e)return;await this.stop()}y.info(`Starting MCP server on port {port}`,{port:e}),this._port=e,this.server=await startServer({factory:this.services.mcp.ServerFactory,port:e}),y.info(`MCP server ready at http://0.0.0.0:{port}/mcp`,{port:e})}stop(){let e=this.server;return e?(y.info(`Stopping MCP server`),this.server=void 0,new Promise(t=>{e.close(e=>{e?y.error(`Failed to stop MCP server`,{err:e}):y.info(`MCP server stopped`),t()})})):(y.info(`MCP server is not running, nothing to stop`),Promise.resolve())}};function streamableLikeC4MCPServer(e,t=33335){y.debug(`Creating StreamableLikeC4MCPServer`);let n=new StreamableLikeC4MCPServer(e,t),r=e.LanguageMetaData.languageId,i=e.shared.lsp.Connection;return e.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate(e=>{if(e.section!==r){y.warn(`Unexpected configuration update: {update}`,{update:e});return}let{enabled:a=!1,port:s=t}=e.configuration.mcp;if(!a){n.stop();return}Promise.resolve().then(()=>n.start(s)).then(()=>{i?.telemetry?.logEvent({eventName:`mcp-server-started`,mcpPort:s})}).catch(e=>{let t=o(e);i?.telemetry?.logEvent({eventName:`mcp-server-start-failed`,mcpPort:s,message:t}),y.warn(`Failed to start LikeC4 MCP Server: \n${t}`),i&&i.window.showErrorMessage(`LikeC4: Failed to start MCP Server\n\n${t}`)})}),n}function stdioLikeC4MCPServer(e){return new StdioLikeC4MCPServer(e)}function WithMCPServer(e=`sse`){return{mcpServer:t=>e===`stdio`?stdioLikeC4MCPServer(t):streamableLikeC4MCPServer(t,typeof e==`object`?e.port:33335),mcpServerFactory:e=>new MCPServerFactory(e)}}export{WithMCPServer as t};