@likec4/mcp 1.59.2 → 1.59.4

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.
@@ -1,9 +1,33 @@
1
- import{configureLanguageServerLogger as e}from"@likec4/language-server";import{fromWorkspace as t}from"@likec4/language-services/node";import{defu as n}from"defu";import{resolve as r}from"node:path";import{isDevelopment as i}from"std-env";import a from"tinyrainbow";import{createContext as o}from"unctx";import{StdioServerTransport as s}from"@modelcontextprotocol/sdk/server/stdio.js";import{hasProp as c,invariant as l}from"@likec4/core";import{loggable as u,logger as d}from"@likec4/log";import{isEmptyish as f,keys as p,pipe as m,piped as h,prop as g}from"remeda";import{McpServer as _,ResourceTemplate as v}from"@modelcontextprotocol/sdk/server/mcp.js";import{completable as y}from"@modelcontextprotocol/sdk/server/completable.js";import*as b from"zod/v3";import{enhanceLayoutWithAI as x}from"@likec4/layouts/ai";import{URI as S}from"vscode-uri";import{isDeploymentNodeModel as C,modelConnection as w}from"@likec4/core/model";import{ifilter as T,invariant as E,isSameHierarchy as D}from"@likec4/core/utils";import{MemoryEventStore as O,StreamableHTTPTransport as k}from"@hono/mcp";import{serve as A}from"@hono/node-server";import{Hono as j}from"hono";import{cors as ee}from"hono/cors";var M=Object.defineProperty,__name=(e,t)=>M(e,`name`,{value:t,configurable:!0});const N=o(),P=o();function useLanguageServices(){return P.use()}function setMcpServerCtx(e){return e?N.set(e,!0):N.unset(),e}function setLanguageServicesCtx(e){return e?P.set(e,!0):P.unset(),e}const F=d.getChild(`mcp`);function likec4Tool(e){return t=>n=>{let{name:r,description:i,...a}=e,o={description:i?.trim()??``,...a};return n.registerTool(r,o,async(...e)=>{let n=useLanguageServices();F.debug(`Calling tool {name}, args: {args}`,{name:r,args:e});try{let r=await t.apply(null,[n,...e]);return typeof r==`string`?{content:[{type:`text`,text:r}]}:f(o.outputSchema)?c(r,`content`)?r:{content:[{type:`text`,text:JSON.stringify(r)}]}:{content:[],structuredContent:r}}catch(e){return F.error(`Tool ${r} failed`,{err:e}),{content:[{type:`text`,text:u(e)}],isError:!0}}}),n}}var I=`1.59.2`;function applySemanticLayoutPrompt(e){let t=useLanguageServices();return e.registerPrompt(`apply_semantic_layout`,{title:`Prepare prompt for applying semantic layout to a likec4 view`,argsSchema:{projectId:y(b.string().describe(`Project id (optional, will use "default" if not specified)`),(e=``)=>t.projects().filter(t=>t.id.startsWith(e)).map(g(`id`))),viewId:y(b.string().describe(`View ID`),async(n=``,r)=>{let i=r?.arguments?.projectId??t.projectsManager.default.id,a=await t.computedModel(i);return a?Array.from(a.views()).filter(e=>e.id.startsWith(n)).map(g(`id`)):(await e.sendLoggingMessage({level:`warning`,data:`Completing viewId for project ${i} with input value "${n}", model not found`}),[])})}},async({viewId:e,projectId:t},n)=>({messages:[{role:`user`,content:{type:`text`,text:["Call `apply-semantic-layout` tool from `likec4` MCP with these arguments:","projectId: `"+t+"`","viewId: `"+e+"`"].join(`
2
- `)}}]})),e}function projectResource(e){let t=useLanguageServices();return e.registerResource(`likec4-project`,new v(`likec4://project/{projectId}`,{list:async()=>({resources:t.projects().map(e=>({uri:`likec4://project/${encodeURIComponent(e.id)}`,name:e.title}))}),complete:{projectId:e=>{if(!e)return t.projects().map(g(`id`));let n=e.toLowerCase();return t.projects().filter(e=>e.id.toLowerCase().includes(n)).map(g(`id`))}}}),{description:`LikeC4 project resource`,mimeType:`application/json`},async(e,{projectId:n})=>{if(!n)return{contents:[]};let r=typeof n==`string`?[n]:n;return{contents:t.projects().filter(e=>r.includes(e.id)).map(e=>({uri:`likec4://project/${encodeURIComponent(e.id)}`,text:JSON.stringify({id:e.id,title:e.title,folder:e.folder.fsPath})}))}}),e}const L=b.object({name:b.string().describe(`Project identifier`),title:b.string().optional().describe(`Human-readable project title`),contactPerson:b.string().optional().describe(`Maintainer contact information`),metadata:b.record(b.string(),b.unknown()).optional().describe(`Custom project metadata as key-value pairs`),extends:b.union([b.string(),b.array(b.string())]).optional().describe(`Style inheritance paths`),exclude:b.array(b.string()).optional().describe(`File exclusion patterns`),include:b.object({paths:b.array(b.string()).describe(`Include paths`),maxDepth:b.number().describe(`Maximum directory depth`),fileThreshold:b.number().describe(`File threshold`)}).optional().describe(`Include configuration`),manualLayouts:b.object({outDir:b.string().describe(`Output directory for manual layouts`)}).optional().describe(`Manual layouts configuration`),styles:b.object({hasTheme:b.boolean().describe(`Whether theme customization is defined`),hasDefaults:b.boolean().describe(`Whether default style values are defined`),hasCustomCss:b.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 R=b.object({id:b.string().describe(`Element id (FQN)`),name:b.string().describe(`Element name`),kind:b.string().describe(`Element kind`),title:b.string(),tags:b.array(b.string()),metadata:b.record(b.union([b.string(),b.array(b.string())])),includedInViews:b.array(b.object({id:b.string().describe(`View id`),title:b.string().describe(`View title`),type:b.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){l(e.findElement(t),`Element "${t}" not found`);let o=new Set,s={},c=0,u=!1,d=[{elementId:t,depth:0}];for(;d.length>0;){let{elementId:t,depth:l}=d.shift();if(l>i||o.has(t))continue;if(o.size>=a){u=!0;break}let f=e.findElement(t);if(!f)continue;o.add(t),c=Math.max(c,l);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});s[t]={...serializeElement(f),neighbors:p,depth:l};for(let e of p)o.has(e.elementId)||d.push({elementId:e.elementId,depth:l+1})}for(let e of Object.values(s))e.neighbors=e.neighbors.filter(e=>e.elementId in s);return{target:t,totalNodes:o.size,maxDepth:c,truncated:u,nodes:s}}const z=b.object({path:b.string().describe(`Path to the file`),range:b.object({start:b.object({line:b.number(),character:b.number()}),end:b.object({line:b.number(),character:b.number()})}).describe(`Range in the file`)}).nullable(),B=b.string().refine(e=>!0).default(`default`).describe(`Project id (optional, will use "default" if not specified)`),V=b.array(b.object({id:b.string().describe(`View id`),title:b.string().describe(`View title`),type:b.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:S.parse(r.uri).fsPath,range:r.range}:null}catch(e){return F.debug(`Failed to locate {params}`,{error:e,params:n}),null}};function toolError(e){return{isError:!0,content:[{type:`text`,text:e}]}}function applySemanticLayoutTool(e){let t=useLanguageServices();return e.registerTool(`apply-semantic-layout`,{description:`Apply semantic layout to the likec4 view`,inputSchema:{projectId:y(b.string().default(`default`).describe(`Project id (optional, will use "default" if not specified)`),(e=``)=>t.projects().filter(t=>t.id.startsWith(e)).map(g(`id`))),viewId:y(b.string().describe(`View ID to apply semantic layout to`),async(e,n)=>{let r=n?.arguments?.projectId??t.projectsManager.default.id,i=await t.computedModel(r);if(!i)return[];let a=e??``;return Array.from(i.views()).filter(e=>e.id.startsWith(a)).map(g(`id`))})},outputSchema:{reasoning:b.string().describe(`Reasoning behind the layout changes`),snapshotUri:b.string().nullish().describe(`Where snapshot was saved (after applying layout)`)}},async(n,r)=>{let i=t.projectsManager.ensureProjectId(n.projectId);i!==n.projectId&&await e.sendLoggingMessage({level:`notice`,data:`Using project "${i}" instead of "${n.projectId}"`},r.sessionId);let a=await t.computedModel(i),o=a.findView(n.viewId);if(!o)return toolError(`View "${n.viewId}" not found in project "${i}"`);let s=await x(o.$view,{name:`MCP`,async sendRequest({systemPrompt:e,userPrompt:t,diagram:n}){let i=await r.sendRequest({method:`sampling/createMessage`,params:{maxTokens:2e4,includeContext:`none`,systemPrompt:e,messages:[{role:`user`,content:{type:`text`,text:t+`
1
+ import{configureLanguageServerLogger as e}from"@likec4/language-server";import{fromSources as t,fromWorkspace as n}from"@likec4/language-services/node";import{defu as r}from"defu";import{relative as i,resolve as a,sep as s}from"node:path";import{isDevelopment as c}from"std-env";import l from"tinyrainbow";import{createContext as u}from"unctx";import{StdioServerTransport as f}from"@modelcontextprotocol/sdk/server/stdio.js";import*as p from"zod/v4";import{z as m}from"zod/v4";import{FqnRef as h,GlobalFqn as g,hasProp as _,invariant as v}from"@likec4/core";import{loggable as y,logger as b}from"@likec4/log";import{isEmptyish as x,keys as S,pipe as C,piped as w,prop as T}from"remeda";import{McpServer as E,ResourceTemplate as D}from"@modelcontextprotocol/sdk/server/mcp.js";import{completable as O}from"@modelcontextprotocol/sdk/server/completable.js";import*as k from"zod/v3";import{RESOURCE_MIME_TYPE as A,registerAppResource as j,registerAppTool as M}from"@modelcontextprotocol/ext-apps/server";import{existsSync as N,readFileSync as P}from"node:fs";import{fileURLToPath as ee}from"node:url";import{MaxZoom as te,MinZoom as ne}from"@likec4/diagram";import{URI as F}from"vscode-uri";import{enhanceLayoutWithAI as I}from"@likec4/layouts/ai";import{isDeploymentNodeModel as L,modelConnection as re}from"@likec4/core/model";import{ifilter as R,invariant as z,isSameHierarchy as ie}from"@likec4/core/utils";import{ProjectId as ae}from"@likec4/core/types";import{randomUUID as oe}from"node:crypto";import{MemoryEventStore as se,StreamableHTTPTransport as ce}from"@hono/mcp";import{serve as le}from"@hono/node-server";import{Hono as ue}from"hono";import{cors as de}from"hono/cors";var B=Object.defineProperty,__name=(e,t)=>B(e,`name`,{value:t,configurable:!0});const V=u(),H=u();function useLanguageServices(){return H.use()}function setMcpServerCtx(e){return e?V.set(e,!0):V.unset(),e}function setLanguageServicesCtx(e){return e?H.set(e,!0):H.unset(),e}const U=b.getChild(`mcp`);function mcpToolSchema(e){return m.object(e).meta({$schema:`https://json-schema.org/draft/2020-12/schema`})}function likec4Tool(e){return t=>n=>{let{name:r,description:i,inputSchema:a,outputSchema:s,...c}=e,l=s!==void 0&&!x(s),u={description:i?.trim()??``,...c,...a===void 0?{}:{inputSchema:mcpToolSchema(a)},...s===void 0?{}:{outputSchema:mcpToolSchema(s)}};return n.registerTool(r,u,async(...e)=>{let n=useLanguageServices();U.debug(`Calling tool {name}, args: {args}`,{name:r,args:e});try{let r=await t.apply(null,[n,...e]);return typeof r==`string`?{content:[{type:`text`,text:r}]}:l?{content:[],structuredContent:r}:_(r,`content`)?r:{content:[{type:`text`,text:JSON.stringify(r)}]}}catch(e){return U.error(`Tool ${r} failed`,{err:e}),{content:[{type:`text`,text:y(e)}],isError:!0}}}),n}}var fe=`1.59.4`;function applySemanticLayoutPrompt(e){let t=useLanguageServices();return e.registerPrompt(`apply_semantic_layout`,{title:`Prepare prompt for applying semantic layout to a likec4 view`,argsSchema:{projectId:O(k.string().describe(`Project id (optional, will use "default" if not specified)`),(e=``)=>t.projects().filter(t=>t.id.startsWith(e)).map(T(`id`))),viewId:O(k.string().describe(`View ID`),async(n=``,r)=>{let i=r?.arguments?.projectId??t.projectsManager.default.id,a=await t.computedModel(i);return a?Array.from(a.views()).filter(e=>e.id.startsWith(n)).map(T(`id`)):(await e.sendLoggingMessage({level:`warning`,data:`Completing viewId for project ${i} with input value "${n}", model not found`}),[])})}},async({viewId:e,projectId:t},n)=>({messages:[{role:`user`,content:{type:`text`,text:["Call `apply-semantic-layout` tool from `likec4` MCP with these arguments:","projectId: `"+t+"`","viewId: `"+e+"`"].join(`
2
+ `)}}]})),e}function projectResource(e){let t=useLanguageServices();return e.registerResource(`likec4-project`,new D(`likec4://project/{projectId}`,{list:async()=>({resources:t.projects().map(e=>({uri:`likec4://project/${encodeURIComponent(e.id)}`,name:e.title}))}),complete:{projectId:e=>{if(!e)return t.projects().map(T(`id`));let n=e.toLowerCase();return t.projects().filter(e=>e.id.toLowerCase().includes(n)).map(T(`id`))}}}),{description:`LikeC4 project resource`,mimeType:`application/json`},async(e,{projectId:n})=>{if(!n)return{contents:[]};let r=typeof n==`string`?[n]:n;return{contents:t.projects().filter(e=>r.includes(e.id)).map(e=>({uri:`likec4://project/${encodeURIComponent(e.id)}`,text:JSON.stringify({id:e.id,title:e.title,folder:e.folder.fsPath})}))}}),e}function readDistAsset(e){let t=[new URL(`../dist/${e}`,import.meta.url),new URL(`../../dist/${e}`,import.meta.url)];for(let e of t){let t=ee(e);if(N(t))return P(t,`utf-8`)}throw Error(`Could not locate dist asset '${e}'; tried: ${t.map(String).join(`, `)}`)}function readRenderViewClientBundle(){return readDistAsset(`app/render-view-client.js`)}function readRenderViewClientStyles(){return readDistAsset(`app/render-view-client.css`)}const pe=p.object({name:p.string().describe(`Project identifier`),title:p.string().optional().describe(`Human-readable project title`),contactPerson:p.string().optional().describe(`Maintainer contact information`),metadata:p.record(p.string(),p.unknown()).optional().describe(`Custom project metadata as key-value pairs`),extends:p.union([p.string(),p.array(p.string())]).optional().describe(`Style inheritance paths`),exclude:p.array(p.string()).optional().describe(`File exclusion patterns`),include:p.object({paths:p.array(p.string()).describe(`Include paths`),maxDepth:p.number().describe(`Maximum directory depth`),fileThreshold:p.number().describe(`File threshold`)}).optional().describe(`Include configuration`),manualLayouts:p.object({outDir:p.string().describe(`Output directory for manual layouts`)}).optional().describe(`Manual layouts configuration`),styles:p.object({hasTheme:p.boolean().describe(`Whether theme customization is defined`),hasDefaults:p.boolean().describe(`Whether default style values are defined`),hasCustomCss:p.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 W=p.object({id:p.string().describe(`Element id (FQN)`),name:p.string().describe(`Element name`),kind:p.string().describe(`Element kind`),title:p.string(),tags:p.array(p.string()),metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])),includedInViews:p.array(p.object({id:p.string().describe(`View id`),title:p.string().describe(`View title`),type:p.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){let s=e.findElement(t);v(s,`Element "${t}" not found`);let c=new Set,l={},u=0,f=!1,p=[{elementId:t,depth:0}];for(;p.length>0;){let{elementId:t,depth:s}=p.shift();if(s>i||c.has(t))continue;if(c.size>=a){f=!0;break}let m=e.findElement(t);if(!m)continue;c.add(t),u=Math.max(u,s);let h=(n===`incoming`?[...m.incoming(r)]:[...m.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});l[t]={...serializeElement(m),neighbors:h,depth:s};for(let e of h)c.has(e.elementId)||p.push({elementId:e.elementId,depth:s+1})}for(let e of Object.values(l))e.neighbors=e.neighbors.filter(e=>e.elementId in l);return{target:t,totalNodes:c.size,maxDepth:u,truncated:f,nodes:l}}const G=p.object({path:p.string().describe(`Path to the file`),range:p.object({start:p.object({line:p.number(),character:p.number()}),end:p.object({line:p.number(),character:p.number()})}).describe(`Range in the file`)}).nullable(),K=p.array(p.object({title:p.string().nullable().describe(`Optional link title`),url:p.string().describe(`Link URL`),relative:p.string().nullable().describe(`Relative path (if URL is relative to workspace root)`)})).describe(`External links associated with this element`);function serializeLinks(e){return(e.links??[]).map(e=>({title:e.title??null,url:e.url,relative:e.relative??null}))}const q=p.string().refine(e=>!0).default(`default`).describe(`Project id (optional, will use "default" if not specified)`),J=p.array(p.object({id:p.string().describe(`View id`),title:p.string().describe(`View title`),type:p.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:F.parse(r.uri).fsPath,range:r.range}:null}catch(e){return U.debug(`Failed to locate {params}`,{error:e,params:n}),null}};function toolError(e){return{isError:!0,content:[{type:`text`,text:e}]}}function pickRecord(e,t){return Object.fromEntries(Object.entries(e).filter(([e])=>t.has(e)))}function buildViewScopedModel(e,t){let n=new Set,r=new Set,i=new Set,a=new Set,includeElement=t=>{let r=e.element(t);n.add(r.id);for(let e of r.ancestors())n.add(e.id)},includeDeployment=t=>{let n=e.deployment.element(t);r.add(n.id);for(let e of n.ancestors())r.add(e.id);n.isInstance()&&includeElement(n.element.id)},includeDeploymentRef=e=>{includeDeployment(e.deployment),h.isInsideInstanceRef(e)&&includeElement(e.element)};t._type===`element`&&t.viewOf&&includeElement(t.viewOf);for(let e of t.nodes)e.modelRef&&includeElement(e.modelRef),e.deploymentRef&&includeDeployment(e.deploymentRef);for(let n of t.edges)for(let t of n.relations){let n=e.relationship(t);n.isModelRelation()?(i.add(n.id),includeElement(n.source.id),includeElement(n.target.id)):(a.add(n.id),includeDeploymentRef(n.$relationship.source),includeDeploymentRef(n.$relationship.target))}let s=e.$data,c=Object.fromEntries(Object.entries(s.imports).flatMap(([e,t])=>{let r=t.filter(t=>n.has(g(e,t.id)));return r.length>0?[[e,r]]:[]}));return{...s,elements:pickRecord(s.elements,n),imports:c,relations:pickRecord(s.relations,i),deployments:{elements:pickRecord(s.deployments.elements,r),relations:pickRecord(s.deployments.relations,a)},views:{[t.id]:t},manualLayouts:{}}}function inlineLocalSvgIcon(e){if(typeof e!=`string`||!e.startsWith(`file:`)||!e.toLowerCase().split(/[?#]/)[0]?.endsWith(`.svg`))return e;try{return`data:image/svg+xml,${encodeURIComponent(P(F.parse(e).fsPath,`utf8`))}`}catch{return null}}function inlineLocalSvgIcons(e){return Array.isArray(e)?e.map(inlineLocalSvgIcons):e&&typeof e==`object`?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,e===`icon`?inlineLocalSvgIcon(t):inlineLocalSvgIcons(t)])):e}function buildRenderPayload(e){let t=e.fullModel?{...e.model.$data,views:{...e.model.$data.views,[e.viewId]:e.layoutedView}}:buildViewScopedModel(e.model,e.layoutedView);return{id:e.viewId,title:e.title,project:e.projectId,view:inlineLocalSvgIcons(e.layoutedView),model:inlineLocalSvgIcons(t)}}const Y=`ui://likec4/render-view.html`,X=p.object({size:p.enum([`compact`,`standard`,`large`]).default(`standard`),fitView:p.boolean().default(!0),initialZoom:p.number().min(ne).max(te).optional()}).default({size:`standard`,fitView:!0});function renderViewTool(e){return M(e,`render-view`,{title:`Render View`,description:`Renders a LikeC4 view as an interactive diagram (pan/zoom/fit) inline in the chat.
3
3
 
4
- `+n}}]}},b.object({content:b.object({type:b.literal(`text`),text:b.string()})}));return i.content.type===`text`?i.content.text:JSON.stringify(i.content)}},r.signal);if(!s)return await e.sendLoggingMessage({level:`error`,data:`Failed to generate layout hints`},r.sessionId),toolError(`Failed to generate layout hints`);await e.sendLoggingMessage({level:`info`,data:`Applying semantic layout...`},r.sessionId);let c=await t.views.layouter.aiLayout({view:o.$view,styles:a.$styles},s),l=await t.editor.applyChange({change:{op:`save-view-snapshot`,layout:c.diagram},viewId:o.id,projectId:i});return await e.sendLoggingMessage({level:`info`,data:`Layout applied successfully`},r.sessionId),{content:[],structuredContent:{reasoning:s.reasoning,...!!l.location&&{snapshotUri:l.location.uri.toString()}}}}),e}const H=R.extend({description:b.string().nullable().describe(`Element description`),technology:b.string().nullable().describe(`Element technology`),shape:b.string().describe(`Rendered shape`),color:b.string().describe(`Rendered color`),children:b.array(b.string()).describe(`Direct child element ids`),incomingCount:b.number().describe(`Number of incoming relationships`),outgoingCount:b.number().describe(`Number of outgoing relationships`)}),U=likec4Tool({name:`batch-read-elements`,description:`
4
+ Request:
5
+ - viewId: string — view id (name)
6
+ - project: string (optional) — project id. Defaults to "default" if omitted.
7
+ - fullModel: boolean (optional) — include the complete model instead of data scoped to this view. Defaults to false.
8
+ - render.size: "compact", "standard", or "large" (optional) — initial canvas size hint. Defaults to "standard".
9
+ - render.fitView: boolean (optional) — fit the diagram into the canvas initially. Defaults to true.
10
+ - render.initialZoom: number (optional) — initial zoom level. This overrides render.fitView.
11
+
12
+ Use this when the user wants to *see* a view. Use "read-view" instead when only the view's structure (nodes/edges) is needed.`,inputSchema:mcpToolSchema({viewId:p.string().describe(`View id (name)`),project:q,fullModel:p.boolean().default(!1).describe(`Include the complete model instead of data scoped to this view`),render:X}),outputSchema:mcpToolSchema({id:p.string(),title:p.string(),project:p.string(),view:p.record(p.string(),p.unknown()).describe(`Layouted view (nodes, edges, bounds), consumed by the paired render-view UI`),model:p.record(p.string(),p.unknown()).describe(`Layouted model data (specification, elements, relations, deployments), scoped to this view by default; complete when fullModel is true. Consumed by the paired UI to build a LikeC4Model for LikeC4ModelProvider — LikeC4Diagram requires one in context.`),render:X}),annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Render view`},_meta:{ui:{resourceUri:Y}}},async e=>{let t=useLanguageServices(),n=t.projectsManager.ensureProjectId(e.project),r=await t.layoutedModel(n),i=r.findView(e.viewId);if(!i)return toolError(`View with ID '${e.viewId}' not found in project ${n}`);if(!i.isLayouted())return toolError(`View '${e.viewId}' has no layout`);let a=i.$layouted,s=i.title??i.id;return{content:[{type:`text`,text:`Rendered view "${s}"`}],structuredContent:{...buildRenderPayload({projectId:n,viewId:i.id,title:s,layoutedView:a,model:r,fullModel:e.fullModel}),render:e.render}}}),e}function renderHtml(e,t){return`<!doctype html>
13
+ <html>
14
+ <head>
15
+ <meta charset="utf-8" />
16
+ <style>${t}</style>
17
+ <style>
18
+ html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
19
+ #root { width: 100%; height: 100%; }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <div id="root"></div>
24
+ <script>${e}<\/script>
25
+ </body>
26
+ </html>`}function renderViewResource(e){return j(e,`Render View UI`,Y,{description:`Interactive diagram UI for the render-view MCP App tool.`},async()=>({contents:[{uri:Y,mimeType:A,text:renderHtml(readRenderViewClientBundle(),readRenderViewClientStyles())}]})),e}function applySemanticLayoutTool(e){let t=useLanguageServices();return e.registerTool(`apply-semantic-layout`,{description:`Apply semantic layout to the likec4 view`,inputSchema:mcpToolSchema({projectId:O(p.string().default(`default`).describe(`Project id (optional, will use "default" if not specified)`),(e=``)=>t.projects().filter(t=>t.id.startsWith(e)).map(T(`id`))),viewId:O(p.string().describe(`View ID to apply semantic layout to`),async(e,n)=>{let r=n?.arguments?.projectId??t.projectsManager.default.id,i=await t.computedModel(r);if(!i)return[];let a=e??``;return Array.from(i.views()).filter(e=>e.id.startsWith(a)).map(T(`id`))})}),outputSchema:mcpToolSchema({reasoning:p.string().describe(`Reasoning behind the layout changes`),snapshotUri:p.string().nullish().describe(`Where snapshot was saved (after applying layout)`)})},async(n,r)=>{let i=t.projectsManager.ensureProjectId(n.projectId);i!==n.projectId&&await e.sendLoggingMessage({level:`notice`,data:`Using project "${i}" instead of "${n.projectId}"`},r.sessionId);let a=await t.computedModel(i),s=a.findView(n.viewId);if(!s)return toolError(`View "${n.viewId}" not found in project "${i}"`);let c=await I(s.$view,{name:`MCP`,async sendRequest({systemPrompt:e,userPrompt:t,diagram:n}){let i=await r.sendRequest({method:`sampling/createMessage`,params:{maxTokens:2e4,includeContext:`none`,systemPrompt:e,messages:[{role:`user`,content:{type:`text`,text:t+`
27
+
28
+ `+n}}]}},p.object({content:p.object({type:p.literal(`text`),text:p.string()})}));return i.content.type===`text`?i.content.text:JSON.stringify(i.content)}},r.signal);if(!c)return await e.sendLoggingMessage({level:`error`,data:`Failed to generate layout hints`},r.sessionId),toolError(`Failed to generate layout hints`);await e.sendLoggingMessage({level:`info`,data:`Applying semantic layout...`},r.sessionId);let l=await t.views.layouter.aiLayout({view:s.$view,styles:a.$styles},c),u=await t.editor.applyChange({change:{op:`save-view-snapshot`,layout:l.diagram},viewId:s.id,projectId:i});return await e.sendLoggingMessage({level:`info`,data:`Layout applied successfully`},r.sessionId),{content:[],structuredContent:{reasoning:c.reasoning,...!!u.location&&{snapshotUri:u.location.uri.toString()}}}}),e}const me=W.extend({description:p.string().nullable().describe(`Element description`),technology:p.string().nullable().describe(`Element technology`),shape:p.string().describe(`Rendered shape`),color:p.string().describe(`Rendered color`),children:p.array(p.string()).describe(`Direct child element ids`),incomingCount:p.number().describe(`Number of incoming relationships`),outgoingCount:p.number().describe(`Number of outgoing relationships`),links:K,sourceLocation:G}),he=likec4Tool({name:`batch-read-elements`,description:`
5
29
  Read details of multiple elements in a single call, reducing round-trips.
6
- Returns a compact summary for each element including metadata, description, technology, shape, children, and relationship counts.
30
+ Returns a compact summary for each element including metadata, description, technology, shape, children, relationship counts, links and source location.
7
31
 
8
32
  Request:
9
33
  - ids: string[] — array of element ids (FQNs) to read (max 50)
@@ -25,6 +49,8 @@ Response (JSON object):
25
49
  - incomingCount: number — number of incoming relationships
26
50
  - outgoingCount: number — number of outgoing relationships
27
51
  - includedInViews: View[] — views that include this element
52
+ - links: Array<{ title: string|null, url: string, relative: string|null }> — external links associated with this element
53
+ - sourceLocation: { path: string, range: { start: { line: number, character: number }, end: { line: number, character: number } } } | null — source location if available
28
54
  - notFound: string[] — ids that were not found in the project
29
55
 
30
56
  View (object) fields:
@@ -38,6 +64,8 @@ Notes:
38
64
  - Maximum 50 element ids per call.
39
65
  - Elements not found are listed in notFound array (not an error).
40
66
  - More efficient than multiple read-element calls when you need summary data for many elements.
67
+ - Returns the same "links" and "sourceLocation" as read-element, so it fully replaces read-element for that case.
68
+ - Call read-element only when you also need relationships, deployedInstances, defaultView or project.
41
69
 
42
70
  Example response:
43
71
  {
@@ -58,12 +86,19 @@ Example response:
58
86
  "outgoingCount": 3,
59
87
  "includedInViews": [
60
88
  { "id": "system-overview", "title": "System Overview", "type": "element" }
61
- ]
89
+ ],
90
+ "links": [
91
+ { "title": "Documentation", "url": "https://docs.example.com/frontend", "relative": null }
92
+ ],
93
+ "sourceLocation": {
94
+ "path": "/abs/path/project/model.c4",
95
+ "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 25, "character": 0 } }
96
+ }
62
97
  }
63
98
  ],
64
99
  "notFound": []
65
100
  }
66
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Batch read elements`},inputSchema:{ids:b.array(b.string()).min(1).max(50).describe(`Array of element ids (FQNs) to read (max 50)`),project:B},outputSchema:{elements:b.array(H),notFound:b.array(b.string()).describe(`Element ids that were not found`)}})(async(e,t)=>{l(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}}),W=b.object({id:b.string(),kind:b.string(),title:b.string(),description:b.string().nullable(),technology:b.string().nullable(),shape:b.string(),color:b.string()}),G=b.object({element1:W,element2:W,propertyDiffs:b.array(b.object({property:b.string().describe(`Property name`),element1Value:b.string().nullable().describe(`Value in element1`),element2Value:b.string().nullable().describe(`Value in element2`)})).describe(`Properties that differ between the two elements`),tags:b.object({onlyInElement1:b.array(b.string()).describe(`Tags present only in element1`),onlyInElement2:b.array(b.string()).describe(`Tags present only in element2`),common:b.array(b.string()).describe(`Tags present in both elements`)}),metadata:b.object({onlyInElement1:b.record(b.union([b.string(),b.array(b.string())])).describe(`Metadata keys only in element1`),onlyInElement2:b.record(b.union([b.string(),b.array(b.string())])).describe(`Metadata keys only in element2`),different:b.array(b.object({key:b.string(),element1Value:b.union([b.string(),b.array(b.string())]),element2Value:b.union([b.string(),b.array(b.string())])})).describe(`Metadata keys present in both but with different values`),common:b.record(b.union([b.string(),b.array(b.string())])).describe(`Metadata keys with identical values in both`)}),relationships:b.object({incomingOnlyElement1:b.number().describe(`Count of unique source elements sending to element1 only`),incomingOnlyElement2:b.number().describe(`Count of unique source elements sending to element2 only`),incomingShared:b.number().describe(`Count of unique source elements sending to both`),outgoingOnlyElement1:b.number().describe(`Count of unique target elements receiving from element1 only`),outgoingOnlyElement2:b.number().describe(`Count of unique target elements receiving from element2 only`),outgoingShared:b.number().describe(`Count of unique target elements receiving from both`)})}),K=likec4Tool({name:`element-diff`,description:`
101
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Batch read elements`},inputSchema:{ids:p.array(p.string()).min(1).max(50).describe(`Array of element ids (FQNs) to read (max 50)`),project:q},outputSchema:{elements:p.array(me),notFound:p.array(p.string()).describe(`Element ids that were not found`)}})(async(e,t)=>{v(t.ids.length<=50,`Maximum 50 element ids per call`);let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=mkLocate(e,n),a=[],s=[];for(let e of t.ids){let t=r.findElement(e);if(!t){s.push(e);continue}a.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,links:serializeLinks(t),sourceLocation:i({element:t.id})})}return{elements:a,notFound:s}}),Z=p.object({id:p.string(),kind:p.string(),title:p.string(),description:p.string().nullable(),technology:p.string().nullable(),shape:p.string(),color:p.string()}),ge=p.object({element1:Z,element2:Z,propertyDiffs:p.array(p.object({property:p.string().describe(`Property name`),element1Value:p.string().nullable().describe(`Value in element1`),element2Value:p.string().nullable().describe(`Value in element2`)})).describe(`Properties that differ between the two elements`),tags:p.object({onlyInElement1:p.array(p.string()).describe(`Tags present only in element1`),onlyInElement2:p.array(p.string()).describe(`Tags present only in element2`),common:p.array(p.string()).describe(`Tags present in both elements`)}),metadata:p.object({onlyInElement1:p.record(p.string(),p.union([p.string(),p.array(p.string())])).describe(`Metadata keys only in element1`),onlyInElement2:p.record(p.string(),p.union([p.string(),p.array(p.string())])).describe(`Metadata keys only in element2`),different:p.array(p.object({key:p.string(),element1Value:p.union([p.string(),p.array(p.string())]),element2Value:p.union([p.string(),p.array(p.string())])})).describe(`Metadata keys present in both but with different values`),common:p.record(p.string(),p.union([p.string(),p.array(p.string())])).describe(`Metadata keys with identical values in both`)}),relationships:p.object({incomingOnlyElement1:p.number().describe(`Count of unique source elements sending to element1 only`),incomingOnlyElement2:p.number().describe(`Count of unique source elements sending to element2 only`),incomingShared:p.number().describe(`Count of unique source elements sending to both`),outgoingOnlyElement1:p.number().describe(`Count of unique target elements receiving from element1 only`),outgoingOnlyElement2:p.number().describe(`Count of unique target elements receiving from element2 only`),outgoingShared:p.number().describe(`Count of unique target elements receiving from both`)})}),_e=likec4Tool({name:`element-diff`,description:`
67
102
  Compare two elements side-by-side, showing differences in properties, tags, metadata, and relationships.
68
103
 
69
104
  Request:
@@ -123,7 +158,7 @@ Example response:
123
158
  "outgoingShared": 2
124
159
  }
125
160
  }
126
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Compare two elements`},inputSchema:{element1Id:b.string().describe(`First element id (FQN)`),element2Id:b.string().describe(`Second element id (FQN)`),project:B},outputSchema:G.shape})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.element1Id);l(i,`Element "${t.element1Id}" not found in project "${n}"`);let a=r.findElement(t.element2Id);l(a,`Element "${t.element2Id}" not found in project "${n}"`);let o=[],s=[{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 s){let t=e.get(i),n=e.get(a);t!==n&&o.push({property:e.name,element1Value:t,element2Value:n})}let c=new Set(i.tags),u=new Set(a.tags),d=[],f=[],p=[];for(let e of c)u.has(e)?d.push(e):f.push(e);for(let e of u)c.has(e)||p.push(e);let m=i.getMetadata(),h=a.getMetadata(),g=new Set([...Object.keys(m),...Object.keys(h)]),_={},v={},y=[],b={};for(let e of g){let t=m[e],n=h[e];t!==void 0&&n===void 0?_[e]=t:t===void 0&&n!==void 0?v[e]=n:t!==void 0&&n!==void 0&&(JSON.stringify(t)===JSON.stringify(n)?b[e]=t:y.push({key:e,element1Value:t,element2Value:n}))}let x=new Set([...i.incoming()].map(e=>e.source.id)),S=new Set([...a.incoming()].map(e=>e.source.id)),C=new Set([...i.outgoing()].map(e=>e.target.id)),w=new Set([...a.outgoing()].map(e=>e.target.id)),T=0,E=0,D=0;for(let e of x)S.has(e)?T++:E++;for(let e of S)x.has(e)||D++;let O=0,k=0,A=0;for(let e of C)w.has(e)?O++:k++;for(let e of w)C.has(e)||A++;return{element1:{id:i.id,kind:i.kind,title:i.title,description:i.description.text,technology:i.technology,shape:i.shape,color:i.color},element2:{id:a.id,kind:a.kind,title:a.title,description:a.description.text,technology:a.technology,shape:a.shape,color:a.color},propertyDiffs:o,tags:{onlyInElement1:f,onlyInElement2:p,common:d},metadata:{onlyInElement1:_,onlyInElement2:v,different:y,common:b},relationships:{incomingOnlyElement1:E,incomingOnlyElement2:D,incomingShared:T,outgoingOnlyElement1:k,outgoingOnlyElement2:A,outgoingShared:O}}}),q=likec4Tool({name:`find-relationship-paths`,description:`
161
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Compare two elements`},inputSchema:{element1Id:p.string().describe(`First element id (FQN)`),element2Id:p.string().describe(`Second element id (FQN)`),project:q},outputSchema:ge.shape})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.element1Id);v(i,`Element "${t.element1Id}" not found in project "${n}"`);let a=r.findElement(t.element2Id);v(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),f=[],p=[],m=[];for(let e of l)u.has(e)?f.push(e):p.push(e);for(let e of u)l.has(e)||m.push(e);let h=i.getMetadata(),g=a.getMetadata(),_=new Set([...Object.keys(h),...Object.keys(g)]),y={},b={},x=[],S={};for(let e of _){let t=h[e],n=g[e];t!==void 0&&n===void 0?y[e]=t:t===void 0&&n!==void 0?b[e]=n:t!==void 0&&n!==void 0&&(JSON.stringify(t)===JSON.stringify(n)?S[e]=t:x.push({key:e,element1Value:t,element2Value:n}))}let C=new Set([...i.incoming()].map(e=>e.source.id)),w=new Set([...a.incoming()].map(e=>e.source.id)),T=new Set([...i.outgoing()].map(e=>e.target.id)),E=new Set([...a.outgoing()].map(e=>e.target.id)),D=0,O=0,k=0;for(let e of C)w.has(e)?D++:O++;for(let e of w)C.has(e)||k++;let A=0,j=0,M=0;for(let e of T)E.has(e)?A++:j++;for(let e of E)T.has(e)||M++;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:p,onlyInElement2:m,common:f},metadata:{onlyInElement1:y,onlyInElement2:b,different:x,common:S},relationships:{incomingOnlyElement1:O,incomingOnlyElement2:k,incomingShared:D,outgoingOnlyElement1:j,outgoingOnlyElement2:M,outgoingShared:A}}}),ve=likec4Tool({name:`find-relationship-paths`,description:`
127
162
  Discover all paths (chains of relationships) between two elements, supporting multi-hop traversal.
128
163
 
129
164
  Request:
@@ -212,7 +247,7 @@ Example response:
212
247
  }
213
248
  ]
214
249
  }
215
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationship paths`},inputSchema:{sourceId:b.string().describe(`Source element FQN`),targetId:b.string().describe(`Target element FQN`),maxDepth:b.number().int().min(1).max(5).optional().default(3).describe(`Maximum path length (default: 3, max: 5)`),includeIndirect:b.boolean().optional().default(!1).describe(`Include indirect (implied) relationships through nested elements (default: false)`),project:B},outputSchema:{paths:b.array(b.object({length:b.number().describe(`Number of hops in the path`),steps:b.array(b.object({source:b.string().describe(`Source element FQN`),target:b.string().describe(`Target element FQN`),relationship:b.object({kind:b.string().nullable().describe(`Relationship kind`),title:b.string().nullable().describe(`Relationship title`),description:b.string().nullable().describe(`Relationship description`),technology:b.string().nullable().describe(`Relationship technology`),tags:b.array(b.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);l(i,`Source element "${t.sourceId}" not found in project "${n}"`);let a=r.findElement(t.targetId);l(a,`Target element "${t.targetId}" not found in project "${n}"`),l(i.id!==a.id,`Source and target must be different elements`);let o=t.maxDepth,s=t.includeIndirect?`all`:`direct`,c=[{elementId:i.id,path:[],visited:new Set([i.id])}],u=[];for(;c.length>0&&u.length<100;){let e=c.shift();if(e.path.length>=o)continue;let t=r.findElement(e.elementId);if(t)for(let n of t.outgoing(s)){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]}};c.push({elementId:t,path:[...e.path,r],visited:new Set([...e.visited,t])})}}return u.sort((e,t)=>e.length-t.length),{paths:u}}),J=b.object({id:b.string(),title:b.string(),kind:b.string()}),Y=b.object({type:b.enum([`direct`,`indirect`]).describe(`Type of relationship, "direct" for direct relationships, "indirect" for relationships through nested elements`),source:J,target:J,kind:b.string().nullable().describe(`Relationship kind`),title:b.string().nullable().describe(`Relationship title`),description:b.string().nullable().describe(`Relationship description`),technology:b.string().nullable().describe(`Relationship technology`),tags:b.array(b.string()).describe(`Relationship tags`),includedInViews:V.describe(`Views that include this relationship`),sourceLocation:z}),X=likec4Tool({name:`find-relationships`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationships between two elements`},description:`
250
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationship paths`},inputSchema:{sourceId:p.string().describe(`Source element FQN`),targetId:p.string().describe(`Target element FQN`),maxDepth:p.number().int().min(1).max(5).optional().default(3).describe(`Maximum path length (default: 3, max: 5)`),includeIndirect:p.boolean().optional().default(!1).describe(`Include indirect (implied) relationships through nested elements (default: false)`),project:q},outputSchema:{paths:p.array(p.object({length:p.number().describe(`Number of hops in the path`),steps:p.array(p.object({source:p.string().describe(`Source element FQN`),target:p.string().describe(`Target element FQN`),relationship:p.object({kind:p.string().nullable().describe(`Relationship kind`),title:p.string().nullable().describe(`Relationship title`),description:p.string().nullable().describe(`Relationship description`),technology:p.string().nullable().describe(`Relationship technology`),tags:p.array(p.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);v(i,`Source element "${t.sourceId}" not found in project "${n}"`);let a=r.findElement(t.targetId);v(a,`Target element "${t.targetId}" not found in project "${n}"`),v(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}}),Q=p.object({id:p.string(),title:p.string(),kind:p.string()}),ye=p.object({type:p.enum([`direct`,`indirect`]).describe(`Type of relationship, "direct" for direct relationships, "indirect" for relationships through nested elements`),source:Q,target:Q,kind:p.string().nullable().describe(`Relationship kind`),title:p.string().nullable().describe(`Relationship title`),description:p.string().nullable().describe(`Relationship description`),technology:p.string().nullable().describe(`Relationship technology`),tags:p.array(p.string()).describe(`Relationship tags`),includedInViews:J.describe(`Views that include this relationship`),sourceLocation:G}),be=likec4Tool({name:`find-relationships`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Find relationships between two elements`},description:`
216
251
  Find relationships between two LikeC4 elements within a project.
217
252
 
218
253
  What it does:
@@ -283,7 +318,7 @@ Response:
283
318
  }
284
319
  ]
285
320
  }
286
- `,inputSchema:{element1:b.string().describe(`Element ID (FQN)`),element2:b.string().describe(`Element ID (FQN)`),project:B},outputSchema:{found:b.array(Y)}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project);if(D(t.element1,t.element2))throw Error(`No relationships possible between parent-child`);let r=[],i=await e.computedModel(n),a=i.findElement(t.element1);E(a,`Element "${t.element1}" not found in project "${n}"`);let o=i.findElement(t.element2);E(o,`Element "${t.element2}" not found in project "${n}"`);let s=mkLocate(e,n),c=w.findConnection(a,o,`both`).flatMap(e=>[...e.relations]);for(let e of c){let t=e.source===a&&e.target===o||e.source===o&&e.target===a;r.push({type:t?`direct`:`indirect`,source:{id:e.source.id,title:e.source.title,kind:e.source.kind},target:{id:e.target.id,title:e.target.title,kind:e.target.kind},kind:e.kind,title:e.title,description:e.description.text,technology:e.technology,tags:[...e.tags],includedInViews:includedInViews(e.views()),sourceLocation:s({relation:e.id})})}return{found:r}}),Z=likec4Tool({name:`list-projects`,description:`
321
+ `,inputSchema:{element1:p.string().describe(`Element ID (FQN)`),element2:p.string().describe(`Element ID (FQN)`),project:q},outputSchema:{found:p.array(ye)}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project);if(ie(t.element1,t.element2))throw Error(`No relationships possible between parent-child`);let r=[],i=await e.computedModel(n),a=i.findElement(t.element1);z(a,`Element "${t.element1}" not found in project "${n}"`);let s=i.findElement(t.element2);z(s,`Element "${t.element2}" not found in project "${n}"`);let c=mkLocate(e,n),l=re.findConnection(a,s,`both`).flatMap(e=>[...e.relations]);for(let e of l){let t=e.source===a&&e.target===s||e.source===s&&e.target===a;r.push({type:t?`direct`:`indirect`,source:{id:e.source.id,title:e.source.title,kind:e.source.kind},target:{id:e.target.id,title:e.target.title,kind:e.target.kind},kind:e.kind,title:e.title,description:e.description.text,technology:e.technology,tags:[...e.tags],includedInViews:includedInViews(e.views()),sourceLocation:c({relation:e.id})})}return{found:r}}),xe=likec4Tool({name:`list-projects`,description:`
287
322
  List LikeC4 projects discoverable in the current workspace.
288
323
 
289
324
  Request:
@@ -316,7 +351,21 @@ Example response:
316
351
  }
317
352
  ]
318
353
  }
319
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`List projects`},inputSchema:{},outputSchema:{projects:b.array(b.object({id:b.string(),title:b.string(),folder:b.string(),sources:b.array(b.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)}))})),Q=b.enum([`exact`,`contains`,`exists`]),te=likec4Tool({name:`query-by-metadata`,description:`
354
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`List projects`},inputSchema:{},outputSchema:{projects:p.array(p.object({id:p.string(),title:p.string(),folder:p.string(),sources:p.array(p.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)}))}));function _usingCtx(){var e=typeof SuppressedError==`function`?SuppressedError:function(e,t){var n=Error();return n.name=`SuppressedError`,n.error=e,n.suppressed=t,n},t={},n=[];function using(e,t){if(t!=null){if(Object(t)!==t)throw TypeError(`using declarations can only be used with objects, functions, null, or undefined.`);if(e)var r=t[Symbol.asyncDispose||Symbol.for(`Symbol.asyncDispose`)];if(r===void 0&&(r=t[Symbol.dispose||Symbol.for(`Symbol.dispose`)],e))var i=r;if(typeof r!=`function`)throw TypeError(`Object is not disposable.`);i&&(r=function o(){try{i.call(t)}catch(e){return Promise.reject(e)}}),n.push({v:t,d:r,a:e})}else e&&n.push({d:t,a:e});return t}return{e:t,u:using.bind(null,!1),a:using.bind(null,!0),d:function d(){var r,i=this.e,a=0;function next(){for(;r=n.pop();)try{if(!r.a&&a===1)return a=0,n.push(r),Promise.resolve().then(next);if(r.d){var e=r.d.call(r.v);if(r.a)return a|=2,Promise.resolve(e).then(next,err)}else a|=1}catch(e){return err(e)}if(a===1)return i===t?Promise.resolve():Promise.reject(i);if(i!==t)throw i}function err(n){return i=i===t?n:new e(n,i),next()}return next()}}}const Se=/^\s*view\s+([A-Za-z_][\w-]*)/;function extractViewId(e){let t=Se.exec(e);return t?t[1]:null}function wrapAsViewsBlock(e){return`views {\n${e}\n}\n`}function toPortablePath(e){return e.split(s).join(`/`)}function toVirtualSourcePath(e,t,n){let r=toPortablePath(i(e,t));return r===``||r.startsWith(`..`)?n:r}function formatDiagnostics(e){return e.map(e=>`- line ${e.range.start.line+1}, column ${e.range.start.character+1}: ${e.message}`).join(`
355
+ `)}function previewViewTool(e){return M(e,`preview-view`,{title:`Preview View`,description:`Renders a preview of a LikeC4 view defined by DSL text, in the context of an existing project's real elements — without saving anything to disk.
356
+
357
+ Request:
358
+ - dsl: string — a single \`view <id> ... { ... }\` definition. It must reference elements that already exist in the target project.
359
+ - project: string (optional) — project id. Defaults to "default" if omitted.
360
+ - fullModel: boolean (optional) — include the complete model instead of data scoped to this view. Defaults to false.
361
+
362
+ Behavior:
363
+ - The view id must be new (not already present in the project). If the view id matches an existing view, an error is returned — use a different id, or use "render-view" to render an existing view.
364
+ - The view is rendered as a draft alongside the project's real elements.
365
+
366
+ Note: preview styling may not exactly match the real project (custom theme/style extensions aren't applied to the preview). Only \`view <id> ...\` (element view) declarations are recognized for id-detection — a \`dynamic view <id> {...}\` or \`deployment view <id> {...}\` will fail with a generic "could not find a \`view <id>\`" error instead.
367
+
368
+ Use "preview-view" to iterate on a new view definition before creating it for real. Use "render-view" to render a view that's already saved.`,inputSchema:mcpToolSchema({dsl:p.string().describe("A single `view <id> ... { ... }` LikeC4 DSL definition"),project:q,fullModel:p.boolean().default(!1).describe(`Include the complete model instead of data scoped to this view`)}),outputSchema:mcpToolSchema({id:p.string(),title:p.string(),project:p.string(),view:p.record(p.string(),p.unknown()),model:p.record(p.string(),p.unknown())}),annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Preview view`},_meta:{ui:{resourceUri:Y}}},async e=>{try{var n=_usingCtx();let r=useLanguageServices(),i=r.projectsManager.ensureProjectId(e.project),a=extractViewId(e.dsl);if(!a)return toolError("Could not find a `view <id> ...` declaration at the start of `dsl`. Example: `view myView of someElement { include * }`");let s=r.project(i);if((await r.computedModel(i))?.findView(a))return toolError(`View "${a}" already exists in the project. Use a different view id for the preview, or use "render-view" to render the existing view.`);let c={};for(let[e,t]of s.documents.entries()){let n=toVirtualSourcePath(s.folder.fsPath,t.fsPath,`seed-${e}.c4`);c[n]=r.documentText(t.toString())??``}let l=`${oe()}.c4`;c[l]=wrapAsViewsBlock(e.dsl);let u=n.a(await t(c,{printErrors:!1,throwIfInvalid:!1})),f=u.getErrors();if(f.length>0)return toolError(`Failed to build preview:\n${formatDiagnostics(f)}`);let p=await u.computedModel(),m=p.findView(a);if(!m)return toolError(`View "${a}" was not found after building the preview.`);let h=u.projectsManager.ensureProjectId(ae(p.$data.projectId)),g=await u.viewsService.layoutView({viewId:m.id,projectId:h});if(!g)return toolError(`Failed to layout preview view "${a}".`);let _=m.title??m.id;return{content:[{type:`text`,text:`Rendered preview of view "${_}"`}],structuredContent:buildRenderPayload({projectId:i,viewId:m.id,title:_,layoutedView:g.diagram,model:p,fullModel:e.fullModel})}}catch(e){n.e=e}finally{await n.d()}}),e}const Ce=p.enum([`exact`,`contains`,`exists`]),we=likec4Tool({name:`query-by-metadata`,description:`
320
369
  Search elements and deployment nodes by metadata key-value pairs with flexible matching modes.
321
370
 
322
371
  Request:
@@ -382,7 +431,7 @@ Example response:
382
431
  }
383
432
  ]
384
433
  }
385
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by metadata`},inputSchema:{key:b.string().describe(`Metadata key to filter by`),value:b.string().optional().describe(`Metadata value to match (ignored for exists mode)`),matchMode:Q.optional().default(`exact`).describe(`Matching mode`),project:B},outputSchema:{results:b.array(R.extend({matchedValue:b.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&&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}}),ne=likec4Tool({name:`query-by-tag-pattern`,description:`
434
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by metadata`},inputSchema:{key:p.string().describe(`Metadata key to filter by`),value:p.string().optional().describe(`Metadata value to match (ignored for exists mode)`),matchMode:Ce.optional().default(`exact`).describe(`Matching mode`),project:q},outputSchema:{results:p.array(W.extend({matchedValue:p.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&&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}}),Te=likec4Tool({name:`query-by-tag-pattern`,description:`
386
435
  Search elements by tag patterns using prefix or substring matching.
387
436
  Useful for tag taxonomies with structured naming conventions (e.g., "schedule_*", "*_asil_*").
388
437
 
@@ -437,7 +486,7 @@ Example response:
437
486
  "truncated": false,
438
487
  "matchedTagValues": ["target_asil_qm", "target_asil_asil_b", "target_asil_qm__tbc"]
439
488
  }
440
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tag pattern`},inputSchema:{pattern:b.string().min(1).describe(`Tag pattern to match`),matchMode:b.enum([`prefix`,`contains`,`suffix`]).optional().default(`prefix`).describe(`Pattern matching mode (default: prefix)`),project:B},outputSchema:{results:b.array(R.extend({matchedTags:b.array(b.string()).describe(`Tags that matched the pattern`)})),truncated:b.boolean().describe(`True if results were truncated`),matchedTagValues:b.array(b.string()).describe(`All unique tag values matching the pattern across all elements`)}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=t.pattern.toLowerCase(),matchesTag=e=>{let n=e.toLowerCase();switch(t.matchMode){case`prefix`:return n.startsWith(i);case`contains`:return n.includes(i);case`suffix`:return n.endsWith(i)}},a=[],o=!1,s=new Set;for(let e of r.elements()){let t=[...e.tags].filter(matchesTag);if(t.length>0){if(t.forEach(e=>s.add(e)),a.length>=50){o=!0;continue}a.push({...serializeElement(e),matchedTags:t})}}for(let e of r.deployment.elements()){if(!C(e))continue;let t=[...e.tags].filter(matchesTag);if(t.length>0){if(t.forEach(e=>s.add(e)),a.length>=50){o=!0;continue}a.push({...serializeElement(e),matchedTags:t})}}return{results:a,truncated:o,matchedTagValues:[...s].sort((e,t)=>e.localeCompare(t))}}),re=likec4Tool({name:`query-by-tags`,description:`
489
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tag pattern`},inputSchema:{pattern:p.string().min(1).describe(`Tag pattern to match`),matchMode:p.enum([`prefix`,`contains`,`suffix`]).optional().default(`prefix`).describe(`Pattern matching mode (default: prefix)`),project:q},outputSchema:{results:p.array(W.extend({matchedTags:p.array(p.string()).describe(`Tags that matched the pattern`)})),truncated:p.boolean().describe(`True if results were truncated`),matchedTagValues:p.array(p.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=[],s=!1,c=new Set;for(let e of r.elements()){let t=[...e.tags].filter(matchesTag);if(t.length>0){if(t.forEach(e=>c.add(e)),a.length>=50){s=!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=>c.add(e)),a.length>=50){s=!0;continue}a.push({...serializeElement(e),matchedTags:t})}}return{results:a,truncated:s,matchedTagValues:[...c].sort((e,t)=>e.localeCompare(t))}}),Ee=likec4Tool({name:`query-by-tags`,description:`
441
490
  Advanced tag filtering with boolean logic (AND, OR, NOT).
442
491
 
443
492
  Request:
@@ -501,7 +550,7 @@ Example response:
501
550
  }
502
551
  ]
503
552
  }
504
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tags`},inputSchema:{allOf:b.array(b.string()).optional().describe(`Element must have ALL these tags (AND)`),anyOf:b.array(b.string()).optional().describe(`Element must have ANY of these tags (OR)`),noneOf:b.array(b.string()).optional().describe(`Element must have NONE of these tags (NOT)`),project:B},outputSchema:{results:b.array(R),truncated:b.boolean().describe(`True if results were truncated due to exceeding the 50-result limit`)}})(async(e,t)=>{l(t.allOf&&t.allOf.length>0||t.anyOf&&t.anyOf.length>0||t.noneOf&&t.noneOf.length>0,`At least one condition (allOf, anyOf, or noneOf) must be specified with at least one tag`);let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=[],a=!1,matchesTags=e=>{let n=e instanceof Set?e:new Set(e);return!(t.allOf&&t.allOf.length>0&&!t.allOf.every(e=>n.has(e))||t.anyOf&&t.anyOf.length>0&&!t.anyOf.some(e=>n.has(e))||t.noneOf&&t.noneOf.length>0&&t.noneOf.some(e=>n.has(e)))};for(let e of r.elements()){if(i.length>=50){a=!0;break}matchesTags(e.tags)&&i.push(serializeElement(e))}if(!a)for(let e of r.deployment.elements()){if(i.length>=50){a=!0;break}C(e)&&matchesTags(e.tags)&&i.push(serializeElement(e))}return{results:i,truncated:a}}),ie=b.enum([`ancestors`,`descendants`,`siblings`,`children`,`parent`,`incomers`,`outgoers`]),ae=likec4Tool({name:`query-graph`,description:`
553
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query by tags`},inputSchema:{allOf:p.array(p.string()).optional().describe(`Element must have ALL these tags (AND)`),anyOf:p.array(p.string()).optional().describe(`Element must have ANY of these tags (OR)`),noneOf:p.array(p.string()).optional().describe(`Element must have NONE of these tags (NOT)`),project:q},outputSchema:{results:p.array(W),truncated:p.boolean().describe(`True if results were truncated due to exceeding the 50-result limit`)}})(async(e,t)=>{v(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}}),De=p.enum([`ancestors`,`descendants`,`siblings`,`children`,`parent`,`incomers`,`outgoers`]),Oe=likec4Tool({name:`query-graph`,description:`
505
554
  Query element hierarchy and relationships in the architecture graph.
506
555
 
507
556
  Request:
@@ -573,7 +622,7 @@ Example response:
573
622
  ],
574
623
  "truncated": false
575
624
  }
576
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query element graph`},inputSchema:{elementId:b.string().describe(`Element id (FQN) to query`),queryType:ie.describe(`Type of graph query`),includeIndirect:b.boolean().optional().default(!0).describe(`For incomers/outgoers: include indirect relationships (default: true)`),project:B},outputSchema:{results:b.array(R),truncated:b.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);l(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}}),oe=b.object({elementId:b.string().describe(`ID of the incoming element`),relationshipLabel:b.string().optional().describe(`Label on the relationship`),technology:b.string().optional().describe(`Technology specified on the relationship`)}),se=likec4Tool({name:`query-incomers-graph`,description:`
625
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query element graph`},inputSchema:{elementId:p.string().describe(`Element id (FQN) to query`),queryType:De.describe(`Type of graph query`),includeIndirect:p.boolean().optional().default(!0).describe(`For incomers/outgoers: include indirect relationships (default: true)`),project:q},outputSchema:{results:p.array(W),truncated:p.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);v(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}}),ke=p.object({elementId:p.string().describe(`ID of the incoming element`),relationshipLabel:p.string().optional().describe(`Label on the relationship`),technology:p.string().optional().describe(`Technology specified on the relationship`)}),$=likec4Tool({name:`query-incomers-graph`,description:`
577
626
  Query the complete graph of all elements that provide input to the target element (recursive incomers/producers).
578
627
 
579
628
  This tool performs a breadth-first traversal to discover all upstream dependencies - elements that directly or
@@ -630,7 +679,7 @@ Notes:
630
679
  Example:
631
680
  For a database element, this returns all services, APIs, and components that write to it,
632
681
  plus all their dependencies, recursively up to maxDepth levels.
633
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete incomers graph`},inputSchema:{elementId:b.string().describe(`Target element id (FQN) to query incomers for`),includeIndirect:b.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:b.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:b.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:B},outputSchema:{target:b.string().describe(`Target element id`),totalNodes:b.number().describe(`Total number of nodes in the graph`),maxDepth:b.number().describe(`Maximum depth reached`),truncated:b.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:b.record(R.extend({incomers:b.array(oe).describe(`Incoming relationships with details`),depth:b.number().describe(`Distance from target element (0 = target)`)}))}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n);l(r.findElement(t.elementId),`Element "${t.elementId}" not found in project "${n}"`);let i=t.includeIndirect?`all`:`direct`,a=traverseGraph(r,t.elementId,`incoming`,i,t.maxDepth,t.maxNodes),o={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;o[e]={...r,incomers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:o}}),ce=b.object({elementId:b.string().describe(`ID of the outgoing element`),relationshipLabel:b.string().optional().describe(`Label on the relationship`),technology:b.string().optional().describe(`Technology specified on the relationship`)}),le=likec4Tool({name:`query-outgoers-graph`,description:`
682
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete incomers graph`},inputSchema:{elementId:p.string().describe(`Target element id (FQN) to query incomers for`),includeIndirect:p.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:p.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:p.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:q},outputSchema:{target:p.string().describe(`Target element id`),totalNodes:p.number().describe(`Total number of nodes in the graph`),maxDepth:p.number().describe(`Maximum depth reached`),truncated:p.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:p.record(p.string(),W.extend({incomers:p.array(ke).describe(`Incoming relationships with details`),depth:p.number().describe(`Distance from target element (0 = target)`)}))}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.elementId);v(i,`Element "${t.elementId}" not found in project "${n}"`);let a=t.includeIndirect?`all`:`direct`,s=traverseGraph(r,t.elementId,`incoming`,a,t.maxDepth,t.maxNodes),c={};for(let[e,t]of Object.entries(s.nodes)){let{neighbors:n,...r}=t;c[e]={...r,incomers:n}}return{target:s.target,totalNodes:s.totalNodes,maxDepth:s.maxDepth,truncated:s.truncated,nodes:c}}),Ae=p.object({elementId:p.string().describe(`ID of the outgoing element`),relationshipLabel:p.string().optional().describe(`Label on the relationship`),technology:p.string().optional().describe(`Technology specified on the relationship`)}),je=likec4Tool({name:`query-outgoers-graph`,description:`
634
683
  Query the complete graph of all elements that receive output from the target element (recursive outgoers/consumers).
635
684
 
636
685
  This tool performs a breadth-first traversal to discover all downstream dependencies - elements that directly or
@@ -687,7 +736,7 @@ Notes:
687
736
  Example:
688
737
  For an API service, this returns all clients, services, and systems that consume its output,
689
738
  plus all their consumers, recursively up to maxDepth levels.
690
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete outgoers graph`},inputSchema:{elementId:b.string().describe(`Target element id (FQN) to query outgoers for`),includeIndirect:b.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:b.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:b.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:B},outputSchema:{target:b.string().describe(`Target element id`),totalNodes:b.number().describe(`Total number of nodes in the graph`),maxDepth:b.number().describe(`Maximum depth reached`),truncated:b.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:b.record(R.extend({outgoers:b.array(ce).describe(`Outgoing relationships with details`),depth:b.number().describe(`Distance from target element (0 = target)`)}))}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n);l(r.findElement(t.elementId),`Element "${t.elementId}" not found in project "${n}"`);let i=t.includeIndirect?`all`:`direct`,a=traverseGraph(r,t.elementId,`outgoing`,i,t.maxDepth,t.maxNodes),o={};for(let[e,t]of Object.entries(a.nodes)){let{neighbors:n,...r}=t;o[e]={...r,outgoers:n}}return{target:a.target,totalNodes:a.totalNodes,maxDepth:a.maxDepth,truncated:a.truncated,nodes:o}}),ue=likec4Tool({name:`read-deployment`,description:`
739
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Query complete outgoers graph`},inputSchema:{elementId:p.string().describe(`Target element id (FQN) to query outgoers for`),includeIndirect:p.boolean().optional().default(!0).describe(`Include indirect relationships through nested elements (default: true)`),maxDepth:p.number().int().positive().max(50).optional().default(10).describe(`Maximum traversal depth (default: 10, max: 50)`),maxNodes:p.number().int().positive().max(2e3).optional().default(200).describe(`Maximum number of nodes to return (default: 200, max: 2000)`),project:q},outputSchema:{target:p.string().describe(`Target element id`),totalNodes:p.number().describe(`Total number of nodes in the graph`),maxDepth:p.number().describe(`Maximum depth reached`),truncated:p.boolean().describe(`True if result was truncated due to maxNodes limit`),nodes:p.record(p.string(),W.extend({outgoers:p.array(Ae).describe(`Outgoing relationships with details`),depth:p.number().describe(`Distance from target element (0 = target)`)}))}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=await e.computedModel(n),i=r.findElement(t.elementId);v(i,`Element "${t.elementId}" not found in project "${n}"`);let a=t.includeIndirect?`all`:`direct`,s=traverseGraph(r,t.elementId,`outgoing`,a,t.maxDepth,t.maxNodes),c={};for(let[e,t]of Object.entries(s.nodes)){let{neighbors:n,...r}=t;c[e]={...r,outgoers:n}}return{target:s.target,totalNodes:s.totalNodes,maxDepth:s.maxDepth,truncated:s.truncated,nodes:c}}),Me=likec4Tool({name:`read-deployment`,description:`
691
740
  Read details about a deployment node or a deployed instance in a LikeC4 project.
692
741
 
693
742
  What it does:
@@ -752,7 +801,7 @@ Example response (deployed instance):
752
801
  "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 25, "character": 0 } }
753
802
  }
