Skip to main content
Glama
SiroSuzume

MCP ts-morph Refactoring Tools

by SiroSuzume

find_unused_exports_by_tsmorph

Find unused exports across your TypeScript project by analyzing AST to list symbols with no external references, helping identify dead code candidates for safe cleanup.

Instructions

[ts-morph] List exports that have no references outside their declaring file across the project. Read-only.

When to use

  • Hunting for dead code candidates after a refactor or migration.

  • Auditing a module's surface area: which exports does nobody actually consume?

  • Pre-deletion safety check before manually removing exports — combine with find_references_by_tsmorph to double-confirm.

When NOT to use

  • You want a single symbol's references — use find_references_by_tsmorph.

  • Single-file unused locals — tsc --noUnusedLocals is faster.

Detection scope

Reports:

  • export function/class/const/let/var/enum/interface/type ... (inline export keyword)

  • export default function/class ... and export default <Identifier>

  • export = <Identifier> (CommonJS)

Detection algorithm

For each candidate identifier, findReferencesAsNodes() is run and the following references are excluded before deciding "unused":

  • References inside the SAME file as the declaration (internal use does not count).

  • References inside any ExportDeclaration (pure re-export sites like export { x } from "./y" or export *). This means a symbol re-exported only via a barrel — with nothing actually consuming the barrel — IS reported as unused.

  • References in node_modules.

If 0 references remain, the export is reported.

Known limitations (this tool returns CANDIDATES, not verdicts)

Static analysis cannot see:

  • Dynamic require() / import() resolved from runtime strings.

  • File-system / convention based routing (Next.js page.tsx, Remix routes, etc.). Pass these as entryPoints.

  • Symbols looked up via reflection or string keys.

  • Pure local re-exports (export { x } without from) where x is declared by a separate const x = ... in the same file — this form is not enumerated.

  • Mixed function + namespace declarations may be partially missed.

  • Workspace packages that publish built output: in a monorepo, when a scanned package's package.json entry points (exports / main / module / types) resolve outside the scanned sources (e.g. "exports": { ".": "./dist/index.js" }), imports from OTHER workspace packages resolve to the built files (or node_modules) instead of the scanned sources. Every export of such a package is then reported unused even when it IS consumed — a systematic false positive. The tool detects this shape and prepends a ⚠ package-level warning to the result; treat all candidates from a warned package as low confidence. Workaround: point that package's exports at source files for analysis, or verify each candidate with find_references_by_tsmorph / textHits.

Default exports are high false-positive

export default <Identifier> / export = <Identifier> (shown with the [default] tag) are prone to FALSE POSITIVES: findReferencesAsNodes runs on the local identifier and often fails to connect to import Foo from "./mod" default-import sites. A default export reported here with textHits well above 0 is almost certainly actually used. Treat [default] candidates as low confidence and always confirm with find_references_by_tsmorph.

Always verify a candidate with find_references_by_tsmorph before deletion.

Options

  • tsconfigPath: absolute path to tsconfig.json.

  • entryPoints: list of absolute file paths whose exports should be skipped (treat as public API). Reference sites IN these files still count as "used" automatically.

  • excludeFilePatterns: substrings; any file whose absolute path includes() a pattern is not scanned. Use this for test files (e.g. ".test."), generated dirs, etc.

  • maxResults: cap on number of reported entries. Default 100. When reached, scanning stops and truncated becomes true — narrow scope with the filters above and retry.

Output modes (responseFormat)

  • "list" (default): one line per candidate (format below).

  • "summary": aggregate counts for the WHOLE project — total, delete-safety split (deletable vs unexport-only), default-export count, and breakdowns by kind and by directory. On large repos the per-line list easily blows past the response size limit, so start with "summary" to see where dead code clusters, then narrow with entryPoints / excludeFilePatterns and switch to "list" for exact locations. (summary scans the whole project regardless of maxResults.)

Result format (list mode)

A bullet list of candidates with file:line:column, symbol name, declaration kind, a [default] tag for default exports, textHits=N, and sameFileRefs=N.

