Skip to content

Commit 1cfaacf

Browse files
authored
feat: add new tools for routes, appdata and changelog (#7)
1 parent 04536ac commit 1cfaacf

File tree

7 files changed

+170
-12
lines changed

7 files changed

+170
-12
lines changed

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,32 @@ MCP collection for the [umi](https://github.com/umijs/umi) framework.
2828
- `umi-setup`, Setup the umi project and generate the tmp files in the `.umi` directory.
2929
- `umi-deadcode`, Find the dead code of the umi project.
3030
- `umi-version`, Show the version of the umi project.
31-
- [ ] `umi-route-list`, List all routes of the umi project. (Based on the appData under `.umi` directory)
32-
- [ ] `umi-appdata-list`, List detailed app data of the umi project. (Based on the appData under `.umi` directory)
31+
- `umi-route-list`, List all routes of the umi project. (Based on the appData under `.umi` directory)
32+
- `umi-appdata-list`, List detailed app data of the umi project. (Based on the appData under `.umi` directory)
33+
- `umi-changelog`, Show the changelog of the umi and @umijs/max.
3334

3435
## Usage
3536

3637
```bash
3738
$ npx -y umi-mcp <root>
3839
```
40+
### Usage with Cursor
41+
add the following to your `<root>/.cursor/mcp.json`:
42+
#### NPX
43+
```json
44+
{
45+
"mcpServers": {
46+
"umi-mcp": {
47+
"command": "npx",
48+
"args": [
49+
"-y",
50+
"umi-mcp",
51+
"<root>"
52+
]
53+
}
54+
}
55+
}
56+
```
3957

4058
## CONTRIBUTING
4159

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import { umiAppdataList } from './tools/umi-appdata-list';
12
import { umiBuild } from './tools/umi-build';
3+
import { umiChangelog } from './tools/umi-changelog';
24
import { umiConfig } from './tools/umi-config';
35
import { umiGenerate } from './tools/umi-generate';
46
import { umiHelp } from './tools/umi-help';
57
import { umiLint } from './tools/umi-lint';
68
import { umiPlugin } from './tools/umi-plugin';
9+
import { umiRouteList } from './tools/umi-route-list';
710
import { umiSetup } from './tools/umi-setup';
811
import { umiVersion } from './tools/umi-version';
912
import { ToolContext } from './types';
@@ -19,4 +22,7 @@ export function registerTools(toolContext: ToolContext) {
1922
umiPlugin(toolContext);
2023
umiGenerate(toolContext);
2124
umiLint(toolContext);
25+
umiRouteList(toolContext);
26+
umiAppdataList(toolContext);
27+
umiChangelog(toolContext);
2228
}

src/path.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { existsSync, statSync } from 'fs';
2+
import { join } from 'path';
3+
4+
export function winPath(path: string) {
5+
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
6+
if (isExtendedLengthPath) {
7+
return path;
8+
}
9+
return path.replace(/\\/g, '/');
10+
}
11+
12+
function winJoin(...args: string[]) {
13+
return winPath(join(...args));
14+
}
15+
16+
export function getPaths(cwd: string, frameworkName: string) {
17+
const src = winJoin(cwd, 'src');
18+
const absSrcPath = existsSync(src) && statSync(src).isDirectory() ? src : cwd;
19+
const absTmpPath = winJoin(absSrcPath, `.${frameworkName}`);
20+
return {
21+
absSrcPath,
22+
absTmpPath,
23+
};
24+
}

src/tools/umi-appdata-list.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import assert from 'assert';
2+
import { execSync } from 'child_process';
3+
import { existsSync, readFileSync } from 'fs';
4+
import { z } from 'zod';
5+
import { parse } from '../parse';
6+
import { getPaths } from '../path';
7+
import { ToolContext } from '../types';
8+
9+
export const umiAppdataList = async ({
10+
server,
11+
root,
12+
frameworkName,
13+
}: ToolContext) => {
14+
server.addTool({
15+
name: `${frameworkName}-appdata-list`,
16+
description: `List the appData of the ${frameworkName} project.`,
17+
parameters: z.object({}),
18+
execute: async () => {
19+
const { absTmpPath } = getPaths(root, frameworkName);
20+
21+
if (!existsSync(absTmpPath)) {
22+
const { binPath } = parse(root);
23+
execSync(`${binPath} setup`, { cwd: root });
24+
}
25+
26+
const appDataPath = `${absTmpPath}/appData.json`;
27+
assert(
28+
existsSync(appDataPath),
29+
`${appDataPath} is not exist, please upgrade to the latest version of ${frameworkName}`,
30+
);
31+
const appDataJson = JSON.parse(readFileSync(appDataPath, 'utf-8'));
32+
return {
33+
type: 'text',
34+
text: JSON.stringify(appDataJson, null, 2),
35+
};
36+
},
37+
});
38+
};

src/tools/umi-build.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const umiBuild = async ({
1010
}: ToolContext) => {
1111
const BuildParams = z.object({
1212
ANALYZE: z
13-
.union([z.literal(1), z.literal(0)])
13+
.literal(1)
1414
.optional()
1515
.describe('Analyze the bundle composition, disabled by default.'),
1616
ANALYZE_PORT: z.number().optional().describe('Custom port'),
@@ -37,16 +37,25 @@ export const umiBuild = async ({
3737
description: `Build the ${frameworkName} project.`,
3838
parameters: BuildParams,
3939
execute: async (params) => {
40-
const { binPath } = parse(root);
41-
const env = { ...process.env };
42-
Object.entries(params).forEach(([key, value]) => {
43-
if (value !== undefined) {
44-
env[key] = typeof value === 'number' ? value.toString() : value;
40+
try {
41+
const { binPath } = parse(root);
42+
const env = { ...process.env };
43+
for (const [key, value] of Object.entries(params)) {
44+
if (value !== undefined && (value === 1 || value === 'none')) {
45+
env[key] = typeof value === 'number' ? value.toString() : value;
46+
}
4547
}
46-
});
47-
48-
const result = execSync(`${binPath} build`, { env, cwd: root });
49-
return result.toString();
48+
const result = execSync(`${binPath} build`, {
49+
env,
50+
cwd: root,
51+
timeout: 5 * 60000,
52+
});
53+
return result.toString();
54+
} catch (error) {
55+
throw new Error(
56+
`Build failed. Please check the error message above.\n${error}`,
57+
);
58+
}
5059
},
5160
});
5261
};

src/tools/umi-changelog.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { z } from 'zod';
2+
import { ToolContext } from '../types';
3+
4+
export const umiChangelog = async ({ server }: ToolContext) => {
5+
server.addTool({
6+
name: 'umi-changelog',
7+
description: 'Show the changelog of the umi and @umijs/max.',
8+
parameters: z.object({
9+
version: z
10+
.string()
11+
.describe('Get the changelog for the current version,eg: 4.4.6'),
12+
}),
13+
execute: async ({ version }) => {
14+
const result = await fetch(
15+
`https://api.github.com/repos/umijs/umi/releases/tags/v${version}`,
16+
);
17+
if (!result.ok) {
18+
throw new Error(`Failed to fetch changelog: ${result.statusText}`);
19+
}
20+
const { body } = await result.json();
21+
return body;
22+
},
23+
});
24+
};

src/tools/umi-route-list.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import assert from 'assert';
2+
import { execSync } from 'child_process';
3+
import { existsSync, readFileSync } from 'fs';
4+
import { z } from 'zod';
5+
import { parse } from '../parse';
6+
import { getPaths } from '../path';
7+
import { ToolContext } from '../types';
8+
9+
export const umiRouteList = async ({
10+
server,
11+
root,
12+
frameworkName,
13+
}: ToolContext) => {
14+
server.addTool({
15+
name: `${frameworkName}-route-list`,
16+
description: `List the routes of the ${frameworkName} project.`,
17+
parameters: z.object({}),
18+
execute: async () => {
19+
const { absTmpPath } = getPaths(root, frameworkName);
20+
21+
if (!existsSync(absTmpPath)) {
22+
const { binPath } = parse(root);
23+
execSync(`${binPath} setup`, { cwd: root });
24+
}
25+
26+
const appDataPath = `${absTmpPath}/appData.json`;
27+
assert(
28+
existsSync(appDataPath),
29+
`${appDataPath} is not exist, please upgrade to the latest version of ${frameworkName}`,
30+
);
31+
const appDataJson = JSON.parse(readFileSync(appDataPath, 'utf-8'));
32+
const routes = appDataJson.defaultConfig?.routes || [];
33+
return {
34+
type: 'text',
35+
text: JSON.stringify(routes, null, 2),
36+
};
37+
},
38+
});
39+
};

0 commit comments

Comments
 (0)