754
803
  }
755
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read deployment entity`},inputSchema:{id:b.string().describe(`Deployment id (FQN)`),project:B},outputSchema:{type:b.enum([`deployment-node`,`deployed-instance`]),id:b.string().describe(`Deployment id (FQN)`),kind:b.string().describe(`Deployment node kind, or element kind for deployed instances`),name:b.string(),title:b.string(),description:b.string().nullable(),technology:b.string().nullable(),tags:b.array(b.string()),project:b.string(),metadata:b.record(b.union([b.string(),b.array(b.string())])),links:b.array(b.object({title:b.string().nullable().describe(`Optional link title`),url:b.string().describe(`Link URL`),relative:b.string().nullable().describe(`Relative path (if URL is relative to workspace root)`)})).describe(`External links associated with this deployment entity`),shape:b.string(),color:b.string(),children:b.array(b.string()).describe(`Children of this deployment node (Array of Deployment ids)`),includedInViews:V.describe(`Views that include this deployment node`),instanceof:b.object({id:b.string().describe(`Element ID (FQN)`),title:b.string(),kind:b.string()}).nullable().describe(`If type is "deployed-instance", the referenced element`),sourceLocation:z}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).deployment.findElement(t.id);l(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})}}),de=likec4Tool({name:`read-element`,description:`
804
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read deployment entity`},inputSchema:{id:p.string().describe(`Deployment id (FQN)`),project:q},outputSchema:{type:p.enum([`deployment-node`,`deployed-instance`]),id:p.string().describe(`Deployment id (FQN)`),kind:p.string().describe(`Deployment node kind, or element kind for deployed instances`),name:p.string(),title:p.string(),description:p.string().nullable(),technology:p.string().nullable(),tags:p.array(p.string()),project:p.string(),metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])),links:K.describe(`External links associated with this deployment entity`),shape:p.string(),color:p.string(),children:p.array(p.string()).describe(`Children of this deployment node (Array of Deployment ids)`),includedInViews:J.describe(`Views that include this deployment node`),instanceof:p.object({id:p.string().describe(`Element ID (FQN)`),title:p.string(),kind:p.string()}).nullable().describe(`If type is "deployed-instance", the referenced element`),sourceLocation:G}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).deployment.findElement(t.id);v(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:serializeLinks(r),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})}}),Ne=likec4Tool({name:`read-element`,description:`
756
805
  Read detailed information about a LikeC4 element.
