Skip to main content
Glama
aferreiraguido

javascript-mcp-server

README.md
# javascript-mcp-server

Deterministic MCP server for semantic analysis of JavaScript/TypeScript with interpretation of the **Vue**, **Nuxt**, **Angular**, **React**, **Next**, **Svelte** and **Node** frameworks.

Inspired by the existing deterministic servers (COBOL, Go, Rust, Python, Java): the server parses, resolves types and normalizes code 100% statically; the LLM only queries already-resolved structures. The AI never interprets the framework directly.

## Deterministic mechanism

| Layer | Mechanism |
|---|---|
| JS/TS language | **TypeScript Compiler API via `ts-morph`** (AST + TypeChecker + scope) |
| Vue | **`@vue/compiler-sfc`** (`parse` + `compileScript` with `registerTS`): resolves `defineProps<Props>()`, `defineProps({})`, `defineEmits`, and the real template bindings (prop/ref/const/function). Also registers `defineModel` (v-model), `useTemplateRef` and `defineSlots<{...}>()` from `<script setup>`. RegEx fallback for Options API (`props: { a: { type, required } }` and `props: { a: String }` forms) |
| Nuxt | Conventions over Vue: `pages/` → routes, `composables/` and `utils/` → auto-imports, `server/api` → HTTP routes |
| Angular | **`@angular/compiler`** (`parseTemplate`) for real template analysis (inputs/outputs/interpolations/pipes); decorators (`@Component`, `@Input/@Output`, DI, lifecycle) and `@NgModule`/`Routes` metadata read structurally with ts-morph (RegEx fallback when there is no ts-morph project) |
| React / Next / Svelte | Structure conventions: `.tsx/.jsx` with JSX, Next `app/`/`pages/`, `.svelte` (props/stores/events/lifecycle) and Svelte 5 runes (`$state`, `$derived`, `$effect`, `$props`, `$inspect`) |
| Node | `ts-morph` + detection of `express`/`fastify`/`koa`, `node:` builtins, `process.env` |

The `ProjectManager` keeps an **incremental cache**: each file is indexed with its `size:mtime` signature; `refresh_js_project` only re-reads modified/new files and removes deleted ones, without rebuilding the whole project. Re-loading the same alias (`load_js_project`) also refreshes incrementally instead of rebuilding.

## Requirements

- Node.js ≥ 20
- npm

> Evolution roadmap: see [`ROADMAP.md`](ROADMAP.md).

## Installation and build

```bash
npm install
npm run build        # compiles to dist/
npm start            # starts the stdio server
npm run typecheck    # type checking
npm test             # smoke tests (node:test + InMemoryTransport)
npm run test:snapshots               # verifies golden markdown snapshots
npm run test:snapshots:update        # regenerates the snapshots (only if the change is intentional)
npm run test:stress                  # performance and incremental cache over a synthetic project of 240+ files (fixture in tmpdir)
```

## Tools (103)

### Projects
- `load_js_project` — loads a directory, detects the framework (if already loaded, does an incremental refresh)
- `list_loaded_projects` / `unload_js_project` / `refresh_js_project`
- `program_summary` — executive summary
- `detect_framework` — `vue | nuxt | angular | react | next | svelte | express | fastify | node | plain`
- `get_file_content` — content of a file by path suffix
- `search_source` — case-insensitive search
- `list_files` — TS/JS files with lines, optionally filter by extension (includes `.vue`)

### Language / TypeScript
- `list_functions` — functions and class methods with signature
- `list_ts_types` — interfaces, type aliases, enums, classes
- `list_variables` — module variables
- `function_call_graph` — local call graph
- `find_references` — references to a symbol
- `variable_xref` — data flow of a variable: definition/read/write (semantic classification)
- `function_callers` — invocations of a function
- `resolve_type` — definition of a symbol
- `find_check_implementations` — classes that implement/inherit
- `get_type_hierarchy` — what a type extends/implements and who uses it
- `inspect_function` — signature, parameters, doc and body of a function or `Class.method`
- `inspect_class` — decorators, extends/implements, fields, methods, constructor
- `typecheck_project` — real type errors of the project (TypeChecker diagnostics via ts-morph)