sameFileRefs — decides delete vs. unexport (read this first)

Every reported export is, by definition, unreferenced OUTSIDE its declaring file. sameFileRefs tells you whether it is still used INSIDE that file (declaration itself and re-export sites excluded), which determines the safe action:

  • sameFileRefs=0: not used anywhere, including its own file → truly dead, safe to delete the whole declaration (combine with textHits=0 for highest confidence).

  • sameFileRefs=1+: used within its own file → only the export keyword is unnecessary. Remove export, but KEEP the declaration — deleting it breaks the in-file references.

Deleting every reported declaration blindly will break the build: the majority are often sameFileRefs=1+ (over-exported but internally used).

textHits — text-occurrence triage hint

textHits is the number of word-boundary occurrences of the export's name in OTHER source files (declaring file excluded — so it says nothing about same-file usage; use sameFileRefs for that):

  • textHits=0: no OTHER file mentions the name. Does NOT by itself mean deletable — still check sameFileRefs.

  • textHits=1+: the name appears as a string literal, JSX tag, dynamic import().then(m => m.X), or comment. Verify with find_references_by_tsmorph before deleting. Short names (e.g. a, id) match incidentally — discount accordingly.

⚠ Package-level warnings

When a package that produced candidates publishes built output (see Known limitations), a ⚠ warnings block is prepended to the result (both list and summary modes) naming the package, its out-of-scan entry points, and how many candidates are affected. Those candidates are likely false positives.

Trailing line reports Scanned files: N and Truncated: bool.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tsconfigPathYesAbsolute path to the project's tsconfig.json.
entryPointsNoAbsolute file paths to treat as public API. Exports declared here are skipped.
excludeFilePatternsNoSubstrings; files whose absolute path includes any of these are not scanned.
maxResultsNoCap on reported entries (list mode). Default 100. Ignored intent in "summary" mode, which scans the whole project.
responseFormatNo"list" (default): one line per candidate. "summary": aggregate counts (delete-safety / kind / directory) for the WHOLE project — use this first on large repos to avoid huge output, then narrow with entryPoints/excludeFilePatterns and switch to "list".list
expandNamespaceImportsNoDefault true. Inject synthetic named imports into files containing `import * as ns from "./mod"` so that exports of the target module register as 'used' even when consumed only via `{ ...ns }` spread or other escaping patterns. Set to false if you want raw findReferences semantics.
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses the tool's behavior: read-only, detection algorithm, excluded reference types, known limitations (dynamic imports, routing conventions, monorepo false positives), and detailed guidance on interpreting results (`sameFileRefs`, `textHits`). It even warns about default export false positives and package-level warnings.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is quite long but well-structured with headings, bullet points, and code formatting. Each section serves a purpose—core statement, usage guidelines, algorithm, limitations, options, output format, and result interpretation. While no sentence seems wasted, it could be slightly trimmed without losing value. The front-loading of purpose and usage is effective.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (6 parameters, no output schema, no annotations), the description is remarkably complete. It covers not only how to invoke the tool but also how to interpret results (`sameFileRefs`, `textHits`), common pitfalls (default exports, monorepo false positives), and strategies for narrowing scope. The agent can confidently use this tool based solely on the description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although the input schema already provides parameter descriptions (100% coverage), the description adds significant practical context: how `entryPoints` affect results, when to use `responseFormat='summary'` for large repos, how `maxResults` interacts with summary mode, and the purpose of `expandNamespaceImports`. This guidance helps the agent use parameters effectively.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear statement: 'List exports that have no references outside their declaring file across the project. Read-only.' It explicitly distinguishes from the sibling tool `find_references_by_tsmorph` in the 'When NOT to use' section, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes dedicated 'When to use' and 'When NOT to use' sections, providing concrete scenarios and naming alternative tools (e.g., `find_references_by_tsmorph`, `tsc --noUnusedLocals`). This gives the agent clear context for selecting the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SiroSuzume/mcp-ts-morph'

If you have feedback or need assistance with the MCP directory API, please join our Discord server