757
806
 
758
807
  Request:
@@ -839,7 +888,7 @@ Example response:
839
888
  "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 25, "character": 0 } }
840
889
  }
841
890
  }
842
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read element`},inputSchema:{id:b.string().describe(`Element id (FQN)`),project:B},outputSchema:{id:b.string().describe(`Element id (FQN)`),kind:b.string().describe(`Element kind`),name:b.string().describe(`Element name`),title:b.string(),description:b.string().nullable(),technology:b.string().nullable(),tags:b.array(b.string()),project:b.string(),metadata:b.record(b.union([b.string(),b.array(b.string())])),links:b.array(b.object({title:b.string().nullable().describe(`Optional link title`),url:b.string().describe(`Link URL`),relative:b.string().nullable().describe(`Relative path (if URL is relative to workspace root)`)})).describe(`External links associated with this element`),shape:b.string(),color:b.string(),children:b.array(b.string()).describe(`Children of this element (Array of FQNs)`),defaultView:b.string().nullable().describe(`Name of the default view of this element`),includedInViews:V.describe(`Views that include this element`),relationships:b.object({incoming:b.array(b.object({source:b.object({id:b.string(),title:b.string(),kind:b.string()}).describe(`Source element of this relationship`),kind:b.string().nullable().describe(`Relationship kind`),target:b.string().describe(`Target element id (FQN), either this element or nested element, if relationship is indirect`),title:b.string().nullable().describe(`Relationship title`),description:b.string().nullable().describe(`Relationship description`),technology:b.string().nullable().describe(`Relationship technology`),tags:b.array(b.string()).describe(`Relationship tags`)})).describe(`Incoming relationships of this element (direct and indirect, incoming to nested elements)`),outgoing:b.array(b.object({source:b.string().describe(`Source element id (FQN), either this element or nested element, if relationship is indirect`),target:b.object({id:b.string(),title:b.string(),kind:b.string()}).describe(`Target element of this relationship`),kind:b.string().nullable().describe(`Relationship kind`),title:b.string().nullable().describe(`Relationship title`),description:b.string().nullable().describe(`Relationship description`),technology:b.string().nullable().describe(`Relationship technology`),tags:b.array(b.string()).describe(`Relationship tags`)})).describe(`Outgoing relationships of this element (direct and indirect, outgoing from nested elements)`)}).describe(`Relationships of this element`),deployedInstances:b.array(b.string()).describe(`Deployed instances of this element (Array of Deployment FQNs)`),sourceLocation:z}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findElement(t.id);l(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})}}),$=likec4Tool({name:`read-project-summary`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read project summary`},description:`
891
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read element`},inputSchema:{id:p.string().describe(`Element id (FQN)`),project:q},outputSchema:{id:p.string().describe(`Element id (FQN)`),kind:p.string().describe(`Element kind`),name:p.string().describe(`Element name`),title:p.string(),description:p.string().nullable(),technology:p.string().nullable(),tags:p.array(p.string()),project:p.string(),metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])),links:K,shape:p.string(),color:p.string(),children:p.array(p.string()).describe(`Children of this element (Array of FQNs)`),defaultView:p.string().nullable().describe(`Name of the default view of this element`),includedInViews:J.describe(`Views that include this element`),relationships:p.object({incoming:p.array(p.object({source:p.object({id:p.string(),title:p.string(),kind:p.string()}).describe(`Source element of this relationship`),kind:p.string().nullable().describe(`Relationship kind`),target:p.string().describe(`Target element id (FQN), either this element or nested element, if relationship is indirect`),title:p.string().nullable().describe(`Relationship title`),description:p.string().nullable().describe(`Relationship description`),technology:p.string().nullable().describe(`Relationship technology`),tags:p.array(p.string()).describe(`Relationship tags`)})).describe(`Incoming relationships of this element (direct and indirect, incoming to nested elements)`),outgoing:p.array(p.object({source:p.string().describe(`Source element id (FQN), either this element or nested element, if relationship is indirect`),target:p.object({id:p.string(),title:p.string(),kind:p.string()}).describe(`Target element of this relationship`),kind:p.string().nullable().describe(`Relationship kind`),title:p.string().nullable().describe(`Relationship title`),description:p.string().nullable().describe(`Relationship description`),technology:p.string().nullable().describe(`Relationship technology`),tags:p.array(p.string()).describe(`Relationship tags`)})).describe(`Outgoing relationships of this element (direct and indirect, outgoing from nested elements)`)}).describe(`Relationships of this element`),deployedInstances:p.array(p.string()).describe(`Deployed instances of this element (Array of Deployment FQNs)`),sourceLocation:G}})(async(e,t)=>{let n=e.projectsManager.ensureProjectId(t.project),r=(await e.computedModel(n)).findElement(t.id);v(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:serializeLinks(r),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})}}),Pe=likec4Tool({name:`read-project-summary`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read project summary`},description:`
843
892
  Request:
844
893
  - project: string (optional) — project id. Defaults to "default" if omitted.
845
894
 
@@ -930,7 +979,7 @@ Example response:
930
979
  }
931
980
  ]