### Metrics / technical debt
- `function_metrics` — cyclomatic complexity, body lines and parameters per function/method (with filters)
- `find_high_complexity` — functions with high cyclomatic complexity (configurable threshold)
- `find_long_functions` — functions with a long body (configurable threshold)

### Quality / bug-hunt (heuristics)
- `find_unchecked_errors` — async calls without `await`/`.catch`: fetch, `fs.*`, exec, spawn, query, DB clients and `axios`/HTTP; also `map`/`forEach` over unawaited promises; excludes Node callback variants (`fs.writeFile(path, data, cb)`); exact callee match and per-line dedup
- `find_unused_variables` — unused module variables (cross-file analysis; exported excluded)
- `find_dead_code` — unreferenced functions/methods (cross-file analysis; Angular lifecycle hooks excluded)
- `find_unimplemented_interfaces` — interfaces whose methods no class implements explicitly
- `find_any_usages` — uses of `any` (annotations, `as any`, `<any>`, arrays, parameters/returns) grouped by file; type debt

### Vue
- `list_vue_components` / `analyze_vue_component` (props, emits, slots, composables, reactive state, provide/inject; `defineModel` → `v-model` section, `useTemplateRef` → `Template refs` section, `defineSlots` → `Slots` section)
- `vue_template_binding_analysis` — which script variables the template uses, classified with the compiler's real bindings (prop/ref/const/function)
- `find_vue_provide_inject` — cross-file provide/inject graph by key (resolves exported constants, detects orphan keys)
- `find_vue_unused_reactive` — declared reactive state unused in template/script (heuristic)
- `find_vue_template_undefined` — template identifiers without binding/local/builtin (heuristic)

### Nuxt
- `list_nuxt_pages` — routes from `pages/` + `server/api`
- `list_nuxt_composables` / `list_nuxt_server_api` / `list_nuxt_plugins_middleware`
- `nuxt_auto_imports` / `nuxt_config`
- `list_nuxt_page_meta` — `definePageMeta` per page (layout, middleware, title, validate, pageTransition)

### Angular
- `list_angular_components` / `analyze_angular_component` (Input/Output, DI, lifecycle)
- `angular_template_analysis` — template analysis with `@angular/compiler` (inputs/outputs/interpolations/pipes/structural directives + references without a class member)
- `angular_dependency_graph` — @Injectable services and consumers
- `list_angular_routes` / `list_angular_modules` / `list_angular_services`
- `angular_module_graph` — NgModules (declarations, imports, providers)
- `analyze_angular_route_guards` — route guards/resolvers (canActivate/canActivateChild/canActivateFn/canDeactivate/canLoad/canMatch/resolve) and their definitions
- `list_angular_standalone_components` — standalone components (composition imports) vs non-standalone (NgModule)
- `find_angular_change_detection` — ChangeDetectionStrategy (OnPush/Default) and manual CD (heuristic)
- `find_angular_subscription_leaks` — subscribe() without unsubscribe/takeUntil/async pipe (heuristic)

### React / Next / Svelte
- `list_react_components` — .tsx/.jsx components with JSX, exports and used hooks
- `analyze_react_component` — deep analysis of a component: typed props, hooks with arguments, custom hooks, early returns, memo/forwardRef and contexts
- `list_react_hooks_deps` — useEffect/useMemo/useCallback with their deps array (heuristic: flags missing deps)
- `list_react_context` — cross-file contexts: createContext, providers and useContext consumers
- `list_next_routes` — app router routes (`app/**/page.tsx`) and pages router
- `list_next_api_routes` — API routes (`app/**/route.ts`) with HTTP methods and `middleware.ts`
- `analyze_react_state` — state inventory per component: useState/useReducer/useRef with bindings
- `find_react_effect_leaks` — useEffect with listeners/timers/subscriptions without cleanup (heuristic)
- `list_next_data_fetching` — ISR/cache in app router: revalidate, generateStaticParams, generateMetadata, fetch with next.revalidate/tags and use client/server directives
- `find_react_memo_opportunities` — components with non-primitive props without memo() and JSX with new values per render (heuristic)
- `list_svelte_components` — .svelte components with props, stores and dispatched events
- `analyze_svelte_component` — deep analysis of a Svelte component: props, reactivity ($:), Svelte 5 runes ($state/$derived/$effect/$props/$inspect/…), stores, context, lifecycle, bindings, snippets and dispatched events
- `list_sveltekit_routes` — SvelteKit routes by directory convention (`src/routes`): +page, +page.server (load/actions), +layout, +server.ts (API) and +error
- `find_svelte_effect_leaks` — onMount with listeners/timers/subscriptions without cleanup in onDestroy (heuristic)
- `find_svelte_unused_stores` — stores (writable/readable/derived) unused in any template or script (heuristic)
- `list_sveltekit_server_hooks` — hooks.server.ts: handle (sequence), handleError and handleFetch with their structure
- `list_sveltekit_load_actions` — load and actions per route with details (throw error/fail/redirect/params)
- `list_react_files` — .tsx/.jsx/.svelte files with lines

