@knip/mcp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/docs/blog/brief-history.md +30 -0
- package/docs/blog/knip-v3.mdx +88 -0
- package/docs/blog/knip-v4.mdx +149 -0
- package/docs/blog/knip-v5.mdx +190 -0
- package/docs/blog/migration-to-v1.md +65 -0
- package/docs/blog/release-notes-v2.md +46 -0
- package/docs/blog/slim-down-to-speed-up.md +269 -0
- package/docs/blog/state-of-knip.md +191 -0
- package/docs/blog/two-years.mdx +107 -0
- package/docs/docs/blog/brief-history.md +30 -0
- package/docs/docs/blog/for-editors-and-agents.md +109 -0
- package/docs/docs/blog/knip-v3.mdx +88 -0
- package/docs/docs/blog/knip-v4.mdx +149 -0
- package/docs/docs/blog/knip-v5.mdx +190 -0
- package/docs/docs/blog/migration-to-v1.md +65 -0
- package/docs/docs/blog/release-notes-v2.md +46 -0
- package/docs/docs/blog/slim-down-to-speed-up.md +269 -0
- package/docs/docs/blog/state-of-knip.md +191 -0
- package/docs/docs/blog/two-years.mdx +107 -0
- package/docs/docs/explanations/comparison-and-migration.md +129 -0
- package/docs/docs/explanations/entry-files.md +70 -0
- package/docs/docs/explanations/plugins.md +318 -0
- package/docs/docs/explanations/why-use-knip.md +128 -0
- package/docs/docs/features/auto-fix.mdx +333 -0
- package/docs/docs/features/compilers.md +172 -0
- package/docs/docs/features/integrated-monorepos.md +52 -0
- package/docs/docs/features/monorepos-and-workspaces.md +134 -0
- package/docs/docs/features/production-mode.md +95 -0
- package/docs/docs/features/reporters.md +302 -0
- package/docs/docs/features/rules-and-filters.md +102 -0
- package/docs/docs/features/script-parser.md +156 -0
- package/docs/docs/features/source-mapping.md +100 -0
- package/docs/docs/guides/configuring-project-files.md +205 -0
- package/docs/docs/guides/contributing.md +24 -0
- package/docs/docs/guides/handling-issues.mdx +646 -0
- package/docs/docs/guides/issue-reproduction.md +94 -0
- package/docs/docs/guides/namespace-imports.md +125 -0
- package/docs/docs/guides/performance.md +97 -0
- package/docs/docs/guides/troubleshooting.md +127 -0
- package/docs/docs/guides/using-knip-in-ci.md +54 -0
- package/docs/docs/guides/working-with-commonjs.md +72 -0
- package/docs/docs/index.mdx +160 -0
- package/docs/docs/overview/configuration.md +104 -0
- package/docs/docs/overview/features.md +66 -0
- package/docs/docs/overview/getting-started.mdx +195 -0
- package/docs/docs/overview/screenshots-videos.md +42 -0
- package/docs/docs/playground.mdx +38 -0
- package/docs/docs/reference/cli.md +481 -0
- package/docs/docs/reference/configuration.md +413 -0
- package/docs/docs/reference/dynamic-configuration.mdx +72 -0
- package/docs/docs/reference/faq.md +441 -0
- package/docs/docs/reference/issue-types.md +43 -0
- package/docs/docs/reference/jsdoc-tsdoc-tags.md +122 -0
- package/docs/docs/reference/known-issues.md +64 -0
- package/docs/docs/reference/plugins/.gitkeep +0 -0
- package/docs/docs/reference/plugins.md +238 -0
- package/docs/docs/reference/related-tooling.md +46 -0
- package/docs/docs/sponsors.mdx +65 -0
- package/docs/docs/typescript/unused-dependencies.md +86 -0
- package/docs/docs/typescript/unused-exports.md +87 -0
- package/docs/docs/writing-a-plugin/argument-parsing.md +202 -0
- package/docs/docs/writing-a-plugin/index.md +376 -0
- package/docs/docs/writing-a-plugin/inputs.md +162 -0
- package/docs/explanations/comparison-and-migration.md +129 -0
- package/docs/explanations/entry-files.md +70 -0
- package/docs/explanations/plugins.md +318 -0
- package/docs/explanations/why-use-knip.md +128 -0
- package/docs/features/auto-fix.mdx +333 -0
- package/docs/features/compilers.md +172 -0
- package/docs/features/integrated-monorepos.md +52 -0
- package/docs/features/monorepos-and-workspaces.md +134 -0
- package/docs/features/production-mode.md +95 -0
- package/docs/features/reporters.md +302 -0
- package/docs/features/rules-and-filters.md +102 -0
- package/docs/features/script-parser.md +156 -0
- package/docs/features/source-mapping.md +100 -0
- package/docs/guides/configuring-project-files.md +205 -0
- package/docs/guides/contributing.md +24 -0
- package/docs/guides/handling-issues.mdx +646 -0
- package/docs/guides/issue-reproduction.md +94 -0
- package/docs/guides/namespace-imports.md +125 -0
- package/docs/guides/performance.md +97 -0
- package/docs/guides/troubleshooting.md +127 -0
- package/docs/guides/using-knip-in-ci.md +54 -0
- package/docs/guides/working-with-commonjs.md +72 -0
- package/docs/index.mdx +156 -0
- package/docs/overview/configuration.md +104 -0
- package/docs/overview/features.md +66 -0
- package/docs/overview/getting-started.mdx +195 -0
- package/docs/overview/screenshots-videos.md +42 -0
- package/docs/playground.mdx +38 -0
- package/docs/reference/cli.md +481 -0
- package/docs/reference/configuration.md +413 -0
- package/docs/reference/dynamic-configuration.mdx +72 -0
- package/docs/reference/faq.md +441 -0
- package/docs/reference/issue-types.md +43 -0
- package/docs/reference/jsdoc-tsdoc-tags.md +122 -0
- package/docs/reference/known-issues.md +64 -0
- package/docs/reference/plugins/.gitkeep +0 -0
- package/docs/reference/plugins.md +238 -0
- package/docs/reference/related-tooling.md +46 -0
- package/docs/sponsors.mdx +65 -0
- package/docs/typescript/unused-dependencies.md +86 -0
- package/docs/typescript/unused-exports.md +87 -0
- package/docs/writing-a-plugin/argument-parsing.md +202 -0
- package/docs/writing-a-plugin/index.md +376 -0
- package/docs/writing-a-plugin/inputs.md +162 -0
- package/license +15 -0
- package/package.json +38 -0
- package/src/cli.js +13 -0
- package/src/curated-resources.js +62 -0
- package/src/server.js +129 -0
- package/src/texts.js +76 -0
- package/src/tools.js +68 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Inputs
|
|
3
|
+
sidebar:
|
|
4
|
+
order: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You may have noticed functions like `toDeferResolve` and `toEntry`. They're a
|
|
8
|
+
way for plugins to tell what they've found and how Knip should handle those. The
|
|
9
|
+
more precise a plugin can be, the better it is for results and performance.
|
|
10
|
+
Here's an overview of all input type functions:
|
|
11
|
+
|
|
12
|
+
- [toEntry][1]
|
|
13
|
+
- [toProductionEntry][2]
|
|
14
|
+
- [toProject][3]
|
|
15
|
+
- [toDependency][4]
|
|
16
|
+
- [toProductionDependency][5]
|
|
17
|
+
- [toDeferResolve][6]
|
|
18
|
+
- [toDeferResolveEntry][7]
|
|
19
|
+
- [toConfig][8]
|
|
20
|
+
- [toBinary][9]
|
|
21
|
+
- [toAlias][10]
|
|
22
|
+
- [Options][11]
|
|
23
|
+
|
|
24
|
+
## toEntry
|
|
25
|
+
|
|
26
|
+
An `entry` input is just like an `entry` in the configuration. It should either
|
|
27
|
+
be an absolute or relative path, and glob patterns are allowed.
|
|
28
|
+
|
|
29
|
+
## toProductionEntry
|
|
30
|
+
|
|
31
|
+
A production `entry` input is just like an `production` in the configuration. It
|
|
32
|
+
should either be an absolute or relative path, and it can have glob patterns.
|
|
33
|
+
|
|
34
|
+
## toProject
|
|
35
|
+
|
|
36
|
+
A `project` input is the equivalent of `project` patterns in the configuration.
|
|
37
|
+
It should either be an absolute or relative path, and (negated) glob patterns
|
|
38
|
+
are allowed.
|
|
39
|
+
|
|
40
|
+
## toDependency
|
|
41
|
+
|
|
42
|
+
The `dependency` indicates the entry is a dependency, belonging in either the
|
|
43
|
+
`"dependencies"` or `"devDependencies"` section of `package.json`.
|
|
44
|
+
|
|
45
|
+
## toProductionDependency
|
|
46
|
+
|
|
47
|
+
The production `dependency` indicates the entry is a production dependency,
|
|
48
|
+
expected to be listed in `"dependencies"`.
|
|
49
|
+
|
|
50
|
+
## toDeferResolve
|
|
51
|
+
|
|
52
|
+
The `deferResolve` input type is used to defer the resolution of a specifier.
|
|
53
|
+
This could be resolved to a dependency or an entry file. For instance, the
|
|
54
|
+
specifier `"input"` could be resolved to `"input.js"`, `"input.tsx"`,
|
|
55
|
+
`"input/index.js"` or the `"input"` package name. Local files are added as entry
|
|
56
|
+
files, package names are external dependencies.
|
|
57
|
+
|
|
58
|
+
If this does not lead to a resolution, the specifier will be reported under
|
|
59
|
+
"unresolved imports".
|
|
60
|
+
|
|
61
|
+
## toDeferResolveEntry
|
|
62
|
+
|
|
63
|
+
The `deferResolveEntry` input type is similar to `deferResolve`, but it's used
|
|
64
|
+
for entry files only (not dependencies) and unresolved inputs are ignored. It's
|
|
65
|
+
different from `toEntry` as glob patterns are not supported.
|
|
66
|
+
|
|
67
|
+
## toConfig
|
|
68
|
+
|
|
69
|
+
The `config` input type is a way for plugins to reference a configuration file
|
|
70
|
+
that should be handled by a different plugin. For instance, Angular
|
|
71
|
+
configurations might contain references to `tsConfig` and `karmaConfig` files,
|
|
72
|
+
so these `config` files can then be handled by the TypeScript and Karma plugins,
|
|
73
|
+
respectively.
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
toConfig('typescript', './path/to/tsconfig.json');
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For instance, the Angular plugin uses this to tell Knip about its `tsConfig`
|
|
82
|
+
value in `angular.json` projects.
|
|
83
|
+
|
|
84
|
+
## toBinary
|
|
85
|
+
|
|
86
|
+
The `binary` input type isn't used by plugins directly, but by the shell script
|
|
87
|
+
parser (through the `getInputsFromScripts` helper). Think of GitHub Actions
|
|
88
|
+
workflow YAML files or husky scripts. Using this input type, a binary is
|
|
89
|
+
"assigned" to the dependency that has it as a `"bin"` in their `package.json`.
|
|
90
|
+
|
|
91
|
+
## toAlias
|
|
92
|
+
|
|
93
|
+
The `alias` input type adds path aliases to the core module resolver. They're
|
|
94
|
+
added to `compilerOptions.paths` so the syntax is identical.
|
|
95
|
+
|
|
96
|
+
## Options
|
|
97
|
+
|
|
98
|
+
When creating inputs from specifiers, an extra `options` object as the second
|
|
99
|
+
argument can be provided.
|
|
100
|
+
|
|
101
|
+
### dir
|
|
102
|
+
|
|
103
|
+
The optional `dir` option assigns the input to a different workspace. For
|
|
104
|
+
instance, GitHub Action workflows are always stored in the root workspace, and
|
|
105
|
+
support `working-directory` in job steps. For example:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
jobs:
|
|
109
|
+
stylelint:
|
|
110
|
+
runs-on: ubuntu-latest
|
|
111
|
+
steps:
|
|
112
|
+
- run: npx esbuild
|
|
113
|
+
working-directory: packages/app
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The GitHub Action plugin understands `working-directory` and adds this `dir` to
|
|
117
|
+
the input:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
toDependency('esbuild', { dir: 'packages/app' });
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Knip now understands `esbuild` is a dependency of the workspace in the
|
|
124
|
+
`packages/app` directory.
|
|
125
|
+
|
|
126
|
+
### optional
|
|
127
|
+
|
|
128
|
+
Use the `optional` flag to indicate the dependency is optional. Then, a
|
|
129
|
+
dependency won't be flagged as unlisted if it isn't.
|
|
130
|
+
|
|
131
|
+
### allowIncludeExports
|
|
132
|
+
|
|
133
|
+
By default, exports of entry files such as `src/index.ts` or the files in
|
|
134
|
+
`package.json#exports` are not reported as unused. When using the
|
|
135
|
+
`--include-entry-exports` flag or `isIncludeExports: true` option, unused
|
|
136
|
+
exports on such entry files are also reported.
|
|
137
|
+
|
|
138
|
+
Exports of entry files coming from plugins are not included in the analysis,
|
|
139
|
+
even with the option enabled. This is because certain tools and frameworks
|
|
140
|
+
consume named exports from entry files, causing false positives.
|
|
141
|
+
|
|
142
|
+
The `allowIncludeExports` option allows the exports of entry files to be
|
|
143
|
+
reported as unused when using `--include-entry-exports`. This option is
|
|
144
|
+
typically used with the [toProductionEntry][2] input type.
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
toProductionEntry('./entry.ts', { allowIncludeExports: true });
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
[1]: #toentry
|
|
153
|
+
[2]: #toproductionentry
|
|
154
|
+
[3]: #toproject
|
|
155
|
+
[4]: #todependency
|
|
156
|
+
[5]: #toproductiondependency
|
|
157
|
+
[6]: #todeferresolve
|
|
158
|
+
[7]: #todeferresolveentry
|
|
159
|
+
[8]: #toconfig
|
|
160
|
+
[9]: #tobinary
|
|
161
|
+
[10]: #toalias
|
|
162
|
+
[11]: #options
|
package/license
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License (ISC)
|
|
2
|
+
|
|
3
|
+
Copyright 2022-2025 Lars Kappert
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
6
|
+
with or without fee is hereby granted, provided that the above copyright notice
|
|
7
|
+
and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
11
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
13
|
+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
14
|
+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
15
|
+
THIS SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@knip/mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Knip MCP Server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"knip-mcp": "./src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/server.js",
|
|
11
|
+
"./tools": "./src/tools.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"docs"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"knip",
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol"
|
|
21
|
+
],
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/webpro-nl/knip",
|
|
25
|
+
"directory": "packages/mcp-server"
|
|
26
|
+
},
|
|
27
|
+
"author": "Lars Kappert <lars@webpro.nl>",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
31
|
+
"zod": "^4.1.11",
|
|
32
|
+
"knip": "5.75.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.18.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {}
|
|
38
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { mcpServer } from './server.js';
|
|
4
|
+
|
|
5
|
+
function disconnect() {
|
|
6
|
+
mcpServer.close();
|
|
7
|
+
process.exitCode = 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
await mcpServer.connect(new StdioServerTransport());
|
|
11
|
+
|
|
12
|
+
process.on('SIGINT', disconnect);
|
|
13
|
+
process.on('SIGTERM', disconnect);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const CURATED_RESOURCES = {
|
|
2
|
+
'getting-started': {
|
|
3
|
+
name: 'Getting Started',
|
|
4
|
+
description: 'New to Knip? Start here for installation and first run',
|
|
5
|
+
path: 'overview/getting-started.mdx',
|
|
6
|
+
},
|
|
7
|
+
configuration: {
|
|
8
|
+
name: 'Configuration',
|
|
9
|
+
description: 'Understand configuration basics, defaults, and file locations',
|
|
10
|
+
path: 'overview/configuration.md',
|
|
11
|
+
},
|
|
12
|
+
'configuring-project-files': {
|
|
13
|
+
name: 'Configuring Project Files',
|
|
14
|
+
description: 'READ FIRST for unused files or false positives. Covers entry/project patterns',
|
|
15
|
+
path: 'guides/configuring-project-files.md',
|
|
16
|
+
},
|
|
17
|
+
'handling-issues': {
|
|
18
|
+
name: 'Handling Issues',
|
|
19
|
+
description: 'How to handle each issue type: files, dependencies, exports, types, duplicates',
|
|
20
|
+
path: 'guides/handling-issues.mdx',
|
|
21
|
+
},
|
|
22
|
+
'monorepos-and-workspaces': {
|
|
23
|
+
name: 'Monorepos & Workspaces',
|
|
24
|
+
description: 'Multi-package repo? Configure workspaces and cross-references here',
|
|
25
|
+
path: 'features/monorepos-and-workspaces.md',
|
|
26
|
+
},
|
|
27
|
+
'production-mode': {
|
|
28
|
+
name: 'Production Mode',
|
|
29
|
+
description: 'Exclude tests, stories, devDependencies with --production and --strict flags',
|
|
30
|
+
path: 'features/production-mode.md',
|
|
31
|
+
},
|
|
32
|
+
compilers: {
|
|
33
|
+
name: 'Compilers',
|
|
34
|
+
description: 'Using .vue, .svelte, .astro, .mdx files? Configure compilers to parse them',
|
|
35
|
+
path: 'features/compilers.md',
|
|
36
|
+
},
|
|
37
|
+
'configuration-reference': {
|
|
38
|
+
name: 'Configuration Reference',
|
|
39
|
+
description: 'Complete reference of all config options: entry, project, ignore, plugins, etc.',
|
|
40
|
+
path: 'reference/configuration.md',
|
|
41
|
+
},
|
|
42
|
+
'plugins-explanation': {
|
|
43
|
+
name: 'Plugins',
|
|
44
|
+
description: 'Config files showing as unused? Understand plugin config vs entry files',
|
|
45
|
+
path: 'explanations/plugins.md',
|
|
46
|
+
},
|
|
47
|
+
'entry-files': {
|
|
48
|
+
name: 'Entry Files',
|
|
49
|
+
description: 'Understand how Knip discovers entry files and default patterns per plugin',
|
|
50
|
+
path: 'explanations/entry-files.md',
|
|
51
|
+
},
|
|
52
|
+
'plugin-list': {
|
|
53
|
+
name: 'Plugin List',
|
|
54
|
+
description: 'Check if a plugin exists for your tool (Jest, Vitest, ESLint, etc.)',
|
|
55
|
+
path: 'reference/plugins.md',
|
|
56
|
+
},
|
|
57
|
+
'known-issues': {
|
|
58
|
+
name: 'Known Issues',
|
|
59
|
+
description: 'Errors or unexpected behavior? Check workarounds for common problems',
|
|
60
|
+
path: 'reference/known-issues.md',
|
|
61
|
+
},
|
|
62
|
+
};
|
package/src/server.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
process.env.NO_COLOR = '1';
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { CURATED_RESOURCES } from './curated-resources.js';
|
|
9
|
+
import {
|
|
10
|
+
DOC_TOOL_DESCRIPTION,
|
|
11
|
+
DOC_TOOL_TOPIC_DESCRIPTION,
|
|
12
|
+
ERROR_HINT,
|
|
13
|
+
RUN_KNIP_TOOL_DESCRIPTION,
|
|
14
|
+
WORKFLOW,
|
|
15
|
+
} from './texts.js';
|
|
16
|
+
import { getDocs, readContent } from './tools.js';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
20
|
+
|
|
21
|
+
const DOCS = 'knip://docs';
|
|
22
|
+
|
|
23
|
+
class MCP {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.server = new McpServer({ name: 'Knip', version: pkg.version });
|
|
26
|
+
this.#registerPrompts();
|
|
27
|
+
this.#registerResources();
|
|
28
|
+
this.#registerTools();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#registerPrompts() {
|
|
32
|
+
const resources = Object.entries(CURATED_RESOURCES).map(([id, doc]) => ({
|
|
33
|
+
uri: `${DOCS}/${id}`,
|
|
34
|
+
name: doc.name,
|
|
35
|
+
description: doc.description,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
this.server.registerPrompt(
|
|
39
|
+
'knip-configure',
|
|
40
|
+
{
|
|
41
|
+
description: 'Set up and optimize Knip configuration. Guides through initial setup and iterative refinement.',
|
|
42
|
+
arguments: [{ name: 'cwd', description: 'Working directory (default: current directory)', required: false }],
|
|
43
|
+
},
|
|
44
|
+
async ({ cwd }) => ({
|
|
45
|
+
messages: [
|
|
46
|
+
{
|
|
47
|
+
role: 'user',
|
|
48
|
+
content: {
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: `Help me configure Knip for ${cwd || 'this project'}.\n\n${WORKFLOW}`,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
resources,
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#registerResources() {
|
|
60
|
+
for (const [id, doc] of Object.entries(CURATED_RESOURCES)) {
|
|
61
|
+
const uri = `${DOCS}/${id}`;
|
|
62
|
+
this.server.registerResource(
|
|
63
|
+
doc.name,
|
|
64
|
+
uri,
|
|
65
|
+
{ description: doc.description, mimeType: 'text/markdown' },
|
|
66
|
+
async () => ({ contents: [{ uri, mimeType: 'text/markdown', text: readContent(doc.path) }] })
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.server.registerResource(
|
|
71
|
+
'docs',
|
|
72
|
+
new ResourceTemplate(`${DOCS}/{+path}`, { list: undefined }),
|
|
73
|
+
{ description: 'Get Knip documentation page by path', mimeType: 'text/markdown' },
|
|
74
|
+
async (uri, { path }) => {
|
|
75
|
+
const result = getDocs(path);
|
|
76
|
+
if ('content' in result) return { content: [{ type: 'text', text: result.content }] };
|
|
77
|
+
const errorText = `Documentation not found: ${path}`;
|
|
78
|
+
return { contents: [{ uri, mimeType: 'text/plain', text: errorText }] };
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#registerTools() {
|
|
84
|
+
this.server.registerTool(
|
|
85
|
+
'knip-run',
|
|
86
|
+
{
|
|
87
|
+
description: RUN_KNIP_TOOL_DESCRIPTION,
|
|
88
|
+
inputSchema: {
|
|
89
|
+
cwd: z.string().optional().describe('Working directory (default: workspace root)'),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
async opts => {
|
|
93
|
+
try {
|
|
94
|
+
const cwd = opts.cwd || process.cwd();
|
|
95
|
+
const results = await getResults(cwd);
|
|
96
|
+
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: 'text', text: JSON.stringify({ error: message, hint: ERROR_HINT }) }],
|
|
101
|
+
isError: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
this.server.registerTool(
|
|
108
|
+
'knip-docs',
|
|
109
|
+
{ description: DOC_TOOL_DESCRIPTION, inputSchema: { topic: z.string().describe(DOC_TOOL_TOPIC_DESCRIPTION) } },
|
|
110
|
+
async ({ topic }) => {
|
|
111
|
+
const docs = getDocs(topic);
|
|
112
|
+
if ('content' in docs) return { content: [{ type: 'text', text: docs.content }] };
|
|
113
|
+
return { content: [{ type: 'text', text: docs.error }], isError: true };
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
connect(transport) {
|
|
119
|
+
return this.server.connect(transport);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
close() {
|
|
123
|
+
return this.server.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const mcpServer = new MCP();
|
|
128
|
+
|
|
129
|
+
export { mcpServer };
|
package/src/texts.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CURATED_RESOURCES } from './curated-resources.js';
|
|
2
|
+
|
|
3
|
+
export const WORKFLOW = `Workflow:
|
|
4
|
+
1. Read essential documentation resources to understand Knip configuration
|
|
5
|
+
2. Run analysis (knip-run) to get configuration hints and issues
|
|
6
|
+
3. Address the hints by adjusting knip.json
|
|
7
|
+
4. Repeat steps 2-3 until no hints remain and false positives are minimized
|
|
8
|
+
|
|
9
|
+
Essential resources:
|
|
10
|
+
- configuring-project-files (must read to configure entry patterns)
|
|
11
|
+
- handling-issues (comprehensive guide to deal with any reported issue type)
|
|
12
|
+
- configuration-reference (all knip.json configuration options)
|
|
13
|
+
|
|
14
|
+
For direct "run knip" or "clean up codebase" requests: Always make sure to run this workflow first.
|
|
15
|
+
|
|
16
|
+
Important (potential next steps after workflow is finished):
|
|
17
|
+
- Before suggesting fixes/solutions, make sure to consult "handling-issues" and "reference/jsdoc-tsdoc-tags"
|
|
18
|
+
- Read "getting-started" and "reference/cli" to install knip and start using Knip from CLI
|
|
19
|
+
- If requested to clean up, consult "features/auto-fix"
|
|
20
|
+
- Knip does not find/fix unused variables/imports within files (use another linter for that)
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
// pkg.contributes.languageModelTools[0].modelDescription
|
|
24
|
+
export const RUN_KNIP_TOOL_DESCRIPTION = `Run Knip and return configuration hints and issues.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
- configurationHints: Ordered suggestions to improve configuration (address these first)
|
|
28
|
+
- counters: Summary counts of each issue type
|
|
29
|
+
- files: List of unused files
|
|
30
|
+
- issues: Detailed issues by type (dependencies, exports, types, etc.)
|
|
31
|
+
- configFile: Current config file status
|
|
32
|
+
|
|
33
|
+
Iterate: adjust knip.json based on hints, run again until no hints remain and false positives are minimized.`;
|
|
34
|
+
|
|
35
|
+
// pkg.contributes.languageModelTools[1].modelDescription
|
|
36
|
+
export const DOC_TOOL_DESCRIPTION = `Get Knip documentation by topic.
|
|
37
|
+
|
|
38
|
+
If registered resources are unavailable, use this tool.
|
|
39
|
+
|
|
40
|
+
Available topics (use these IDs):
|
|
41
|
+
${Object.entries(CURATED_RESOURCES)
|
|
42
|
+
.map(([id, doc]) => `- ${id}: ${doc.description}`)
|
|
43
|
+
.join('\n')}
|
|
44
|
+
|
|
45
|
+
Can also fetch any doc by path (e.g., "reference/cli" or "guides/troubleshooting").
|
|
46
|
+
Use this instead of fetching from knip.dev.`;
|
|
47
|
+
|
|
48
|
+
// pkg.contributes.languageModelTools[1].inputSchema.properties.topic.description
|
|
49
|
+
export const DOC_TOOL_TOPIC_DESCRIPTION =
|
|
50
|
+
'Topic key (e.g. "configuring-project-files") for curated resources, or path (e.g. "explanations/plugins") for all available docs';
|
|
51
|
+
|
|
52
|
+
export const ERROR_HINT = `For unexpected errors (exit code 2) such as "error loading file":
|
|
53
|
+
- Consult docs: known-issues and configuration-reference
|
|
54
|
+
- If no config file exists, create knip.json in root: {"$schema":"https://unpkg.com/knip@5/schema.json"}
|
|
55
|
+
- Damage control for "error loading file":
|
|
56
|
+
1) First try to disable the related plugin's config file:
|
|
57
|
+
- E.g. vite: { config: [] }
|
|
58
|
+
- In a monorepo use e.g. { workspaces: { "packages/lib": { vite: { config: [] } } } }
|
|
59
|
+
- If this succeeds, add the file as a regular entry point
|
|
60
|
+
2) Then try to disable the related plugin:
|
|
61
|
+
- E.g. vite: false
|
|
62
|
+
- In a monorepo use e.g. { workspaces: { "packages/lib": { vite: false } } }
|
|
63
|
+
- If this succeeds, add the file as a regular entry point
|
|
64
|
+
3) As a last resort, ignore the workspace: ignoreWorkspaces: ["packages/lib"]
|
|
65
|
+
- Run knip again`;
|
|
66
|
+
|
|
67
|
+
export const CONFIG_REVIEW_HINT = `Review the existing configuration for potential improvements:
|
|
68
|
+
|
|
69
|
+
- Never use "ignore" patterns (hides real issues!), always prefer specific solutions, other ignore* options are allowed
|
|
70
|
+
- Many unused exported types? Add: ignoreExportsUsedInFile: { interface: true, type: true } (prefer this over other ignore* options)
|
|
71
|
+
- Remove ignore patterns that don't match any files
|
|
72
|
+
- Redundant ignore patterns: Knip respects .gitignore by default (node_modules, dist, build, .git)
|
|
73
|
+
- Remove entry patterns covered by config defaults and plugins
|
|
74
|
+
- Config files (e.g. vite.config.ts) showing as unused? Enable/disable the plugin explicitly
|
|
75
|
+
- Dependencies matching Node.js builtins: add to ignoreDependencies (e.g. buffer, process)
|
|
76
|
+
- Unresolved imports from path aliases: add paths to Knip config (tsconfig.json semantics)`;
|
package/src/tools.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { createOptions, createSession, finalizeConfigurationHints, KNIP_CONFIG_LOCATIONS } from 'knip/session';
|
|
5
|
+
import { CURATED_RESOURCES } from './curated-resources.js';
|
|
6
|
+
import { CONFIG_REVIEW_HINT } from './texts.js';
|
|
7
|
+
|
|
8
|
+
export { ERROR_HINT } from './texts.js';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const docsDir = join(__dirname, '../docs');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('knip/session').Results} results
|
|
15
|
+
* @param {{ cwd: string, configFilePath: string | undefined }} options
|
|
16
|
+
*/
|
|
17
|
+
export function buildResults(results, options) {
|
|
18
|
+
return {
|
|
19
|
+
configFile: options.configFilePath
|
|
20
|
+
? { exists: true, filePath: options.configFilePath, reviewHint: CONFIG_REVIEW_HINT }
|
|
21
|
+
: { exists: false, locations: KNIP_CONFIG_LOCATIONS },
|
|
22
|
+
configurationHints: finalizeConfigurationHints(results, options),
|
|
23
|
+
counters: results.counters,
|
|
24
|
+
files: Array.from(results.issues.files),
|
|
25
|
+
issues: Object.fromEntries(Object.entries(results.issues).filter(([key]) => key !== 'files' && key !== '_files')),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} cwd
|
|
31
|
+
*/
|
|
32
|
+
export async function getResults(cwd) {
|
|
33
|
+
const options = await createOptions({ cwd, isSession: true, isUseTscFiles: false });
|
|
34
|
+
const session = await createSession(options);
|
|
35
|
+
return buildResults(session.getResults(), options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** @param {string} filePath */
|
|
39
|
+
export function readContent(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
const content = readFileSync(join(docsDir, filePath), 'utf-8');
|
|
42
|
+
return content.replace(/^---[\s\S]*?---\n/, '');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return `Error reading ${filePath}: ${error.message}`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} topic
|
|
50
|
+
* @returns {{ content: string } | { error: string }}
|
|
51
|
+
*/
|
|
52
|
+
export function getDocs(topic) {
|
|
53
|
+
const content = findDocPage(topic);
|
|
54
|
+
if (content) return { content };
|
|
55
|
+
return { error: `Documentation not found: ${topic}. Available: ${Object.keys(CURATED_RESOURCES).join(', ')}` };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** @param {string} topic */
|
|
59
|
+
function findDocPage(topic) {
|
|
60
|
+
if (CURATED_RESOURCES[topic]) return readContent(CURATED_RESOURCES[topic].path);
|
|
61
|
+
|
|
62
|
+
for (const ext of ['.md', '.mdx']) {
|
|
63
|
+
const filePath = join(docsDir, `${topic}${ext}`);
|
|
64
|
+
if (existsSync(filePath)) return readContent(`${topic}${ext}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|