@webjsdev/mcp 0.1.8 → 0.1.9
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/package.json +4 -1
- package/resources/AGENTS.md +4 -1
- package/src/mcp.js +5 -44
- package/src/routes-report.js +90 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The webjs Model Context Protocol server: live app introspection (routes / actions / components / check) plus the framework knowledge layer (docs, recipes, source) for AI coding agents",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"./check-report": {
|
|
14
14
|
"default": "./src/check-report.js"
|
|
15
15
|
},
|
|
16
|
+
"./routes-report": {
|
|
17
|
+
"default": "./src/routes-report.js"
|
|
18
|
+
},
|
|
16
19
|
"./package.json": "./package.json"
|
|
17
20
|
},
|
|
18
21
|
"files": [
|
package/resources/AGENTS.md
CHANGED
|
@@ -462,9 +462,12 @@ webjs dev [--port N] [--no-hot] # dev server with live reload (node --watch o
|
|
|
462
462
|
webjs start [--port N] # prod server; source IS the runtime, plain HTTP/1.1 (reverse-proxy for TLS + HTTP/2). Runs webjs.start.before first (#550)
|
|
463
463
|
webjs test [--server] [--browser] [--watch]
|
|
464
464
|
webjs check [--rules] [--json] # correctness validator (report-only, no autofix); --json for an agent loop
|
|
465
|
+
webjs routes [--json] [--table] [--no-headers] # print the route table (path / owner file / methods, #975). Default tree; --json is byte-identical to the MCP list_routes tool; --no-headers drops the --table header for piping
|
|
465
466
|
webjs mcp # read-only MCP: routes, actions (RPC hashes), components, check
|
|
466
|
-
webjs doctor
|
|
467
|
+
webjs doctor [--json] [--strict] # project-health checklist (incl. a framework-resolve check that warns when @webjsdev/core can't be resolved from the app dir, the fresh-worktree-without-node_modules trap #954; a page/layout elision advisory); non-zero exit on a hard fail. --json emits `{ results, summary }` (results is the DoctorResult[], each carrying a stable code); --strict also fails the exit on warnings (#975)
|
|
467
468
|
webjs types # generate .webjs/routes.d.ts (typed Route union + per-route params, #258)
|
|
469
|
+
webjs version # print the installed @webjsdev/cli version (also: webjs --version / -v, #975)
|
|
470
|
+
webjs help [command] # full usage banner, or per-command usage + Options + Examples (e.g. webjs help routes, #975). Flag forms: webjs --help / -h (banner), webjs <command> --help / -h (that command). typecheck/db/ui --help forward to their wrapped tool; an unknown topic exits 1
|
|
468
471
|
webjs typecheck [tsc args...] # the project's own tsc --noEmit
|
|
469
472
|
webjs create <name> [--template api|saas]
|
|
470
473
|
webjs db <generate|migrate|push|studio|seed> # wraps drizzle-kit (+ runs db/seed.server.ts)
|
package/src/mcp.js
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
getPrompt,
|
|
34
34
|
} from './mcp-docs.js';
|
|
35
35
|
import { resolveFrameworkRoots, runSourceTool } from './mcp-source.js';
|
|
36
|
+
import { projectRoutes } from './routes-report.js';
|
|
36
37
|
|
|
37
38
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
38
39
|
|
|
@@ -274,22 +275,6 @@ export function extractActionConfig(src) {
|
|
|
274
275
|
};
|
|
275
276
|
}
|
|
276
277
|
|
|
277
|
-
/**
|
|
278
|
-
* The literal URL path for a page/api directory: `blog/[slug]` -> `/blog/[slug]`,
|
|
279
|
-
* the root `.` -> `/`. Route groups `(group)` and `_private` segments drop, the
|
|
280
|
-
* same normalization `buildRouteTable` uses for matching.
|
|
281
|
-
*
|
|
282
|
-
* @param {string} routeDir POSIX-style, `.` for the app root.
|
|
283
|
-
* @returns {string}
|
|
284
|
-
*/
|
|
285
|
-
function routePathFromDir(routeDir) {
|
|
286
|
-
if (!routeDir || routeDir === '.') return '/';
|
|
287
|
-
const segs = routeDir
|
|
288
|
-
.split('/')
|
|
289
|
-
.filter((s) => !(s.startsWith('(') && s.endsWith(')')) && !s.startsWith('_'));
|
|
290
|
-
return segs.length ? '/' + segs.join('/') : '/';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
278
|
/**
|
|
294
279
|
* The tool runners. Each is async, takes `(appDir)`, and returns a plain
|
|
295
280
|
* JSON-serialisable projection of an existing server data function. All are
|
|
@@ -311,34 +296,10 @@ export function makeToolRunners(deps) {
|
|
|
311
296
|
return {
|
|
312
297
|
async list_routes(appDir) {
|
|
313
298
|
const table = await buildRouteTable(appDir);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
path: routePathFromDir(r.routeDir),
|
|
319
|
-
file: relative(appDir, r.file),
|
|
320
|
-
};
|
|
321
|
-
if (r.paramNames && r.paramNames.length) {
|
|
322
|
-
out.dynamic = true;
|
|
323
|
-
out.params = r.paramNames;
|
|
324
|
-
}
|
|
325
|
-
return out;
|
|
326
|
-
}),
|
|
327
|
-
);
|
|
328
|
-
const apis = await Promise.all(
|
|
329
|
-
table.apis.map(async (r) => {
|
|
330
|
-
let methods = [];
|
|
331
|
-
try {
|
|
332
|
-
methods = extractRouteMethods(await readFile(r.file, 'utf8'));
|
|
333
|
-
} catch {}
|
|
334
|
-
return {
|
|
335
|
-
path: routePathFromDir(r.routeDir),
|
|
336
|
-
file: relative(appDir, r.file),
|
|
337
|
-
methods,
|
|
338
|
-
};
|
|
339
|
-
}),
|
|
340
|
-
);
|
|
341
|
-
return { pages, apis };
|
|
299
|
+
// The projection lives in `routes-report.js` so `list_routes` and the CLI
|
|
300
|
+
// `webjs routes --json` stay byte-identical (#975), the same split as
|
|
301
|
+
// `check-report.js` for the `check` tool.
|
|
302
|
+
return projectRoutes(table, { appDir, readFile, extractRouteMethods });
|
|
342
303
|
},
|
|
343
304
|
|
|
344
305
|
async list_actions(appDir) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON projector for the app route table (#975).
|
|
3
|
+
*
|
|
4
|
+
* `webjs routes --json` and the `webjs mcp` server's `list_routes` tool BOTH
|
|
5
|
+
* return the identical shape, so the projection lives here once (the same
|
|
6
|
+
* pattern as `check-report.js` for `check --json` / the MCP `check` tool). The
|
|
7
|
+
* input is the raw `RouteTable` from `buildRouteTable(appDir)` (the ONE route
|
|
8
|
+
* walker, reused, never re-implemented); the output is a stable, agent-friendly
|
|
9
|
+
* `{ pages, apis }` shape.
|
|
10
|
+
*
|
|
11
|
+
* This module is a LEAF: it imports only `node:path` and receives the two
|
|
12
|
+
* effectful dependencies it needs (`readFile` to read a `route.{js,ts}` file's
|
|
13
|
+
* source, `extractRouteMethods` to lexically pull its HTTP-verb exports) as
|
|
14
|
+
* injected arguments, the same dependency-injection style the MCP tool runners
|
|
15
|
+
* already use. That keeps it free of any `@webjsdev/mcp` main-entry import (so
|
|
16
|
+
* the CLI can pull just this projector without loading the MCP server) and free
|
|
17
|
+
* of a cycle with `mcp.js`.
|
|
18
|
+
*
|
|
19
|
+
* @module routes-report
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { relative } from 'node:path';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The literal URL path for a page/api directory: `blog/[slug]` -> `/blog/[slug]`,
|
|
26
|
+
* the root `.` -> `/`. Route groups `(group)` and `_private` segments drop, the
|
|
27
|
+
* same normalization `buildRouteTable` uses for matching.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} routeDir POSIX-style, `.` for the app root.
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function routePathFromDir(routeDir) {
|
|
33
|
+
if (!routeDir || routeDir === '.') return '/';
|
|
34
|
+
const segs = routeDir
|
|
35
|
+
.split('/')
|
|
36
|
+
.filter((s) => !(s.startsWith('(') && s.endsWith(')')) && !s.startsWith('_'));
|
|
37
|
+
return segs.length ? '/' + segs.join('/') : '/';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {{ path: string, file: string, dynamic?: boolean, params?: string[] }} PageRouteReport
|
|
42
|
+
* @typedef {{ path: string, file: string, methods: string[] }} ApiRouteReport
|
|
43
|
+
* @typedef {{ pages: PageRouteReport[], apis: ApiRouteReport[] }} RoutesReport
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Project a `RouteTable` into the structured `{ pages, apis }` report shared by
|
|
48
|
+
* `webjs routes --json` and the MCP `list_routes` tool. Pure over its inputs
|
|
49
|
+
* (given the same table + deps it produces the same output); the only I/O is
|
|
50
|
+
* the injected `readFile` reading each `route.{js,ts}` source to extract its
|
|
51
|
+
* methods. A `route.{js,ts}` that cannot be read degrades to an empty method
|
|
52
|
+
* list rather than throwing, so one unreadable file never sinks the whole
|
|
53
|
+
* report.
|
|
54
|
+
*
|
|
55
|
+
* @param {{ pages: any[], apis: any[] }} table the `buildRouteTable(appDir)` result
|
|
56
|
+
* @param {{
|
|
57
|
+
* appDir: string,
|
|
58
|
+
* readFile: (path: string, enc: string) => Promise<string>,
|
|
59
|
+
* extractRouteMethods: (src: string) => string[],
|
|
60
|
+
* }} deps
|
|
61
|
+
* @returns {Promise<RoutesReport>}
|
|
62
|
+
*/
|
|
63
|
+
export async function projectRoutes(table, { appDir, readFile, extractRouteMethods }) {
|
|
64
|
+
const pages = table.pages.map((r) => {
|
|
65
|
+
/** @type {PageRouteReport} */
|
|
66
|
+
const out = {
|
|
67
|
+
path: routePathFromDir(r.routeDir),
|
|
68
|
+
file: relative(appDir, r.file),
|
|
69
|
+
};
|
|
70
|
+
if (r.paramNames && r.paramNames.length) {
|
|
71
|
+
out.dynamic = true;
|
|
72
|
+
out.params = r.paramNames;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
});
|
|
76
|
+
const apis = await Promise.all(
|
|
77
|
+
table.apis.map(async (r) => {
|
|
78
|
+
let methods = [];
|
|
79
|
+
try {
|
|
80
|
+
methods = extractRouteMethods(await readFile(r.file, 'utf8'));
|
|
81
|
+
} catch {}
|
|
82
|
+
return {
|
|
83
|
+
path: routePathFromDir(r.routeDir),
|
|
84
|
+
file: relative(appDir, r.file),
|
|
85
|
+
methods,
|
|
86
|
+
};
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
return { pages, apis };
|
|
90
|
+
}
|