932
981
  }
933
- `,inputSchema:{project:B},outputSchema:{title:b.string(),folder:b.string(),sources:b.array(b.string()),config:L.describe(`Project configuration`),specification:b.object({elementKinds:b.array(b.string()),relationshipKinds:b.array(b.string()),deploymentKinds:b.array(b.string()),tags:b.array(b.string()),metadataKeys:b.array(b.string())}),elements:b.array(b.object({id:b.string(),kind:b.string(),title:b.string(),tags:b.array(b.string())})).describe(`List of elements in the project`),deployments:b.array(b.discriminatedUnion(`type`,[b.object({type:b.literal(`deployment-node`),id:b.string().describe(`Node ID`),kind:b.string().describe(`Deployment node kind`),title:b.string().describe(`Node title`),tags:b.array(b.string())}),b.object({type:b.literal(`deployed-instance`),id:b.string().describe(`Node ID`),title:b.string().describe(`Node title`),tags:b.array(b.string()),referencedElementId:b.string().describe(`Element ID (FQN)`)})])).describe(`List of deployment nodes and deployed instances in the project`),views:b.array(b.object({id:b.string(),title:b.string(),type:b.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:p(i.specification.elements),relationshipKinds:p(i.specification.relationships),deploymentKinds:p(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,fe=b.discriminatedUnion(`type`,[b.object({type:b.literal(`element`),id:b.string().describe(`Node ID`),elementId:b.string().describe(`Element ID (FQN)`),kind:b.string().describe(`Element kind`),title:b.string().describe(`Node title`),description:b.string().nullable(),technology:b.string().nullable(),children:b.array(b.string()).describe(`Children nodes, array of node IDs`),shape:b.string().describe(`Rendered shape`),color:b.string().describe(`Rendered color`),tags:b.array(b.string())}),b.object({type:b.literal(`deployment-node`),id:b.string().describe(`Node ID`),deploymentId:b.string().describe(`Deployment entity ID (FQN)`),kind:b.string().describe(`Deployment kind`),title:b.string().describe(`Node title`),description:b.string().nullable(),technology:b.string().nullable(),children:b.array(b.string()).describe(`Children nodes, array of node IDs`),shape:b.string().describe(`Rendered shape`),color:b.string().describe(`Rendered color`),tags:b.array(b.string())}),b.object({type:b.literal(`deployed-instance`),id:b.string().describe(`Node ID`),deploymentId:b.string().describe(`Deployment entity ID (FQN)`),title:b.string().describe(`Node title`),description:b.string().nullable(),technology:b.string().nullable(),referencedElement:b.object({id:b.string().describe(`Element ID (FQN)`),kind:b.string().describe(`Element kind`),title:b.string().describe(`Element title`)}),shape:b.string().describe(`Rendered shape`),color:b.string().describe(`Rendered color`),tags:b.array(b.string())})]),pe=likec4Tool({name:`read-view`,description:`
982
+ `,inputSchema:{project:q},outputSchema:{title:p.string(),folder:p.string(),sources:p.array(p.string()),config:pe.describe(`Project configuration`),specification:p.object({elementKinds:p.array(p.string()),relationshipKinds:p.array(p.string()),deploymentKinds:p.array(p.string()),tags:p.array(p.string()),metadataKeys:p.array(p.string())}),elements:p.array(p.object({id:p.string(),kind:p.string(),title:p.string(),tags:p.array(p.string())})).describe(`List of elements in the project`),deployments:p.array(p.discriminatedUnion(`type`,[p.object({type:p.literal(`deployment-node`),id:p.string().describe(`Node ID`),kind:p.string().describe(`Deployment node kind`),title:p.string().describe(`Node title`),tags:p.array(p.string())}),p.object({type:p.literal(`deployed-instance`),id:p.string().describe(`Node ID`),title:p.string().describe(`Node title`),tags:p.array(p.string()),referencedElementId:p.string().describe(`Element ID (FQN)`)})])).describe(`List of deployment nodes and deployed instances in the project`),views:p.array(p.object({id:p.string(),title:p.string(),type:p.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,Fe=p.discriminatedUnion(`type`,[p.object({type:p.literal(`element`),id:p.string().describe(`Node ID`),elementId:p.string().describe(`Element ID (FQN)`),kind:p.string().describe(`Element kind`),title:p.string().describe(`Node title`),description:p.string().nullable(),technology:p.string().nullable(),children:p.array(p.string()).describe(`Children nodes, array of node IDs`),shape:p.string().describe(`Rendered shape`),color:p.string().describe(`Rendered color`),tags:p.array(p.string())}),p.object({type:p.literal(`deployment-node`),id:p.string().describe(`Node ID`),deploymentId:p.string().describe(`Deployment entity ID (FQN)`),kind:p.string().describe(`Deployment kind`),title:p.string().describe(`Node title`),description:p.string().nullable(),technology:p.string().nullable(),children:p.array(p.string()).describe(`Children nodes, array of node IDs`),shape:p.string().describe(`Rendered shape`),color:p.string().describe(`Rendered color`),tags:p.array(p.string())}),p.object({type:p.literal(`deployed-instance`),id:p.string().describe(`Node ID`),deploymentId:p.string().describe(`Deployment entity ID (FQN)`),title:p.string().describe(`Node title`),description:p.string().nullable(),technology:p.string().nullable(),referencedElement:p.object({id:p.string().describe(`Element ID (FQN)`),kind:p.string().describe(`Element kind`),title:p.string().describe(`Element title`)}),shape:p.string().describe(`Rendered shape`),color:p.string().describe(`Rendered color`),tags:p.array(p.string())})]),Ie=likec4Tool({name:`read-view`,description:`
934
983
  Read detailed information about a LikeC4 view.
