Here is a question a controls engineer actually asks:
"When I drag a component onto the canvas in the Web Editor and hit save, what ends up going out on the wire to the controller?"
It is a completely reasonable question. It is also, in most codebases, a two-hour archaeology session — because answering it means crossing four languages, three runtimes and a network protocol, and no single search tool sees all of them.
Fantom MCP Server was built for that question.
The stack the question crosses
Sandstar Web Editor is a visual programming environment for the Sedona Framework. You drag components onto a canvas, wire them together, and deploy to live controllers. Underneath, one user gesture travels through a genuinely heterogeneous stack:
Four languages, one feature. grep can find the string sox in all of them. What it cannot tell you is that the writeComp handler in the Fantom weblet is the thing the Vue store calls, or that changing its signature breaks a subscription path three layers away.
Twenty-three languages in one index
Fantom MCP Server's language registry covers 23 languages:
axon · c · cpp · csharp · css · dart · fantom · go · html · java · javascript · json · kotlin · php · polymer · python · ruby · rust · scala · swift · typescript · vue · xeto
Three of those grammars — Fantom, Axon and Xeto — were written for this project, because none existed. That matters for the same reason it mattered in Axon MCP Server: a chunk that ends mid-block is a chunk that means nothing.
Three indexes, not one
Semantic search alone is not enough to answer a structural question. Fantom MCP Server maintains three stores and uses them together:
- LanceDB holds the vectors, behind an ANN index. Earlier releases did a full table scan on every query; building the approximate index is what took retrieval from "noticeable" to "immediate."
- LadybugDB (Kuzu) holds the actual call graph — nodes and edges, persisted.
getCallers,getCallees,getCodeImpactandgetCodeNeighborsare graph queries, not grep passes re-run per request. - FlexSearch holds a keyword index, because sometimes you genuinely do know the identifier.
Results from the vector and keyword sides are fused with reciprocal rank fusion, then optionally re-ranked by a cross-encoder or an OpenRouter reranker. Which of those runs, and where, is a routing decision — covered in its own post.
What this looks like in practice
askCodebase runs a retrieval loop over the index and synthesises a cited answer — the citation being the point. For a cross-language question, the useful shape of an answer is less "here is a paragraph" and more "here are the four functions, in four languages, in call order."
Illustrative — the following is the shape of a cross-stack answer, not a transcript of a specific run:
- a Vue store action in
.ts, which posts a JSON message - the weblet handler in
.fanthat receives it - the router entry mapping that message to one of the 17 SOX command types
- the component-write path that ends up on the wire over DASP/UDP
You can then ask getCodeImpact on any one of them and find out what else moves if you change it. That is the query that is genuinely hard without a persisted graph, and it is the one that saves the afternoon.
Asking how the code got this way
Structure answers "what calls what." A second family of tools answers "and why is it like that":
whatChangedRecently— what moved in this project latelygetSymbolHistory— how one function evolvedexplainSymbolChange— what a particular change diddiffIndexRuns— what the index itself saw differently between two runs
For a codebase maintained across years and handed between integrators, this is often more valuable than the semantic search. The code is readable. The reasoning is what left with the last engineer.
And it still does the migration
SkySpark 4.0 changed the extension format. migrateSkySpark4x rewrites using statements, converts Axon strings, generates the Xeto files (lib.trio, funcs.xeto, lib.xeto), validates with the Fantom compiler, and tags a Git backup on the way in. commitMigration finishes it; rollbackMigration undoes it with a single call.
41 MCP tools in total, across documentation search, semantic code search, call-graph analysis, history, generation and migration.
The honest summary
Fantom MCP Server does not make your codebase simpler. The Web Editor will still be Vue talking to Fantom talking to SOX over UDP, and that is the correct architecture for what it does.
What changes is that the boundaries between those layers stop being walls for a search tool. One question, asked once, crosses all four — and comes back with the functions, in order, in whatever language they happen to be written in.
Fantom MCP Server is source-available at github.com/Project-SandStar/FantomMcpServer. See also the project page and Sandstar Web Editor.