jscpd integrates into AI-powered development workflows through three complementary mechanisms: the AI reporter, agent skills, and an MCP server.
The ai reporter produces compact, token-efficient output designed to be piped directly into an LLM prompt or agentic pipeline. It uses common-path-prefix compression and omits code fragments and colors — just the clone locations and a summary.
jscpd --reporters ai /path/to/sourcesrc/utils/ auth.ts:10-25 ~ helpers.ts:40-55
src/utils/auth.ts 30-45 ~ 80-95
src/ utils/auth.ts:10-25 ~ api/routes.ts:5-20
---
23 clones · 4.2% duplication
Benchmarked on the fixtures/ directory (212 clones, 347 files):
| Reporter | Output size | Estimated tokens |
|---|---|---|
console (default) |
~21,800 chars | ~5,400 |
ai |
~4,500 chars | ~1,100 |
~79% fewer tokens than the default console reporter.
Add --summary for a compact refactoring-hotspot overview — top files and folders by tokens, lines, size, and a complexity estimate. In the ai reporter each entry is one line with all metrics inline, so an agent gets the full picture for a handful of tokens:
Summary by tokens (321 files, 129 folders):
files (tokens/lines/size/cx/dup%):
src/core/files.ts 2052/363/11.4K/80/0.0%
...
folders (files/tokens/lines/size):
src/core 8/5264/843/26.5K
...
jscpd --reporters ai --summary --no-tips /path/to/sourceSee rust.md for the metric definitions and --summary-top / --summary-by options.
When an agent only needs the complexity ranking, --complexity skips clone detection and prints the same compact rows without the duplication column:
Complexity by complexity (6 files, 3 folders):
files (tokens/lines/size/cx):
src/rates.ts 182/35/757/11
...
folders (files/tokens/lines/size/mean cx):
src 4/557/83/2.2K/5
...
jscpd --reporters ai --complexity --no-tips /path/to/sourceTo hand an agent one kind of clone, combine --reporters ai with --kind, e.g. --ignore-identifiers --kind renamed for copies that differ only in names.
jscpd ships AI agent skills that teach coding assistants how to use jscpd, refactor detected duplications, and clean up a codebase more broadly.
Covers all CLI options, the AI reporter output format, and configuration file syntax.
npx skills add kucherenko/jscpd --skill jscpdA guided process for reading clone output, choosing the right extraction strategy, applying the refactor, and verifying the clone is eliminated.
npx skills add kucherenko/jscpd --skill dry-refactoringA broader pass for "clean up this codebase" requests: fix duplication first (delegates to dry-refactoring), then find and remove or refactor dead code (--dead-code), then find and simplify the largest/most complex files (--complexity) — prioritized from --health/--dashboard and re-measured at the end.
npx skills add kucherenko/jscpd --skill codebase-refactoringExplains how jscpd --compare pairs the functions of two folders, in one language or across two, and what the similarity levels mean. It gives the agent a way to run a comparison: pick the folders, read the overview, check a sample of pairs and every low one, search for the counterparts of unmatched functions before calling them missing, and report the result.
npx skills add kucherenko/jscpd --skill compare-codebasesFor porting a codebase to another language or framework, and for checking two implementations of one app (Android and iOS) for parity. The agent binds each source function to the tests that exercise it with a per-test coverage report, ports the tests first and the code second, and measures both with jscpd --compare SOURCE TARGET. It ports the missing functions in dependency order, turns on each function's ported tests as it lands, checks with coverage that they reach the new code, and records the functions it decided not to port.
npx skills add kucherenko/jscpd --skill code-migrationAfter installation, ask your agent to "find and fix code duplication" for the focused pass, "clean up this codebase" for the broader one, or "port the iOS app to Android" for a migration, and it will invoke jscpd with the right options and act on the results.
jscpd speaks the Model Context Protocol (MCP), exposing detection capabilities as tools that AI assistants can call directly from the editor. Start the server once against your codebase, then let your AI assistant check any snippet for duplication on demand — no CLI invocation needed.
The jscpd/cpd binary serves MCP over stdio itself (jscpd --mcp or cpd --mcp). Most MCP clients start and manage such a server on their own, and stdio needs no port and no network policy. The server scans the project in the background as it starts, so the client connects at once. The first tool call waits for the scan, and a line on stderr says when it is done. Snippet checks compare against the tokens the server keeps in memory and need no new scan.
jscpd --mcp /path/to/project
# The detection options apply to the scan and to snippet checks:
jscpd --mcp --min-tokens 30 --format javascript,typescript /path/to/project
# Semantic clones too by default (the model first: jscpd --semantic-download)
jscpd --mcp --semantic /path/to/projectClient configuration (Claude Desktop, Claude Code, Cursor, APM, ...):
{
"mcpServers": {
"jscpd": {
"command": "cpd",
"args": ["--mcp", "/path/to/project"]
}
}
}The tools find the four types of clone:
| Kind | Type | What it is | How the server finds it |
|---|---|---|---|
exact |
Type-1 | The same tokens; layout and comments may differ | The token passes of the scan |
renamed |
Type-2 | The same code with identifiers or literals changed | The normalization the options configure (--ignore-identifiers, --ignore-literals, --ignore-annotations); with none configured, a second scan that normalizes identifiers and literals |
similar |
Type-3 | A copy with lines added, removed or changed | gap: clones of one file pair merged across up to --max-gap-lines unmatched lines (2 when the option is not set). ast: functions of JavaScript, TypeScript, Python, Java, Kotlin, Scala, C#, Go, Rust, C, C++, PHP, Ruby, Swift and Clojure whose normalized syntax trees share subtrees, at --similarity (0.8 when not set): the names of the functions and methods they call and their operators count, local names and literals do not, and test files are left out |
semantic |
Type-4 | Functions that do the same job, written differently or in another language | The --semantic embedding model |
See fixtures/mcp-demo for a runnable example of each kind and of compare_folders.
Every clone tool takes kinds, a list of kind names. gap and ast name one of the two ways to find similar clones, and type1 to type4 work as names too. Without kinds, a tool reports what jscpd reports with the server's options. By default that means exact clones, plus renamed ones when --ignore-identifiers, --ignore-literals or --ignore-annotations is on, similar ones with --max-gap-lines or --similarity, and semantic ones with --semantic or the config's semantic.enabled. --kind sets other defaults, so jscpd --mcp --kind exact,renamed,similar looks for the first three types in every call.
The server finds a kind the first time a request asks for it and keeps the result until check_current_directory scans again. A request without renamed reads the scan made with the configured options, so its exact clones match a jscpd run with the same options. Ast and semantic clones need the syntax trees and functions of the files, and a scan reads them from the same text as its tokens. The first request for one of these kinds may therefore scan again, and every result describes the same files.
Semantic clones need the embedding model on this machine (jscpd --semantic-download, 548 MB) or an embeddings API (--semantic-url). The server never downloads the model. When it cannot look for semantic clones, the result lists semantic under unavailable with the reason, and the assistant can ask you first. The first semantic request embeds every function of the project, which can take minutes. Later requests reuse the vectors cached for the project, and jscpd --semantic on the same folder uses that cache too.
check_duplication(code, format, kinds?, limit?, similarity?)compares a snippet with the scanned project, so you can reuse existing code instead of writing it again. A match has itskind, the file and lines of the project's copy and of the snippet's, andtokens. Similar and semantic matches addsimilarity, similar ones alsomethod(gaporast), and a match between two functions addsname(the project's) andsnippetName. Exact matches come first, then renamed, similar and semantic ones.similarity, a ratio in(0, 1], sets the threshold of ast matches for the call and asks for them;1finds functions of the same structure only. A request withoutkindsgets its ast matches undersimilarwithsimilarCount, plussimilarNotefor a language without syntax trees, the way earlier versions answered. Withkinds, every match is induplications.formattakes a format name (javascript) or a file extension (js).get_file_clones(path, kinds?, limit?)lists the clones of one file, for refactoring that file.pathis relative to the scan root, as results show paths, or absolute.get_statistics(kinds?)returns the totals and per-format statistics of the last scan for the kinds asked, and the number of clones of each kind (byKind).check_current_directory(kinds?, limit?)scans the configured paths again and returns the new counts and clone list.compare_folders(left, right, limit?)compares two folders function by function, as--comparedoes, in one language or across two. Use it for a port and its original, or for the iOS and Android versions of an app. Comparing two folders says how it differs from--compare.
A tool answers with JSON, sent as structuredContent and again as text for clients of older revisions. Clone lists come biggest first, except the matches of check_duplication, which keep the order above. The optional limit argument caps every list, at 100 entries by default. The count next to a list (clones, count) always gives the full number, and a cut list carries a note. If a tool cannot use its arguments, such as a missing code, an unknown format or kind, or a similarity out of range, the server answers with a tool error (isError: true), a message that says what to fix, so the assistant can try again. An unknown tool gets a JSON-RPC error.
--mcp and --compare do not go together on one command line, and jscpd --mcp --compare A B stops with the argument '--mcp' cannot be used with '--compare'. The server offers the comparison as the compare_folders tool instead, which runs the same code as --compare:
{ "name": "compare_folders", "arguments": { "left": "python", "right": "typescript" } }On fixtures/compare-demo, the tool finds a counterpart for 5 of the 7 Python functions and 4 of the 5 TypeScript ones, as jscpd --compare python typescript does. It differs from the command line in a few ways:
- The tool returns the report of
--compare -r json, with thecodeandtestssections,pairs,unmatchedandreadyToPort, plus the model's name. The console, Markdown and HTML reports, the migration map among them, stay on the command line. - Both folders must lie inside the paths the server scans, and a relative path starts from one of them. To compare two separate projects, start the server with both:
jscpd --mcp ../python-lib ../rust-lib. limitcuts each list of the report to 100 entries by default. The totals stay whole, and anotenames the lists the tool cut.- The tool needs the embedding model or an embeddings API (
--semantic-url). The server never downloads the model, and without it the tool answers with an error that tells the assistant to ask the user first. - The model and its thresholds come from the server's
--semantic-*options and config file, as for--compare, and--semantic-scopehas no effect.--min-tokenssets the size a function needs to count, 30 by default, as with--compare. - The vectors are cached per pair of folders, and
jscpd --compareon the same folders uses the same cache. A local model loads once per server process, so a second comparison does not read the weights again.
The server speaks MCP 2026-07-28, the stateless revision, in which each request names its version in _meta and server/discover describes the server. It also speaks the revisions that open with an initialize handshake (2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05), and one process serves clients of both kinds.
There is no HTTP transport in v5. jscpd-server — MCP over Streamable HTTP plus a REST API, for several clients sharing one long-lived server — is part of jscpd v4 and is maintained on the master-v4 branch (npm install -g jscpd-server).