935
984
 
936
985
  Request:
@@ -978,7 +1027,7 @@ Example response:
978
1027
  "range": { "start": { "line": 10, "character": 0 }, "end": { "line": 30, "character": 0 } }
979
1028
  }
980
1029
  }
981
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read view`},inputSchema:{viewId:b.string().describe(`View id (name)`),project:B},outputSchema:{id:b.string(),type:b.enum([`element`,`deployment`,`dynamic`]).describe(`View type`),title:b.string(),description:b.string().nullable(),tags:b.array(b.string()),project:b.string(),nodes:b.array(fe),edges:b.array(b.object({source:b.string().describe(`Source node`),target:b.string().describe(`Target node`),label:b.string().nullable(),description:b.string().nullable(),technology:b.string().nullable(),tags:b.array(b.string())})).describe(`Edge represents relationship between nodes`),sourceLocation:z}})(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})}}),me=b.array(b.discriminatedUnion(`type`,[b.object({type:b.literal(`element`),project:b.string().describe(`Project ID`),id:b.string().describe(`Element ID (FQN)`),name:b.string().describe(`Element name`),kind:b.string(),title:b.string(),technology:b.string().nullable(),shape:b.string(),includedInViews:V,metadata:b.record(b.union([b.string(),b.array(b.string())])),tags:b.array(b.string())}),b.object({type:b.literal(`deployment-node`),project:b.string().describe(`Project ID`),id:b.string().describe(`Deployment ID (FQN)`),name:b.string().describe(`Deployment name`),kind:b.string(),title:b.string(),technology:b.string().nullable(),shape:b.string(),includedInViews:V,metadata:b.record(b.union([b.string(),b.array(b.string())])),tags:b.array(b.string())})])),he=likec4Tool({name:`search-element`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Search elements`},description:`
1030
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Read view`},inputSchema:{viewId:p.string().describe(`View id (name)`),project:q},outputSchema:{id:p.string(),type:p.enum([`element`,`deployment`,`dynamic`]).describe(`View type`),title:p.string(),description:p.string().nullable(),tags:p.array(p.string()),project:p.string(),nodes:p.array(Fe),edges:p.array(p.object({source:p.string().describe(`Source node`),target:p.string().describe(`Target node`),label:p.string().nullable(),description:p.string().nullable(),technology:p.string().nullable(),tags:p.array(p.string())})).describe(`Edge represents relationship between nodes`),sourceLocation:G}})(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})}}),Le=p.array(p.discriminatedUnion(`type`,[p.object({type:p.literal(`element`),project:p.string().describe(`Project ID`),id:p.string().describe(`Element ID (FQN)`),name:p.string().describe(`Element name`),kind:p.string(),title:p.string(),technology:p.string().nullable(),shape:p.string(),includedInViews:J,metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])),tags:p.array(p.string())}),p.object({type:p.literal(`deployment-node`),project:p.string().describe(`Project ID`),id:p.string().describe(`Deployment ID (FQN)`),name:p.string().describe(`Deployment name`),kind:p.string(),title:p.string(),technology:p.string().nullable(),shape:p.string(),includedInViews:J,metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])),tags:p.array(p.string())})])),Re=likec4Tool({name:`search-element`,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Search elements`},description:`
982
1031
  Search LikeC4 elements and deployment nodes across all projects.
983
1032
 
984
1033
  Query syntax (case-insensitive):
@@ -1033,7 +1082,7 @@ Example response:
1033
1082
  }
1034
1083
  ]
