TypeScript code intelligence that makes AI coding agents smarter and more efficient.
ts-scangives LLM-powered tools instant access to type errors, imports, exports, definitions, references, callers, and more — so “dumber” models produce reliable code and “smarter” models use fewer tokens.
AI coding assistants often struggle with large TypeScript codebases:
- Less capable (cheap) models generate incorrect imports, miss type errors, or guess symbol locations – leading to broken code.
- Powerful (expensive) models can reason about the project structure, but reading entire files or search results wastes context-window tokens.
“Understand the codebase” via grep → open file → reason is not free:
- One-shot lookups (find a name, list exports) are already O(N) LLM work over the repo.
- Graph questions — who calls this? / is it reachable from package exports? — need a separate search at every level. Fan-out × depth lands around O(N·D) and often ~O(N²) agent loops. On large monorepos that tracking usually has no practical sense: the model burns tokens before it finishes the walk.
ts-scan does not make graph walks magically free. The Language Service / caller walk still runs inside the tool (with project reuse, depth caps, and other optimizations) — that cost lives on cheap CPU. The model pays O(1) tool roundtrip and reads a compact answer instead of spending expensive LLM cycles on each hop.
Per-tool LLM complexity estimates: docs/tools/README.md.
| Feature | Description |
|---|---|
| Instant type checking | Single-file errors via the language service (check_type_errors) |
| Import / export introspection | Signatures + JSDoc without reading implementations |
| Symbol resolve | Correct import path for a named export (resolve_symbol) |
| Hover & definition | Position-based inspect and go_to_definition |
| Diagnostics ranges | File or line-range diagnostics with severity/code filters |
| References & callers | TypeScript-identity references and static caller graphs |
| Signature help | Active overload / argument index at a call site |
| Reachability | Static paths from exports / tests / handlers / bins |
| Cheap-CPU graph walks | O(1) LLM roundtrips; heavy work stays in ts-scan, not the model |
| AI-friendly output | Compact plain text designed for LLM consumption |
| MCP server | All features as MCP tools (stdio or HTTP) |
Scenarios: docs/use-cases.md. Per-tool docs (incl. complexity): docs/tools/README.md.
Requirements: Node.js 18+ and a TypeScript project with a valid tsconfig.json.
# Install globally
npm install -g ts-scan
# Or use on-the-fly with npx
npx ts-scan <command>Configure ts-scan as an MCP server so agents can call its tools directly:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"ts-scan": {
"type": "local",
"command": ["npx", "ts-scan", "--mcp"],
"enabled": true
}
}
}ts-scan resolves the correct tsconfig from each absolute file path, including a sibling tsconfig.spec.json / tsconfig.test.json when the file is not in tsconfig.json, so one global MCP server works across monorepos and multiple packages. Source edits are picked up on the next call; changing a tsconfig reloads that project.
Install from npm, then point Cursor at the global binary:
npm install -g ts-scan{
"mcpServers": {
"ts-scan": {
"command": "ts-scan",
"args": ["--mcp"]
}
}
}After upgrading the global package, restart the MCP server in Cursor.
MCP tools require absolute file_path / relativeTo values. Relative paths are rejected so discovery never depends on the MCP process cwd (often the user home directory in Cursor).
CLI still accepts relative paths (resolved from process.cwd()). For CLI --resolve without --relative-to, PROJECT_ROOT remains a backward-compatible fallback.
Add ts-scan as a user-scoped or project-scoped MCP server (stdio):
# user scope (available in all projects)
claude mcp add --scope user --transport stdio ts-scan -- ts-scan --mcp
# project scope (current repo)
claude mcp add --scope project --transport stdio ts-scan -- ts-scan --mcpOr add a project .mcp.json (after a global/local ts-scan install):
{
"mcpServers": {
"ts-scan": {
"command": "ts-scan",
"args": ["--mcp"]
}
}
}Ensure ts-scan is on PATH (npm install -g ts-scan or npm run install-local from this repo). Restart Claude Code after changing MCP config.
Models are not trained to use ts-scan, so two skills enforce its use for any TypeScript code generation or modification. See type-safe-coder and dependency-planner for detailed workflows.
Pasteable agent instructions live in docs/AGENTS.md (all MCP tools, path rules, startup). Copy that block into a consuming project's AGENTS.md so agents use ts-scan even without the opencode skills.
# Check for type errors
ts-scan --check src/app.ts
# List imports / exports
ts-scan --imports src/app.ts
ts-scan --exports src/utils.ts
# Resolve a symbol (prefer --relative-to)
ts-scan --resolve UserService --relative-to src/index.ts
# Hover / definition at a position
ts-scan --inspect src/app.ts --line 42
ts-scan --definition src/app.ts --line 42
# Diagnostics (optional range)
ts-scan --diagnostics src/app.ts --start-line 10 --end-line 40
# References / callers (position or symbol)
ts-scan --references src/types.ts --line 12
ts-scan --references-symbol RuntimeEdge --relative-to src/app.ts
ts-scan --callers src/create-server.ts --line 1
ts-scan --callers-symbol createServer --relative-to src/index.ts
# Signature help / reachability
ts-scan --signature-help src/client.ts --line 40 --column 18
ts-scan --reachability src/create-server.ts --line 1ts-scan can run as an MCP server, exposing all commands as tools that AI agents can call directly – no shell commands needed.
# Stdio (Claude Code, Claude Desktop, Cline, Cursor, opencode, etc.)
ts-scan --mcp
# HTTP
ts-scan --mcp --port 3000| Tool Name | Description | Parameters (summary) | Docs |
|---|---|---|---|
check_type_errors |
Type errors for a file | absolute file_path |
doc |
list_imports |
Imports + signatures/JSDoc | absolute file_path, detail? |
doc |
list_exports |
Exports + signatures/JSDoc | absolute file_path, grep? |
doc |
resolve_symbol |
Import path for a symbol | symbol, absolute relativeTo |
doc |
inspect |
Hover at position | file_path, line, column?, compact? |
doc |
get_diagnostics |
Diagnostics / range | file_path, range?, severity?, codes? |
doc |
go_to_definition |
Definition at position | file_path, line, column? |
doc |
find_references |
Identity references | position or symbol mode; crossPackage?… |
doc |
signature_help |
Call-site parameter hints | file_path, line, column |
doc |
find_callers |
Static caller graph | position or symbol mode; maxDepth?… |
doc |
reachability |
Paths from entrypoints | file_path, line, column?, entrypointKinds?… |
doc |
Index: docs/tools/README.md.
CLI file commands resolve the correct tsconfig from the file path, including sibling test configs. For CLI --resolve without --relative-to, PROJECT_ROOT (or cwd) still selects the fallback project root.
All commands return compact plain-text designed for LLM consumption:
- No noise – Only the requested data, no configuration logs or build artifacts.
- Token-efficient – Minimal output so expensive models don't burn tokens on irrelevant context.
- Machine-parseable – Structured text with clear separators between blocks.
Example output:
Types and JSdoc:
//from
import { fetchUser } from "./api/user"
/** Fetches a user by ID. Returns a promise that resolves to the user object. */
export async function fetchUser(id: string): Promise<User>
Test projects and a project cache that stays current:
- Sibling tsconfigs – when a file is not in
tsconfig.jsonor its project references, a siblingtsconfig.*.jsonis used (tsconfig.spec.json,tsconfig.test.json,tsconfig.vitest.json,tsconfig.e2e.json). A file outside every include is opened against the nearest tsconfig. - Fresh project cache – an LRU of language-service projects is kept per tsconfig. On the next call, source files whose mtime or size changed are reloaded; a changed tsconfig or
extendsconfig rebuilds that project.
Fewer false answers from export signatures, symbol lookup, and reachability:
- Export signatures – namespaces stay
export namespace, aliases stayexport { Local as Public }, and an explicit return type is kept (LabelA) instead of a shortened checker name (Label). resolve_symbollookup – local exports are read from the AST (async/abstract/declare, multilineexport { }). Comments, strings, and the local name inexport { Local as Public }are not treated as that export.resolve_symbolranking – when several packages export the same name, only the first hit is Recommended import; the rest are Also exported from.- Reachability roots – a public export is a
package.jsonexports/main/typestarget (including re-exports from that entry), not a guessedsrc/index.ts. A file whose name containshandleris not a handler entry;wire-*-handlersand bridge-style names still are.
New MCP / CLI tools (see docs/tools):
inspect/--inspect– hover: symbol, type, JSDoc at a file position.get_diagnostics/--diagnostics– diagnostics for a file or line range (severity / code filters).go_to_definition/--definition– definition at a position (prefer source overdist/ junk peers).find_references/--references/--references-symbol– TypeScript-identity references.signature_help/--signature-help– active signature and argument index at a call site.find_callers/--callers/--callers-symbol– static caller graph.reachability/--reachability– static paths from exports / tests / handlers / bins.
Monorepo / agent reliability improvements:
- Owning-package tsconfig discovery – resolve the correct
tsconfig.jsonfrom each absolute file path (ancestors + project references); one global MCP server works across packages withoutPROJECT_ROOT. - Composite / TS6307 fix – load the owning package file list so valid sibling modules no longer report false
TS6307. - Clearer path errors – distinguish missing file, excluded / outside include, and no tsconfig found; missing paths list nearest sibling
.tsfiles. list_exportsgrep – exact, case-sensitive, OR filters; explicit message when nothing matches.- Export signatures – no more duplicated
export export type …. resolve_symbolranking – prefer package entry (Recommended import) over cross-package relative Implementation path.list_importscompact mode – summarize external (non-relative) types by default (detail: "full"for complete surfaces); type aliases omit RHS; avoid tautologicalT = Taliases.
- MCP / CLI support for
PROJECT_ROOTwhen the process cwd is not the project root.
ISC – see the LICENSE file for details.