### Node
- `list_api_endpoints` — Express/Fastify/Koa
- `analyze_api_endpoint` — handler call chain (→ service → repo), validation, auth middleware and next(err); resolves mounted routers
- `list_express_routers` — sub-routers, mounts, global middleware in order and error-handlers (4 args)
- `node_native_modules` / `node_entry_points` / `node_async_flow`
- `node_process_env` — variables read with `process.env` (dot/bracket), crossed with the root `.env*` files: flags defined keys (✓) and undefined keys (⚠ must be set in the environment/CI), and lists `.env` keys without any read in the code (orphans)
- `find_event_emitter_leaks` — EventEmitter listeners without their cleanup pair in scope (heuristic)
- `find_sync_io_blocking` — `fs.*Sync` in handlers/async that blocks the event loop (heuristic)
- `find_unclosed_resources` — streams/connections/http.request without close/end/destroy (heuristic)
- `find_deprecated_apis` — `new Buffer`, `url.parse`, `util.is*`, `createCipher`, `require.extensions` (heuristic)

### Cross-framework
- `module_dependency_graph` — relative imports/requires graph between modules, with cycles and modules without importers
- `find_test_mapping` — production modules (src/) → tests that import them, and src/ without tests

### Security and robustness (heuristics)
- `find_xss_vectors` — XSS vectors: v-html (Vue), `{@html}` (Svelte), dangerouslySetInnerHTML (React), innerHTML/insertAdjacentHTML/document.write
- `find_sql_injection` — query/execute with interpolated template literal or string concatenation
- `find_eval_sites` — eval / new Function / Function() (dynamic execution)
- `find_command_injection` — exec/execSync/spawn with variable interpolation
- `find_hardcoded_secrets` — keys/passwords/tokens with literal value, URLs with credentials, private keys and AWS keys
- `find_insecure_http` — http://, ws:// WebSocket and cookies without Secure/HttpOnly
- `find_silent_catches` — empty catches, console-only, or without error binding (discarded error)
- `find_event_listener_leaks` — addEventListener/setInterval without removeEventListener/clearInterval in the same scope

### Technical debt and quality (heuristics)
- `find_code_duplication` — duplicated code blocks (copy-paste) between functions/methods
- `find_todo_fixme` — TODO / FIXME / HACK / XXX markers in comments
- `find_unused_dependencies` — package.json dependencies never imported in any file
- `find_ts_ignores` — type suppressors @ts-ignore / @ts-nocheck / @ts-expect-error
- `find_magic_numbers` — numeric literals that are not 0/1 outside named constants

### TypeScript types in depth (heuristics)
- `find_non_null_assertions` — non-null assertions `expr!` (trust without verification)
- `find_unsafe_type_casts` — `as any`, `as never` and double cast `as unknown as X`
- `find_loose_generics` — type parameters without constraint or unused in the body
- `find_untyped_exports` — exported functions/methods without return type or unannotated params

## Limitations and heuristics

