@webjsdev/cli 0.10.11 → 0.10.12
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/lib/mcp-docs.js +400 -0
- package/lib/mcp-source.js +244 -0
- package/lib/mcp.js +167 -18
- package/package.json +7 -2
- package/resources/AGENTS.md +404 -0
- package/resources/agent-docs/advanced.md +1090 -0
- package/resources/agent-docs/built-ins.md +367 -0
- package/resources/agent-docs/components.md +486 -0
- package/resources/agent-docs/configuration.md +207 -0
- package/resources/agent-docs/framework-dev.md +65 -0
- package/resources/agent-docs/lit-muscle-memory-gotchas.md +456 -0
- package/resources/agent-docs/metadata.md +334 -0
- package/resources/agent-docs/recipes.md +440 -0
- package/resources/agent-docs/service-worker.md +100 -0
- package/resources/agent-docs/ssr-partial-nav-design.md +214 -0
- package/resources/agent-docs/styling.md +235 -0
- package/resources/agent-docs/testing.md +372 -0
- package/resources/agent-docs/typescript.md +334 -0
package/lib/mcp.js
CHANGED
|
@@ -23,6 +23,17 @@
|
|
|
23
23
|
import { createInterface } from 'node:readline';
|
|
24
24
|
import { relative } from 'node:path';
|
|
25
25
|
|
|
26
|
+
import {
|
|
27
|
+
resolveDocsLocation,
|
|
28
|
+
listResources,
|
|
29
|
+
readResource,
|
|
30
|
+
initText,
|
|
31
|
+
searchDocs,
|
|
32
|
+
PROMPTS,
|
|
33
|
+
getPrompt,
|
|
34
|
+
} from './mcp-docs.js';
|
|
35
|
+
import { resolveFrameworkRoots, runSourceTool } from './mcp-source.js';
|
|
36
|
+
|
|
26
37
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -31,41 +42,109 @@ const PROTOCOL_VERSION = '2024-11-05';
|
|
|
31
42
|
* into an agent-friendly shape. Descriptions are crisp so a model picks the
|
|
32
43
|
* right tool without reading source.
|
|
33
44
|
*/
|
|
45
|
+
/** The shared input schema for the introspection tools: an optional appDir override. */
|
|
46
|
+
const APPDIR_SCHEMA = {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
appDir: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'App directory to introspect. Defaults to the server cwd.',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: [],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** `init` takes no input. */
|
|
58
|
+
const INIT_SCHEMA = { type: 'object', properties: {}, required: [] };
|
|
59
|
+
|
|
60
|
+
/** `docs` takes an optional topic OR a free-text query. */
|
|
61
|
+
const DOCS_SCHEMA = {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
topic: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'A doc name (e.g. components, recipes, lit-muscle-memory-gotchas, AGENTS). Returns the full doc.',
|
|
67
|
+
},
|
|
68
|
+
query: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'Free-text search across all webjs docs. Returns matching lines with their source.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: [],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** `source` reads the framework source: a `path` to read, a `query` to grep, or a `package` to list. */
|
|
77
|
+
const SOURCE_SCHEMA = {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
path: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'A framework source file to read, e.g. server/src/ssr.js or @webjsdev/core/src/render-client.js.',
|
|
83
|
+
},
|
|
84
|
+
query: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'Grep the @webjsdev/* src trees for this substring. Returns file:line hits.',
|
|
87
|
+
},
|
|
88
|
+
package: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Limit a no-args listing to one package (core, server, cli, ts-plugin, ui).',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: [],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The tools. The four introspection tools project an EXISTING @webjsdev/server
|
|
98
|
+
* function (read-only, appDir-scoped). `init` + `docs` (#376) surface the
|
|
99
|
+
* framework knowledge: `init` is the "read first" mental-model primer, `docs`
|
|
100
|
+
* retrieves a doc by topic or searches the corpus. Descriptions are crisp so a
|
|
101
|
+
* model picks the right tool without reading source.
|
|
102
|
+
*/
|
|
34
103
|
const TOOL_DEFS = [
|
|
104
|
+
{
|
|
105
|
+
name: 'init',
|
|
106
|
+
description:
|
|
107
|
+
'READ THIS FIRST before writing or editing a webjs app. Returns the webjs mental model (NOT React/Next: no RSC, components hydrate but pages do not, signals-default state, the .server boundary) plus the invariants and the doc index. Read-only.',
|
|
108
|
+
inputSchema: INIT_SCHEMA,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'docs',
|
|
112
|
+
description:
|
|
113
|
+
'Retrieve webjs framework docs: pass `topic` for a full doc (components, recipes, styling, built-ins, configuration, advanced, metadata, typescript, testing, lit-muscle-memory-gotchas, AGENTS, ...) or `query` to search the corpus. No args returns the topic index. Read-only.',
|
|
114
|
+
inputSchema: DOCS_SCHEMA,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'source',
|
|
118
|
+
description:
|
|
119
|
+
'Read the FRAMEWORK authored source (webjs is buildless: node_modules/@webjsdev/*/src is the JSDoc source, run directly server-side; only the core browser bundle is built into dist/, which this skips). Pass `path` to read a file (e.g. server/src/ssr.js), `query` to grep the @webjsdev/* src trees, or no args to list the packages + entry points. Use when the docs do not answer something. Read-only.',
|
|
120
|
+
inputSchema: SOURCE_SCHEMA,
|
|
121
|
+
},
|
|
35
122
|
{
|
|
36
123
|
name: 'list_routes',
|
|
37
124
|
description:
|
|
38
125
|
'List the app route table: SSR pages (path, file, dynamic flag, param names) and route.{js,ts} API handlers (path, file, HTTP methods). Read-only.',
|
|
126
|
+
inputSchema: APPDIR_SCHEMA,
|
|
39
127
|
},
|
|
40
128
|
{
|
|
41
129
|
name: 'list_actions',
|
|
42
130
|
description:
|
|
43
131
|
'List registered server actions (the .server.{js,ts} files with "use server"): file, exported function name, and the /__webjs/action/<hash>/<fn> RPC endpoint. Read-only.',
|
|
132
|
+
inputSchema: APPDIR_SCHEMA,
|
|
44
133
|
},
|
|
45
134
|
{
|
|
46
135
|
name: 'list_components',
|
|
47
136
|
description:
|
|
48
137
|
'List registered custom-element tags: tag name, defining file, and class name. Read-only.',
|
|
138
|
+
inputSchema: APPDIR_SCHEMA,
|
|
49
139
|
},
|
|
50
140
|
{
|
|
51
141
|
name: 'check',
|
|
52
142
|
description:
|
|
53
143
|
'Run webjs check (correctness rules) and return the structured violations { rule, file, message, fix } plus a summary count and per-rule breakdown. Read-only.',
|
|
144
|
+
inputSchema: APPDIR_SCHEMA,
|
|
54
145
|
},
|
|
55
146
|
];
|
|
56
147
|
|
|
57
|
-
/** The shared input schema: every tool takes an optional appDir override. */
|
|
58
|
-
const TOOL_INPUT_SCHEMA = {
|
|
59
|
-
type: 'object',
|
|
60
|
-
properties: {
|
|
61
|
-
appDir: {
|
|
62
|
-
type: 'string',
|
|
63
|
-
description: 'App directory to introspect. Defaults to the server cwd.',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
required: [],
|
|
67
|
-
};
|
|
68
|
-
|
|
69
148
|
/**
|
|
70
149
|
* Lexically extract the names exported from a module source. Recognises the
|
|
71
150
|
* common forms a server-action / route file uses without LOADING the module
|
|
@@ -302,6 +381,39 @@ export async function runMcpServer(opts) {
|
|
|
302
381
|
}
|
|
303
382
|
const runners = makeToolRunners(deps);
|
|
304
383
|
|
|
384
|
+
// The docs corpus deps for the knowledge layer (#376): resources / prompts /
|
|
385
|
+
// init / docs. Injectable for tests; otherwise resolved from the bundled
|
|
386
|
+
// (published) or repo-root (dev) docs and node fs.
|
|
387
|
+
let docsDeps = opts.docsDeps;
|
|
388
|
+
if (!docsDeps) {
|
|
389
|
+
const loc = resolveDocsLocation(import.meta.url);
|
|
390
|
+
const { readFile } = await import('node:fs/promises');
|
|
391
|
+
const { readdirSync, existsSync } = await import('node:fs');
|
|
392
|
+
docsDeps = {
|
|
393
|
+
docsDir: loc.docsDir,
|
|
394
|
+
agentsPath: loc.agentsPath,
|
|
395
|
+
listDir: readdirSync,
|
|
396
|
+
exists: existsSync,
|
|
397
|
+
readFile,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// The `source` tool (#378): read the framework's own source from
|
|
402
|
+
// node_modules/@webjsdev/*/src (no-build, so it is the real JSDoc). Roots are
|
|
403
|
+
// resolved once from the server cwd. Injectable for tests.
|
|
404
|
+
let sourceDeps = opts.sourceDeps;
|
|
405
|
+
if (!sourceDeps) {
|
|
406
|
+
const { readFile } = await import('node:fs/promises');
|
|
407
|
+
const { readdirSync, existsSync, realpathSync } = await import('node:fs');
|
|
408
|
+
const readdir = (d) => readdirSync(d, { withFileTypes: true }).map((e) => ({ name: e.name, isDir: e.isDirectory() }));
|
|
409
|
+
sourceDeps = {
|
|
410
|
+
roots: resolveFrameworkRoots(cwd, { exists: existsSync }),
|
|
411
|
+
readFile,
|
|
412
|
+
readdir,
|
|
413
|
+
realpath: realpathSync,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
305
417
|
/** Write one JSON-RPC frame as a single line to stdout. */
|
|
306
418
|
const send = (frame) => {
|
|
307
419
|
stdout.write(JSON.stringify(frame) + '\n');
|
|
@@ -323,7 +435,7 @@ export async function runMcpServer(opts) {
|
|
|
323
435
|
if (method === 'initialize') {
|
|
324
436
|
return rpcResult(id, {
|
|
325
437
|
protocolVersion: PROTOCOL_VERSION,
|
|
326
|
-
capabilities: { tools: {} },
|
|
438
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
327
439
|
serverInfo: { name: 'webjs', version },
|
|
328
440
|
});
|
|
329
441
|
}
|
|
@@ -336,24 +448,61 @@ export async function runMcpServer(opts) {
|
|
|
336
448
|
tools: TOOL_DEFS.map((t) => ({
|
|
337
449
|
name: t.name,
|
|
338
450
|
description: t.description,
|
|
339
|
-
inputSchema:
|
|
451
|
+
inputSchema: t.inputSchema,
|
|
340
452
|
})),
|
|
341
453
|
});
|
|
342
454
|
}
|
|
343
455
|
|
|
456
|
+
// Knowledge layer (#376): the framework docs as MCP resources.
|
|
457
|
+
if (method === 'resources/list') {
|
|
458
|
+
return rpcResult(id, { resources: listResources(docsDeps) });
|
|
459
|
+
}
|
|
460
|
+
if (method === 'resources/read') {
|
|
461
|
+
const uri = ((msg && msg.params) || {}).uri;
|
|
462
|
+
try {
|
|
463
|
+
const r = await readResource(docsDeps, uri);
|
|
464
|
+
return rpcResult(id, { contents: [r] });
|
|
465
|
+
} catch (e) {
|
|
466
|
+
return rpcError(id, -32602, e && e.message ? e.message : String(e));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Knowledge layer (#376): the recipes as guided-workflow prompts.
|
|
471
|
+
if (method === 'prompts/list') {
|
|
472
|
+
return rpcResult(id, { prompts: PROMPTS });
|
|
473
|
+
}
|
|
474
|
+
if (method === 'prompts/get') {
|
|
475
|
+
const params = (msg && msg.params) || {};
|
|
476
|
+
try {
|
|
477
|
+
return rpcResult(id, getPrompt(params.name, params.arguments));
|
|
478
|
+
} catch (e) {
|
|
479
|
+
return rpcError(id, -32602, e && e.message ? e.message : String(e));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
344
483
|
if (method === 'tools/call') {
|
|
345
484
|
const params = (msg && msg.params) || {};
|
|
346
485
|
const name = params.name;
|
|
347
486
|
const args = params.arguments || {};
|
|
348
|
-
|
|
349
|
-
|
|
487
|
+
// The knowledge tools route to the docs / source layer; they return text.
|
|
488
|
+
const isKnowledgeTool = name === 'init' || name === 'docs' || name === 'source';
|
|
489
|
+
if (!isKnowledgeTool && !runners[name]) {
|
|
350
490
|
return rpcError(id, -32602, `Unknown tool: ${String(name)}`);
|
|
351
491
|
}
|
|
352
492
|
const appDir = typeof args.appDir === 'string' && args.appDir ? args.appDir : cwd;
|
|
353
493
|
try {
|
|
354
|
-
const result =
|
|
494
|
+
const result = isKnowledgeTool
|
|
495
|
+
? name === 'init'
|
|
496
|
+
? await initText(docsDeps)
|
|
497
|
+
: name === 'docs'
|
|
498
|
+
? await searchDocs(docsDeps, args)
|
|
499
|
+
: await runSourceTool(sourceDeps, args)
|
|
500
|
+
: await runners[name](appDir);
|
|
501
|
+
// Knowledge tools return a markdown string; introspection tools return
|
|
502
|
+
// a JSON-serialisable object.
|
|
503
|
+
const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
355
504
|
return rpcResult(id, {
|
|
356
|
-
content: [{ type: 'text', text
|
|
505
|
+
content: [{ type: 'text', text }],
|
|
357
506
|
});
|
|
358
507
|
} catch (e) {
|
|
359
508
|
// A tool failure is an MCP tool-result error (isError), not a transport
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "webjs CLI - dev, start, create, db",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"bin",
|
|
11
11
|
"lib",
|
|
12
12
|
"templates",
|
|
13
|
-
"README.md"
|
|
13
|
+
"README.md",
|
|
14
|
+
"resources"
|
|
14
15
|
],
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"@webjsdev/server": "^0.8.0",
|
|
@@ -35,5 +36,9 @@
|
|
|
35
36
|
],
|
|
36
37
|
"engines": {
|
|
37
38
|
"node": ">=24.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"prepack": "node scripts/copy-mcp-resources.js",
|
|
42
|
+
"postpack": "node scripts/clean-mcp-resources.js"
|
|
38
43
|
}
|
|
39
44
|
}
|