@likec4/language-server 1.54.0 → 1.55.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.
- package/dist/_chunks/LikeC4FileSystem.mjs +2 -2
- package/dist/_chunks/module.d.mts +12 -41
- package/dist/_chunks/module.mjs +20 -20
- package/dist/_chunks/utils.mjs +1 -1
- package/dist/browser/index.d.mts +2 -2
- package/dist/browser/index.mjs +1 -1
- package/dist/browser/worker.mjs +1 -1
- package/dist/filesystem/index.d.mts +1 -1
- package/dist/filesystem/index.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/module.d.mts +2 -2
- package/dist/module.mjs +1 -1
- package/package.json +15 -27
- package/src/test/index.ts +1 -0
- package/src/test/testServices.ts +258 -0
- package/dist/_chunks/mcp.mjs +0 -1154
- package/dist/_chunks/noop.mjs +0 -1
- package/dist/_chunks/noop2.mjs +0 -1
- package/dist/mcp/index.d.mts +0 -3
- package/dist/mcp/index.mjs +0 -1
- package/mcp/package.json +0 -4
package/dist/_chunks/mcp.mjs
DELETED
|
@@ -1,1154 +0,0 @@
|
|
|
1
|
-
import{l as e,oi as t}from"./utils.mjs";import{ifilter as n,invariant as r,isSameHierarchy as i}from"@likec4/core/utils";import{loggable as a}from"@likec4/log";import{invariant as o}from"@likec4/core";import{keys as s}from"remeda";import{URI as c}from"vscode-uri";import{isDeploymentNodeModel as l,modelConnection as u}from"@likec4/core/model";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=t.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:a(t)}],isError:!0}}})}var b=`1.54.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){o(e.findElement(t),`Element "${t}" not found`);let s=new Set,c={},l=0,u=!1,d=[{elementId:t,depth:0}];for(;d.length>0;){let{elementId:t,depth:o}=d.shift();if(o>i||s.has(t))continue;if(s.size>=a){u=!0;break}let f=e.findElement(t);if(!f)continue;s.add(t),l=Math.max(l,o);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:o};for(let e of p)s.has(e.elementId)||d.push({elementId:e.elementId,depth:o+1})}for(let e of Object.values(c))e.neighbors=e.neighbors.filter(e=>e.elementId in c);return{target:t,totalNodes:s.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(e.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:c.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)=>{o(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);o(i,`Element "${t.element1Id}" not found in project "${n}"`);let a=r.findElement(t.element2Id);o(a,`Element "${t.element2Id}" not found in project "${n}"`);let s=[],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&&s.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:s,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);o(i,`Source element "${t.sourceId}" not found in project "${n}"`);let a=r.findElement(t.targetId);o(a,`Target element "${t.targetId}" not found in project "${n}"`),o(i.id!==a.id,`Source and target must be different elements`);let s=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>=s)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(i(t.element1,t.element2))throw Error(`No relationships possible between parent-child`);let a=[],o=await e.computedModel(n),s=o.findElement(t.element1);r(s,`Element "${t.element1}" not found in project "${n}"`);let c=o.findElement(t.element2);r(c,`Element "${t.element2}" not found in project "${n}"`);let l=mkLocate(e,n),d=u.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;a.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:l({relation:e.id})})}return{found:a}}),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(!l(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)=>{o(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}l(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);o(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);o(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),s={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;s[e]={...r,incomers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:s}}),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);o(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),s={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;s[e]={...r,outgoers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:s}}),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);o(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);o(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,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=e.project(n),i=await e.computedModel(n);return{title:r.title,folder:r.folder.fsPath,sources:r.documents?.map(e=>e.fsPath)??[],config:serializeConfig(r.config),specification:{elementKinds:s(i.specification.elements),relationshipKinds:s(i.specification.relationships),deploymentKinds:s(i.specification.deployments),tags:[...i.tags],metadataKeys:i.specification.metadataKeys??[]},elements:[...i.elements()].filter(e=>!e.imported).map(e=>({id:e.id,kind:e.kind,title:e.title,tags:[...e.tags]})),deployments:[...i.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:[...i.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 r=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 r)try{let r=await e.computedModel(t.id);for(let e of n(r.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 n(r.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);o(r,`Element "${t.elementId}" not found in project "${n}"`);let i=t.maxDepth,a=t.metadataKeys,s=[],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(s.length<200){let r=e.getMetadata(),i=a?Object.fromEntries(a.filter(e=>e in r).map(e=>[e,r[e]])):r;s.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:s,totalDescendants:c,truncated:l,truncatedByDepth:u}});var MCPServerFactory=class{constructor(e){this.services=e}create(e){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,...e,capabilities:{tools:{},logging:{},...e?.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=e=>{t.error(a(e))},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(a(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:o=!1,port:s=t}=e.configuration.mcp;if(!o){n.stop();return}Promise.resolve().then(()=>n.start(s)).then(()=>{i?.telemetry?.logEvent({eventName:`mcp-server-started`,mcpPort:s})}).catch(e=>{let t=a(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};
|