- The bug-hunt tools (`find_unchecked_errors`, `find_unused_variables`, `find_dead_code`, `find_unimplemented_interfaces`) are **heuristics** by design: they flag "possible" dead code and can produce false positives/negatives (indirect use via decorators, template refs or dynamic calls). The reference count is **cross-file** over the real AST (ts-morph), not `grep`.
- `find_dead_code` excludes exported symbols and Angular lifecycle hooks (`ngOnInit`, etc.), which are invoked by the framework.
- `find_dead_code`/`find_unused_variables` also count identifiers from **templates** (`.html` in Angular, `<template>` block in Vue/Nuxt): a method used only from the template is not reported as dead.
- `find_unimplemented_interfaces` is heuristic: in TypeScript interface satisfaction is structural, so a class can satisfy one without declaring it in `implements`. Empty interfaces and those extended by another interface are discarded.
- `find_any_usages` is a type-debt heuristic: it detects ts-morph's semantic `any` nodes (`: any`, `as any`, `<any>`, `any[]`, parameters/returns). It does not distinguish intentional `any` (e.g. an `any` at the boundary with untyped libraries) from accidental; it groups by file sorted by number of uses.
- `find_unchecked_errors` only considers known callees (fetch, `fs.*`, exec, spawn, query, DB client verbs…) and requires the line to be free of `await`/`.catch`/`.then`/`Promise.all`. Generic HTTP verbs (`get`, `post`, `put`, `patch`, `delete`, `head`, `options`) only count when the receiver is HTTP/axios (`axios`, `http(s)`, `client`, `request`, `api`) — so `map.get(...)` is not flagged. It also detects `map`/`forEach` that fire unawaited promises (fire-and-forget), excluding those already handled with `await`/`Promise.all`/`.catch` on the same line. Node callback variants (`fs.writeFile(path, data, cb)`, `fs.readFile(p, cb)`) are excluded because the callback receives the error.
- `variable_xref` classifies DEFINITION/WRITE/READ with ts-morph semantic references (`findReferences`); it is not interprocedural flow analysis.
- `angular_template_analysis` reports template identifiers without a class member. Members are resolved via ts-morph (incl. `@Input`/`@Output`, methods, getters/setters, constructor parameters); if there is no ts-morph project a RegEx fallback is used that can miss cases. Template pipes are resolved to the project's `@Pipe`/`@Directive` class (by class name or by the decorator's `name`) and excluded from the "without a member" section; the real `parseTemplate` errors are shown in "Compiler diagnostics". `@Component` metadata (`analyze_angular_component`, `list_angular_components`) is read structurally with ts-morph, with RegEx fallback.
- `detect_framework` is heuristic over `package.json` + directory structure. A project that uses `@vue/compiler-sfc` only as a build dependency (without SFCs in the app code) is no longer classified as Vue. React is detected by dependency + presence of `.tsx/.jsx`; Next/Svelte by dependency.
- `list_files` with `ext='.vue'` lists SFCs by directory walk (ts-morph does not parse `.vue`); the rest of the extensions come from the ts-morph project.
- `inspect_function` and `resolve_type` resolve top-level functions, nested functions and `Class.method` methods; arrow-functions are only shown in `inspect_function`.
- The analysis does not run `npm install` on the analyzed project: it only reads `package.json`, `tsconfig.json` and the source code.
- Vue SFC parsing is cached by `size:mtimeMs` signature (same criterion as the ProjectManager): SFCs are not re-parsed on every tool call.
- `analyze_vue_component`: the `Composables` section lists each composable once (dedup); `defineSlots` is extracted by finding the real `}>()` closing so it does not cut on nested types (`{ title: string }`), and `defineModel` supports generics (`defineModel<string>()`).
- `analyze_svelte_component`: the `Runes (Svelte 5)` section lists the lines with runes (`$state`, `$derived`/`$derived.by`, `$effect`/`$effect.pre`, `$props`, `$inspect`, `$bindable`); the `$:` (classic reactivity) stay in their own section. Svelte 5 converts `$:` into runes, but they are not translated into each other: each syntax is reported where it appears.

## Distribution (single-file)

The server can be packaged into **a single self-contained JS file** that includes all the
dependencies (TypeScript, ts-morph, Vue/Angular compilers). On the destination **only Node ≥ 20
is needed** — no npm, no `node_modules`, no copying the directory:

```bash
npm run dist:bundle        # generates release/javascript-mcp-server.cjs (~26 MB)
./release/javascript-mcp-server.cjs   # executable, shebang included
```

