Language capability matrix

trace-mcp ships 81 language plugins. They are not equally deep, and this page says how deep each one is. Every language in the list gets symbol extraction; call graphs and type edges are a much smaller set. For what each language’s framework integrations understand on top of that, see supported frameworks; for the include globs referenced below, see configuration. The edge columns are what makes the graph queryable: a language with call edges answers get_call_graph and call-aware find_usages (tools reference), one without them still answers search and get_outline. How those edges are produced is the two-pass pipeline in architecture.

What the columns mean

Column Meaning
Parser tree-sitter — real grammar-based AST. regex — pattern extraction, no AST. custom — hand-written parser for a structured format.
Default The shipped default include globs reach files with this extension. Where this is empty the plugin only runs once you add the extension to include in .trace.json.
Imports Import statements are resolved into graph edges. Plugins outside this set still parse imports, but nothing turns them into edges yet.
Calls A call-graph resolver exists, so “who calls this” is answerable for this language.
Types Type-annotation or inheritance edges are resolved.
Tests At least one test exercises this plugin directly.

Default config vs. plugin count. The default include is one global glob over every extension the plugins below claim, so 78 of the 81 plugins run wherever their files live in the repo — no directory anchoring. The remaining 3 are pure data formats, left out because lockfiles, fixtures and .svg would swamp the index for little symbol value. Ask for them explicitly if you want them:

{ "include": ["schemas/**/*.json", "k8s/**/*.xml"] }

Note that include in a project config replaces the built-in list rather than adding to it, so copy the default glob alongside your addition if you still want the rest of the repo indexed.

A regex plugin still gives you working search, get_outline and symbol navigation — that covers most “find it and read it” work. What it does not give you is a call graph: that needs an AST plus a per-language resolver. Where the Calls column is empty, get_call_graph returns nothing for that language unless you enable LSP enrichment (lsp.enabled: true) or ingest a SCIP index.

Imports means edges, not parsing. Most plugins extract import statements; far fewer have a pipeline pass that resolves the specifier to a target node, and without one the extracted import never becomes an edge. This column counts the second thing, so it is much shorter than the language list — see src/indexer/edge-resolvers/import-capable-languages.ts.

“Yes” is not “every import.” C# is the narrowest case in this column today: its resolver only trusts using forms that name a specific type (using static X.Y.Type, an aliased using A = X.Y.Type) and deliberately leaves a plain using Namespace; — the overwhelming majority of real C# imports — unresolved, because a namespace can span most of a codebase and a file-level edge to all of it would be closer to noise than signal. On a large real repo (Newtonsoft.Json, ~5,000 using directives) this column’s “yes” for C# produced edges for 12 of them — roughly 0.2%. See src/indexer/edge-resolvers/csharp-imports.ts for the reasoning and src/indexer/plugins/language/csharp/helpers.ts for extraction.

Import resolution depth on real repos

C#’s 0.2% above is not an outlier worth a special paragraph — it is one point on a spread every resolver-bearing language sits somewhere on. Imports: yes only means a pipeline pass exists; it says nothing about how much of a real codebase that pass actually connects. Measured on one well-known public repo per language — clone it, run the real indexing pipeline, read the { created, external, ambiguous } counts each resolver already logs (src/indexer/edge-resolvers/*-imports.ts), filtered by that resolver’s exact log message so a multi-language repo can’t attribute one language’s counts to another — the reusable, reproducible measurement is scripts/measure-import-resolution.ts, and its output is docs/_data/import-resolution.json:

Language Repo Commit Edges created Left external Left ambiguous Share resolved
lua mpeterv/luacheck 7360cfb4cf2c 130 16 0 89.0%
c curl/curl 8f6046485e15 3138 990 0 76.0%
elixir elixir-plug/plug 73404f851852 87 44 0 66.4%
php guzzle/guzzle 93939470950a 374 247 0 60.2%
ruby sinatra/sinatra cb22afd7902b 131 88 0 59.8%
rust BurntSushi/ripgrep 3fce3b5bb023 453 447 0 50.3%
typescript colinhacks/zod 7a00236683c7 660 660 0 50.0%
go spf13/cobra adbc8813901b 159 178 0 47.2%
java google/gson b3f4ca20087f 1085 1586 0 40.6%
kotlin square/okhttp dfcfab38248e 2621 4330 0 37.7%
cpp fmtlib/fmt 2d4d8a13c2b4 155 411 0 27.4%
python pallets/flask d318b6834711 96 333 0 22.4%
csharp JamesNK/Newtonsoft.Json 09bb545d7296 12 4979 0 0.2%