1035
1084
  }
1036
- `,inputSchema:{search:b.string().min(2,`Search must be at least 2 characters long`)},outputSchema:{total:b.number(),found:me}})(async(e,t)=>{let n=e.projects(),r=[],i=t.search.toLowerCase(),predicate;i.startsWith(`kind:`)?(i=i.slice(5),F.debug(`search by kind: {search}`,{search:i}),predicate=e=>e.kind.toLowerCase()===i):i.startsWith(`shape:`)?(i=i.slice(6),F.debug(`search by shape: {search}`,{search:i}),predicate=e=>e.shape.toLowerCase()===i):i.startsWith(`meta:`)?(i=i.slice(5),F.debug(`search by metadata: {search}`,{search:i}),predicate=e=>!!e.getMetadata(i)):i.startsWith(`#`)?(i=i.slice(1),F.debug(`search by tag: {search}`,{search:i}),predicate=e=>e.tags.some(e=>e.toLowerCase().includes(i))):(F.debug(`search by id/title: {search}`,{search:i}),predicate=e=>e.id.toLowerCase().includes(i)||e.title.toLowerCase().includes(i));for(let t of n)try{let n=await e.computedModel(t.id);for(let e of T(n.elements(),e=>!e.imported&&predicate(e)))r.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 T(n.deployment.nodes(),predicate))r.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){F.error(`Error searching in project ${t.id}:`,{error:e})}return{total:r.length,found:r.slice(0,20)}}),ge=b.object({id:b.string().describe(`Element id (FQN)`),name:b.string().describe(`Element name`),kind:b.string().describe(`Element kind`),title:b.string().describe(`Human-readable title`),depth:b.number().describe(`Depth relative to the root element (1 = direct child)`),tags:b.array(b.string()).describe(`Assigned tags`),metadata:b.record(b.union([b.string(),b.array(b.string())])).describe(`Element metadata`),childCount:b.number().describe(`Number of direct children`),incomingCount:b.number().describe(`Number of incoming relationships`),outgoingCount:b.number().describe(`Number of outgoing relationships`)}),_e=likec4Tool({name:`subgraph-summary`,description:`
1085
+ `,inputSchema:{search:p.string().min(2,`Search must be at least 2 characters long`)},outputSchema:{total:p.number(),found:Le}})(async(e,t)=>{let n=e.projects(),r=[],i=t.search.toLowerCase(),predicate;i.startsWith(`kind:`)?(i=i.slice(5),U.debug(`search by kind: {search}`,{search:i}),predicate=e=>e.kind.toLowerCase()===i):i.startsWith(`shape:`)?(i=i.slice(6),U.debug(`search by shape: {search}`,{search:i}),predicate=e=>e.shape.toLowerCase()===i):i.startsWith(`meta:`)?(i=i.slice(5),U.debug(`search by metadata: {search}`,{search:i}),predicate=e=>!!e.getMetadata(i)):i.startsWith(`#`)?(i=i.slice(1),U.debug(`search by tag: {search}`,{search:i}),predicate=e=>e.tags.some(e=>e.toLowerCase().includes(i))):(U.debug(`search by id/title: {search}`,{search:i}),predicate=e=>e.id.toLowerCase().includes(i)||e.title.toLowerCase().includes(i));for(let t of n)try{let n=await e.computedModel(t.id);for(let e of R(n.elements(),e=>!e.imported&&predicate(e)))r.push({type:`element`,project:t.id,id:e.id,name:e.name,kind:e.kind,title:e.title,technology:e.technology,shape:e.shape,tags:[...e.tags],metadata:e.getMetadata(),includedInViews:includedInViews(e.views())});for(let e of R(n.deployment.nodes(),predicate))r.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){U.error(`Error searching in project ${t.id}:`,{error:e})}return{total:r.length,found:r.slice(0,20)}}),ze=p.object({id:p.string().describe(`Element id (FQN)`),name:p.string().describe(`Element name`),kind:p.string().describe(`Element kind`),title:p.string().describe(`Human-readable title`),depth:p.number().describe(`Depth relative to the root element (1 = direct child)`),tags:p.array(p.string()).describe(`Assigned tags`),metadata:p.record(p.string(),p.union([p.string(),p.array(p.string())])).describe(`Element metadata`),childCount:p.number().describe(`Number of direct children`),incomingCount:p.number().describe(`Number of incoming relationships`),outgoingCount:p.number().describe(`Number of outgoing relationships`)}),Be=likec4Tool({name:`subgraph-summary`,description:`
1037
1086
  Get a compact, table-friendly summary of all descendants of a parent element.