- It is the same server (103 tools), verified with stdio smoke: `initialize`, `tools/list`,
  project loading, Vue analysis with `@vue/compiler-sfc` and semantic analysis with ts-morph.
- The `--external` are the optional template-engines that `@vue/compiler-sfc` tries to `require`
  lazily inside try/catch (twig, ejs, pug, handlebars, …); they are not used and do not affect the tools.
- The bundle is CJS (`format=cjs`): even if the project is ESM, the final file is a single `.cjs`.
- The `release/` directory is not versioned; regenerate it with the script after each change.

## MCP client configuration

```json
{
  "mcpServers": {
    "javascript": {
      "command": "node",
      "args": ["/path/to/javascript-mcp-server/dist/index.js"]
    }
  }
}
```

Or using the single-file bundle:

```json
{
  "mcpServers": {
    "javascript": {
      "command": "/path/to/javascript-mcp-server/release/javascript-mcp-server.cjs"
    }
  }
}
```

## Structure

```
src/
├── index.ts             # MCP server + tool registration
├── project-manager.ts   # project loading, framework detection, ts-morph cache
├── analysis.ts          # language semantic engine (ts-morph) + cross-framework bug-hunt
├── markdown.ts / version.ts
└── ast/
    ├── vue.ts           # Vue interpretation (+ SFC cache by signature)
    ├── nuxt.ts          # Nuxt conventions
    ├── angular.ts       # Angular interpretation
    ├── react.ts         # React / Next
    ├── svelte.ts        # Svelte / SvelteKit (leaks, stores, hooks, load/actions)
    ├── node.ts          # Node/Express/Fastify
    ├── security.ts      # security (XSS, injection, secrets, …)
    ├── debt.ts          # technical debt (duplication, unused deps, magic numbers)
    ├── cross.ts         # module_dependency_graph and find_test_mapping
    └── types.ts         # TypeScript types in depth (non-null, casts, generics, exports)
test/
├── smoke.test.ts        # smoke tests via InMemoryTransport
├── snapshot.test.ts     # golden markdown snapshots (regenerate with npm run test:snapshots:update)
├── stress.test.ts       # performance/incremental cache (npm run test:stress; generates the fixture in tmpdir)
├── generate-stress.ts   # generator of the synthetic project of 240+ files used by stress.test.ts
├── snapshots/           # golden tool outputs
└── fixtures/            # example projects (vue-basic, vue-deep, nuxt-basic, nuxt-deep, angular-basic,
                         # angular-deep, react-basic, react-next-deep, next-basic, svelte-basic, sveltekit-basic,
                         # svelte-deep, node-express, node-deep, ts-deep, cross-modules, security-vulns,
                         # debt-vulns, js-pure, type-errors, callgraph-basic)
```

TDQS

C2.7/5.0

Scored across 103 tools

Disambiguation2/5

Many tools have overlapping boundaries: find_any_usages and find_unsafe_type_casts both flag `as any`, find_event_emitter_leaks and find_event_listener_leaks sound nearly identical, and find_unused_varibles vs. find_dead_code cover adjacent territory. With 103 tools, an agent is likely to mis-select among the numerous find_* detectors despite their individual descriptions.

Naming Consistency4/5

The dominant verb_noun pattern (list_*, find_*, analyze_*, inspect_*) is clear and predictable. A notable minority of noun-first tool names (program_summary, nuxt_config, module_dependency_graph, node_process_env) and framework-specific naming variants prevent a perfect score, but the overall convention is still readable and mostly consistent.

Tool Count1/5

At 103 tools, this server is extremely over-scoped, far beyond the 50+ extreme-mismatch threshold. Even for a broad JS/TS analysis domain, a single MCP server of this size creates impractical selection overhead and should be split into multiple focused servers.

Completeness4/5

The server covers an unusually wide range of read-only static analysis needs: project lifecycle, symbols, types, call graphs, type-checking, framework-specific analysis, API detection, security heuristics, and technical-debt finders. Minor gaps exist (e.g., no general package.json/tsconfig reader, no direct import-list tool), but nothing critical blocks understanding a loaded project.

Maintenance

ActivitySlowing
ResponsivenessNo issues