“Left external” does not mean “missed,” but it does not always mean “correctly out of scope” either — read the resolver. Most of the time a specifier a resolver leaves external is genuinely a third-party or standard library import (import "github.com/spf13/pflag", import os), and resolving it to a file in this repo would be wrong. But the bucket’s exact meaning is resolver-specific: csharp’s external count also holds plain using Namespace; directives that are internal to the repo — left unresolved by design because a namespace can span most of a codebase (see the C# paragraph above), not because the target lives outside it. Treat this table as what “the resolver exists” costs out to on one real codebase per language, not as a defect list. Every row is n=1 — one repo’s import style, not a distribution; re-run the script against a second repo per language before treating any row as a trend rather than a data point.

Matrix

Language Extensions Parser Default Imports Calls Types Tests
ada .adb .ads .ada regex (multi-pass) yes — — — yes
al .al regex yes — — — yes
apex .cls .trigger .apex regex (multi-pass) yes — — — yes
assembly .asm .s .S regex yes — — — yes
astro .astro tree-sitter yes yes — — yes
autohotkey .ahk .ah2 regex yes — — — yes
bash .sh .bash .zsh tree-sitter yes — — — yes
blade .blade.php regex yes — — — yes
c .c .h tree-sitter yes yes — — yes
clojure .clj .cljs .cljc .edn regex yes — — — —
cmake .cmake CMakeLists.txt regex yes — — — yes
cobol .cob .cbl .cpy .cobol regex (multi-pass) yes — — — yes
common-lisp .lisp .lsp .cl .asd regex (multi-pass) yes — — — yes
cpp .cpp .cxx .cc .hpp tree-sitter yes yes — — yes
csharp .cs tree-sitter yes yes — — yes
css .css .scss .sass .less tree-sitter yes yes — — yes
cuda .cu .cuh regex yes — — — —
d .d .di regex (multi-pass) yes — — — yes
dart .dart tree-sitter yes — — — yes
dockerfile Dockerfile .dockerfile regex yes — — — yes
ejs .ejs regex yes — — — yes
elisp .el .elc tree-sitter yes — — — —
elixir .ex .exs tree-sitter yes yes — — yes
elm .elm tree-sitter yes — — — —
erlang .erl .hrl regex yes — — — yes
form .frm .prc regex yes — — — —
fortran .f .f90 .f95 .f03 regex yes — — — yes
fsharp .fs .fsi .fsx regex (multi-pass) yes — — — —
gdscript .gd regex yes — — — yes
gleam .gleam regex yes — — — yes
glsl .glsl .vert .frag .geom regex yes — — — —
go .go tree-sitter yes yes — — yes
graphql .graphql .gql custom yes — — — yes
groovy .groovy .gradle .gvy regex yes — — — yes
haskell .hs .lhs regex yes — — — yes
hcl .tf .hcl .tfvars custom yes yes — — yes
html .html .htm tree-sitter yes yes — — yes
ini .ini .cfg .conf .properties regex — — — — —
java .java tree-sitter yes yes — — yes
json .json .jsonc .json5 tree-sitter — — — — yes
julia .jl regex yes — — — yes
kotlin .kt .kts tree-sitter yes yes — — yes
lean .lean regex yes — — — —
lua .lua .luau tree-sitter yes yes — — yes
magma .m .mag .magma regex yes — — — —
makefile Makefile makefile .mk GNUmakefile regex yes — — — yes
markdown .md .mdx .markdown .qmd custom yes yes — — yes
matlab .m .mlx .mat regex (multi-pass) yes — — — yes
meson meson.build meson_options.txt regex yes — — — —
nim .nim .nims .nimble regex yes — — — yes
nix .nix regex yes — — — yes
objc .m .mm tree-sitter yes — — — yes
ocaml .ml .mli tree-sitter yes — — — yes
pascal .pas .dpr .dpk .lpr regex (multi-pass) yes — — — yes
perl .pl .pm .t regex yes — — — yes
php .php tree-sitter yes yes yes — yes
plsql .pls .plb .pck .pkb regex (multi-pass) yes — — — yes
powershell .ps1 .psm1 .psd1 regex (multi-pass) yes — — — yes
prisma .prisma custom yes — — — yes
protobuf .proto regex yes — — — yes
python .py .pyi tree-sitter yes yes yes yes yes
r .r .R .Rmd regex yes — — — yes
ruby .rb .rake tree-sitter yes yes — — yes
rust .rs tree-sitter yes yes — — yes
scala .scala .sc tree-sitter yes — — — yes
solidity .sol tree-sitter yes — — — yes
sql .sql regex yes — — — yes
svelte .svelte regex yes yes — — yes
swift .swift tree-sitter yes — — — yes
tcl .tcl .tk .itcl .itk regex (multi-pass) yes — — — yes
toml .toml tree-sitter yes — — — yes
typescript .ts .tsx .mts .cts tree-sitter yes yes yes yes yes
verilog .v .sv .svh .vh regex yes — — — yes
verse .verse regex yes — — — yes
vhdl .vhd .vhdl .vho .vhs regex yes — — — yes
vimscript .vim .vimrc regex yes — — — —
vue .vue tree-sitter yes yes — — yes
wolfram .wl .wls .m .nb regex yes — — — —
xml .xml .xul .xsl .xslt custom — yes — — yes
yaml .yaml .yml custom yes yes — — yes
zig .zig .zon tree-sitter yes — — — yes

Last updated: September 15, 2026