1038
1087
  Returns each descendant with its depth, metadata, tags, and relationship counts in a single call.
1039
1088
  Much more efficient than calling read-element for each descendant individually.
@@ -1099,7 +1148,7 @@ Example response:
1099
1148
  "truncated": false,
1100
1149
  "truncatedByDepth": false
1101
1150
  }
1102
- `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Subgraph summary`},inputSchema:{elementId:b.string().describe(`Parent element id (FQN) whose descendants to summarize`),maxDepth:b.number().int().min(1).max(20).optional().default(10).describe(`Maximum depth of descendants to include (default: 10, max: 20)`),metadataKeys:b.array(b.string()).optional().describe(`If provided, only include these metadata keys in the response`),project:B},outputSchema:{root:b.object({id:b.string(),kind:b.string(),title:b.string(),childCount:b.number()}),descendants:b.array(ge),totalDescendants:b.number().describe(`Total number of descendants found`),truncated:b.boolean().describe(`True if results were truncated due to exceeding the limit`),truncatedByDepth:b.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);l(r,`Element "${t.elementId}" not found in project "${n}"`);let i=t.maxDepth,a=t.metadataKeys,o=[],s=0,c=!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}s++;let n=[...e.children()];if(o.length<200){let r=e.getMetadata(),i=a?Object.fromEntries(a.filter(e=>e in r).map(e=>[e,r[e]])):r;o.push({id:e.id,name:e.name,kind:e.kind,title:e.title,depth:t,tags:[...e.tags],metadata:i,childCount:n.length,incomingCount:[...e.incoming()].length,outgoingCount:[...e.outgoing()].length})}else c=!0;for(let e of n)d.push({element:e,depth:t+1})}return{root:{id:r.id,kind:r.kind,title:r.title,childCount:f.length},descendants:o,totalDescendants:s,truncated:c,truncatedByDepth:u}});function createMCPServer(e){let t=new _({name:`LikeC4`,version:I},{instructions:`LikeC4 MCP – query and navigate LikeC4 models.
1151
+ `,annotations:{readOnlyHint:!0,idempotentHint:!0,title:`Subgraph summary`},inputSchema:{elementId:p.string().describe(`Parent element id (FQN) whose descendants to summarize`),maxDepth:p.number().int().min(1).max(20).optional().default(10).describe(`Maximum depth of descendants to include (default: 10, max: 20)`),metadataKeys:p.array(p.string()).optional().describe(`If provided, only include these metadata keys in the response`),project:q},outputSchema:{root:p.object({id:p.string(),kind:p.string(),title:p.string(),childCount:p.number()}),descendants:p.array(ze),totalDescendants:p.number().describe(`Total number of descendants found`),truncated:p.boolean().describe(`True if results were truncated due to exceeding the limit`),truncatedByDepth:p.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);v(r,`Element "${t.elementId}" not found in project "${n}"`);let i=t.maxDepth,a=t.metadataKeys,s=[],c=0,l=!1,u=!1,f=[],p=[...r.children()];for(let e of p)f.push({element:e,depth:1});for(;f.length>0;){let{element:e,depth:t}=f.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)f.push({element:e,depth:t+1})}return{root:{id:r.id,kind:r.kind,title:r.title,childCount:p.length},descendants:s,totalDescendants:c,truncated:l,truncatedByDepth:u}});function createMCPServer(e){let t=new E({name:`LikeC4`,version:fe},{instructions:`LikeC4 MCP – query and navigate LikeC4 models.
1103
1152
 
1104
1153
  Conventions:
1105
1154
  - All tools are read-only and idempotent.
@@ -1112,6 +1161,8 @@ Available tools:
1112
1161
  - read-element — Full element details including relationships, includedInViews, deployedInstances, metadata and sourceLocation. Input: { id, project? }.
1113
1162
  - read-deployment — Details of a deployment node or deployed instance. Input: { id, project? }.
1114
1163
  - read-view — Full view details (nodes/edges) and sourceLocation. Input: { viewId, project? }.
1164
+ - render-view — Renders a view as an interactive diagram (pan/zoom/fit) inline in chat, for hosts that support MCP Apps. Input: { viewId, project? }.
1165
+ - preview-view — Renders a preview of a LikeC4 view defined by DSL text, in the context of an existing project's real elements, without saving to disk. The view id must be new: if it already exists, the tool returns an error and asks for a different id. Input: { dsl, project? }.
1115
1166
  - find-relationships — Direct and indirect relationships between two elements in a project. Input: { element1, element2, project? }.
1116
1167
  - query-graph — Query element hierarchy (ancestors, descendants, siblings, children, parent) and relationships (incomers, outgoers). Input: { elementId, queryType, includeIndirect?, project? }.
1117
1168
  - 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? }.
@@ -1119,7 +1170,7 @@ Available tools:
1119
1170
  - query-by-metadata — Search elements by metadata key-value pairs with exact/contains/exists matching. Input: { key, value?, matchMode?, project? }.
1120
1171
  - query-by-tags — Advanced tag filtering with boolean logic (allOf, anyOf, noneOf). Input: { allOf?, anyOf?, noneOf?, project? }.
1121
1172
  - find-relationship-paths — Discover all paths (chains of relationships) between two elements with BFS traversal. Input: { sourceId, targetId, maxDepth?, includeIndirect?, project? }.
1122
- - batch-read-elements — Read details of multiple elements in a single call, reducing round-trips. Input: { ids, project? }.
1173
+ - batch-read-elements — Read details of multiple elements in a single call, reducing round-trips. Includes links and sourceLocation, same as read-element. Input: { ids, project? }.
1123
1174
  - query-by-tag-pattern — Search elements by tag prefix/contains/suffix patterns. Input: { pattern, matchMode?, project? }.
1124
1175
  - element-diff — Compare two elements side-by-side showing differences in properties, tags, metadata, and relationships. Input: { element1Id, element2Id, project? }.
1125
1176
  - subgraph-summary — Compact summary of all descendants of a parent element with metadata, tags, and relationship counts. Input: { elementId, maxDepth?, metadataKeys?, project? }.
@@ -1132,4 +1183,4 @@ Instructions:
1132
1183
  - If response returns "sourceLocation", provide link to this location in the editor
1133
1184
 
1134
1185
  Full documentation: https://likec4.dev/llms-full.txt
1135
- `,enforceStrictCapabilities:!0,capabilities:{completions:{},tools:{},logging:{},prompts:{},resources:{}}});return t.server.onerror=e=>{d.error(u(e))},m(t,setMcpServerCtx,Z,$,de,ue,pe,he,X,q,h(ae,se,le,te,re,ne),h(U,K,_e),h(applySemanticLayoutPrompt,applySemanticLayoutTool),projectResource)}var StdioLikeC4MCPServer=class{services;options;transport=void 0;_mcp=void 0;constructor(e,t={}){this.services=e,this.options=t}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(){if(!this.transport){F.info(`Starting MCP stdio server`),this._mcp=createMCPServer(this.services),setMcpServerCtx(this._mcp),this.transport=new s(this.options.stdin,this.options.stdout);try{await this._mcp.connect(this.transport)}catch(e){throw await this.stop(),e}F.info(`LikeC4 MCP Server running on stdio`)}}async stop(){if(this.transport)try{F.info(`Stopping MCP stdio server`),await this.transport.close(),this._mcp&&await this._mcp.close()}finally{this._mcp=void 0,this.transport=void 0,setMcpServerCtx(void 0)}}};async function createHonoApp(e){let t=new j;t.use(`*`,ee({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();setMcpServerCtx(n);let r=new k({eventStore:new O({})});return t.all(`/mcp`,async e=>(n.isConnected()||await n.connect(r),await r.handleRequest(e))),t.notFound(e=>(F.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)=>(F.error(u(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=A({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{services;_port;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()}F.info(`Starting MCP server on port {port}`,{port:e}),this._port=e,this.server=await startServer({factory:()=>createMCPServer(this.services),port:e}),F.info(`MCP server ready at http://0.0.0.0:{port}/mcp`,{port:e})}stop(){let e=this.server;return e?(F.info(`Stopping MCP server`),this.server=void 0,setMcpServerCtx(void 0),new Promise(t=>{e.close(e=>{e?F.error(`Failed to stop MCP server`,{err:e}):F.info(`MCP server stopped`),t()})})):(F.info(`MCP server is not running, nothing to stop`),Promise.resolve())}};async function initLikeC4MCP(o){let s=n(o,{workspacePath:`.`,mcp:`stdio`,graphviz:`wasm`,watch:!0,throwIfInvalid:!1,configureLogger:!0});s.configureLogger&&(e({useStdErr:s.mcp===`stdio`,colors:a.isColorSupported&&s.mcp!==`stdio`,logLevel:i?`debug`:`info`}),process.on(`uncaughtException`,e=>{F.error(`uncaughtException`,{err:e})}),process.on(`unhandledRejection`,e=>{F.error(`unhandledRejection`,{err:e})}));let c=r(s.workspacePath);F.info`Loading LikeC4 from workspace: ${c}`;let l=await t(c,{graphviz:s.graphviz,manualLayouts:!0,configureLogger:!1,watch:s.watch,throwIfInvalid:s.throwIfInvalid}),u=setLanguageServicesCtx(l.languageServices);return{server:s.mcp===`stdio`?new StdioLikeC4MCPServer(u,s.stdio):new StreamableLikeC4MCPServer(u,s.mcp.port),likec4:l}}async function startLikeC4MCP(e){let t=e?.mcp===void 0||e.mcp===`stdio`?e?.stdio?.stdin??process.stdin:void 0,n,r=!1,i=t?t.readableEnded||t.destroyed:!1,a,cleanup=async()=>{let e=n;e&&(a??=(async()=>{try{await e.server.stop()}finally{try{await e.likec4.dispose()}finally{setLanguageServicesCtx(void 0)}}})(),await a)};function stopObservingStdin(){t&&(t.off(`end`,onStdinClosed),t.off(`close`,onStdinClosed))}function onStdinClosed(){i=!0,stopObservingStdin(),r&&cleanup().catch(e=>{F.error(`Failed to stop MCP stdio server after stdin closed`,{err:e})})}t&&!i&&(t.once(`end`,onStdinClosed),t.once(`close`,onStdinClosed),i=t.readableEnded||t.destroyed);try{if(n=await initLikeC4MCP(e),n.server instanceof StdioLikeC4MCPServer){if(i)return stopObservingStdin(),await cleanup(),n;await n.server.start(),r=!0,i&&await cleanup()}else await n.server.start();return n}catch(e){if(stopObservingStdin(),n)try{await cleanup()}catch(e){F.error(`Failed to clean up MCP stdio server after start error`,{err:e})}throw e}}export{__name as i,startLikeC4MCP as n,F as r,initLikeC4MCP as t};
1186
+ `,enforceStrictCapabilities:!0,capabilities:{completions:{},tools:{},logging:{},prompts:{},resources:{}}});return t.server.onerror=e=>{b.error(y(e))},C(t,setMcpServerCtx,xe,Pe,Ne,Me,Ie,Re,be,ve,w(Oe,$,je,we,Ee,Te),w(he,_e,Be),w(applySemanticLayoutPrompt,applySemanticLayoutTool,renderViewTool,previewViewTool),w(projectResource,renderViewResource))}var StdioLikeC4MCPServer=class{services;options;transport=void 0;_mcp=void 0;constructor(e,t={}){this.services=e,this.options=t}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(){if(!this.transport){U.info(`Starting MCP stdio server`),this._mcp=createMCPServer(this.services),setMcpServerCtx(this._mcp),this.transport=new f(this.options.stdin,this.options.stdout);try{await this._mcp.connect(this.transport)}catch(e){throw await this.stop(),e}U.info(`LikeC4 MCP Server running on stdio`)}}async stop(){if(this.transport)try{U.info(`Stopping MCP stdio server`),await this.transport.close(),this._mcp&&await this._mcp.close()}finally{this._mcp=void 0,this.transport=void 0,setMcpServerCtx(void 0)}}};async function createHonoApp(e){let t=new ue;t.use(`*`,de({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();setMcpServerCtx(n);let r=new ce({eventStore:new se({})});return t.all(`/mcp`,async e=>(n.isConnected()||await n.connect(r),await r.handleRequest(e))),t.notFound(e=>(U.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)=>(U.error(y(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=le({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{services;_port;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()}U.info(`Starting MCP server on port {port}`,{port:e}),this._port=e,this.server=await startServer({factory:()=>createMCPServer(this.services),port:e}),U.info(`MCP server ready at http://0.0.0.0:{port}/mcp`,{port:e})}stop(){let e=this.server;return e?(U.info(`Stopping MCP server`),this.server=void 0,setMcpServerCtx(void 0),new Promise(t=>{e.close(e=>{e?U.error(`Failed to stop MCP server`,{err:e}):U.info(`MCP server stopped`),t()})})):(U.info(`MCP server is not running, nothing to stop`),Promise.resolve())}};async function initLikeC4MCP(t){let i=r(t,{workspacePath:`.`,mcp:`stdio`,graphviz:`wasm`,watch:!0,throwIfInvalid:!1,configureLogger:!0});i.configureLogger&&(e({useStdErr:i.mcp===`stdio`,colors:l.isColorSupported&&i.mcp!==`stdio`,logLevel:c?`debug`:`info`}),process.on(`uncaughtException`,e=>{U.error(`uncaughtException`,{err:e})}),process.on(`unhandledRejection`,e=>{U.error(`unhandledRejection`,{err:e})}));let s=a(i.workspacePath);U.info`Loading LikeC4 from workspace: ${s}`;let u=await n(s,{graphviz:i.graphviz,manualLayouts:!0,configureLogger:!1,watch:i.watch,throwIfInvalid:i.throwIfInvalid}),f=setLanguageServicesCtx(u.languageServices);return{server:i.mcp===`stdio`?new StdioLikeC4MCPServer(f,i.stdio):new StreamableLikeC4MCPServer(f,i.mcp.port),likec4:u}}async function startLikeC4MCP(e){let t=e?.mcp===void 0||e.mcp===`stdio`?e?.stdio?.stdin??process.stdin:void 0,n,r=!1,i=t?t.readableEnded||t.destroyed:!1,a,cleanup=async()=>{let e=n;e&&(a??=(async()=>{try{await e.server.stop()}finally{try{await e.likec4.dispose()}finally{setLanguageServicesCtx(void 0)}}})(),await a)};function stopObservingStdin(){t&&(t.off(`end`,onStdinClosed),t.off(`close`,onStdinClosed))}function onStdinClosed(){i=!0,stopObservingStdin(),r&&cleanup().catch(e=>{U.error(`Failed to stop MCP stdio server after stdin closed`,{err:e})})}t&&!i&&(t.once(`end`,onStdinClosed),t.once(`close`,onStdinClosed),i=t.readableEnded||t.destroyed);try{if(n=await initLikeC4MCP(e),n.server instanceof StdioLikeC4MCPServer){if(i)return stopObservingStdin(),await cleanup(),n;await n.server.start(),r=!0,i&&await cleanup()}else await n.server.start();return n}catch(e){if(stopObservingStdin(),n)try{await cleanup()}catch(e){U.error(`Failed to clean up MCP stdio server after start error`,{err:e})}throw e}}export{__name as i,startLikeC4MCP as n,U as r,initLikeC4MCP as t};
package/dist/cli.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{n as e,r as t}from"./chunks/src.mjs";import{resolve as n}from"node:path";import{defineCommand as r,runMain as i}from"citty";i(r({meta:{name:`likec4-mcp-server`,description:`LikeC4 MCP server`},args:{stdio:{type:`boolean`,description:`use stdio transport (default)`,required:!1},http:{type:`boolean`,description:`use streamable http transport`,required:!1},port:{type:`string`,description:`change http port`,valueHint:`number`,required:!1},graphviz:{type:`enum`,options:[`binary`,`wasm`],description:`use binary or wasm graphviz`,default:`wasm`,required:!1},watch:{type:`boolean`,description:`disable watch for changes`,default:!0,required:!1},workspace:{type:`positional`,description:`change workspace, can be set by LIKEC4_WORKSPACE env`,valueHint:`directory`,required:!1,default:process.env.LIKEC4_WORKSPACE||`.`}},setup({args:e}){if(e.stdio&&(e.http||e.port))throw Error(`stdio and http are mutually exclusive`)},async run({args:r}){let i=r.stdio||!r.http&&!r.port,a=r.port?parseInt(r.port):33335,o=n(r.workspace||`.`);r.watch&&t.warn`watch for changes enabled, use ${`--no-watch`} to consume less resources`,await e({workspacePath:o,graphviz:r.graphviz,mcp:i?`stdio`:{port:a},configureLogger:!0,watch:r.watch})}})).catch(e=>{t.error(e),process.exit(1)});
2
+ import{n as e,r as t}from"./chunks/src.mjs";import{resolve as n}from"node:path";import{defineCommand as r,runMain as i}from"citty";const a=r({meta:{name:`likec4-mcp-server`,description:`LikeC4 MCP server`},args:{stdio:{type:`boolean`,description:`use stdio transport (default)`,required:!1},http:{type:`boolean`,description:`use streamable http transport`,required:!1},port:{type:`string`,description:`change http port`,valueHint:`number`,required:!1},graphviz:{type:`enum`,options:[`binary`,`wasm`],description:`use binary or wasm graphviz`,default:`wasm`,required:!1},watch:{type:`boolean`,description:`disable watch for changes`,default:!0,required:!1},workspace:{type:`positional`,description:`change workspace, can be set by LIKEC4_WORKSPACE env`,valueHint:`directory`,required:!1,default:process.env.LIKEC4_WORKSPACE||`.`}},setup({args:e}){if(e.stdio&&(e.http||e.port))throw Error(`stdio and http are mutually exclusive`)},async run({args:r}){let i=r.stdio||!r.http&&!r.port,a=r.port?parseInt(r.port):33335,o=n(r.workspace||`.`);r.watch&&t.warn`watch for changes enabled, use ${`--no-watch`} to consume less resources`,await e({workspacePath:o,graphviz:r.graphviz,mcp:i?`stdio`:{port:a},configureLogger:!0,watch:r.watch})}});i(a).catch(e=>{t.error(e),process.exit(1)});export{};