@webjsdev/mcp 0.1.17 → 0.1.19
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 +30 -103
- package/package.json +5 -10
- package/src/brief.js +52 -0
- package/src/catalog.js +82 -0
- package/src/mcp-init.js +139 -0
- package/src/mcp-source.js +94 -8
- package/src/mcp.js +96 -164
- package/resources/AGENTS.md +0 -701
- package/resources/SKILL.md +0 -286
- package/resources/corpus.json +0 -6
- package/resources/references/auth-and-sessions.md +0 -300
- package/resources/references/built-ins.md +0 -336
- package/resources/references/client-router-and-streaming.md +0 -313
- package/resources/references/components.md +0 -469
- package/resources/references/data-and-actions.md +0 -319
- package/resources/references/module-structure.md +0 -229
- package/resources/references/muscle-memory-gotchas.md +0 -341
- package/resources/references/optimistic-ui.md +0 -179
- package/resources/references/routing-and-pages.md +0 -323
- package/resources/references/runtime.md +0 -108
- package/resources/references/service-worker.md +0 -80
- package/resources/references/styling.md +0 -295
- package/resources/references/testing.md +0 -223
- package/resources/references/typescript.md +0 -231
- package/resources/references/ui-kit.md +0 -127
- package/src/mcp-docs.js +0 -641
package/src/mcp.js
CHANGED
|
@@ -23,19 +23,12 @@
|
|
|
23
23
|
import { createInterface } from 'node:readline';
|
|
24
24
|
import { relative } from 'node:path';
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
|
|
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';
|
|
26
|
+
import { initText } from './mcp-init.js';
|
|
27
|
+
import { resolveFrameworkRoots, runSourceTool, listDir } from './mcp-source.js';
|
|
36
28
|
// The export lookup behind the `source` tool's `export` arg, re-exported for
|
|
37
29
|
// `webjs source <Export>` in the CLI (#1623), which shells the same text.
|
|
38
|
-
export { lookupExport, resolveFrameworkRoots } from './mcp-source.js';
|
|
30
|
+
export { lookupExport, resolveFrameworkRoots, listDir, listSources } from './mcp-source.js';
|
|
31
|
+
export { featureCatalog, FEATURE_CATALOG } from './catalog.js';
|
|
39
32
|
import { projectRoutes } from './routes-report.js';
|
|
40
33
|
|
|
41
34
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
@@ -56,10 +49,9 @@ const RESERVED_CONFIG = new Set(['method', 'cache', 'tags', 'invalidates', 'vali
|
|
|
56
49
|
/**
|
|
57
50
|
* The `appDir` property, shared by every tool that is scoped to one app.
|
|
58
51
|
*
|
|
59
|
-
* `init`
|
|
60
|
-
*
|
|
61
|
-
* not ADVERTISE `appDir` could never receive one from a conforming client
|
|
62
|
-
* the app-corpus rung would be reachable only from hand-written JSON-RPC.
|
|
52
|
+
* `init` carries it too: it compares the app's installed `@webjsdev/mcp` with
|
|
53
|
+
* the running server's to warn about a stale server (#1319), and a tool that did
|
|
54
|
+
* not ADVERTISE `appDir` could never receive one from a conforming client.
|
|
63
55
|
*/
|
|
64
56
|
const APPDIR_PROP = {
|
|
65
57
|
type: 'string',
|
|
@@ -73,45 +65,38 @@ const APPDIR_SCHEMA = {
|
|
|
73
65
|
required: [],
|
|
74
66
|
};
|
|
75
67
|
|
|
76
|
-
/** `init` takes only the optional appDir,
|
|
68
|
+
/** `init` takes only the optional appDir, whose installed server version it compares. */
|
|
77
69
|
const INIT_SCHEMA = {
|
|
78
70
|
type: 'object',
|
|
79
71
|
properties: {
|
|
80
72
|
appDir: {
|
|
81
73
|
...APPDIR_PROP,
|
|
82
|
-
description:
|
|
74
|
+
description: 'App directory the agent is editing, checked for a newer @webjsdev/mcp than this server. Defaults to the server cwd.',
|
|
83
75
|
},
|
|
84
76
|
},
|
|
85
77
|
required: [],
|
|
86
78
|
};
|
|
87
79
|
|
|
88
|
-
/** `
|
|
89
|
-
const
|
|
80
|
+
/** `ui` lists the @webjsdev/ui kit, or details one component or block by `name`. */
|
|
81
|
+
const UI_SCHEMA = {
|
|
90
82
|
type: 'object',
|
|
91
83
|
properties: {
|
|
92
|
-
|
|
93
|
-
...APPDIR_PROP,
|
|
94
|
-
description: "App directory whose docs corpus to read. Defaults to the server cwd.",
|
|
95
|
-
},
|
|
96
|
-
topic: {
|
|
84
|
+
name: {
|
|
97
85
|
type: 'string',
|
|
98
|
-
description: 'A
|
|
86
|
+
description: 'A component (button, dialog) or block (login-01) name. Omit for the inventory.',
|
|
99
87
|
},
|
|
100
|
-
|
|
88
|
+
type: {
|
|
101
89
|
type: 'string',
|
|
102
|
-
|
|
90
|
+
enum: ['ui', 'block', 'chart', 'all'],
|
|
91
|
+
description: 'List one kind with one-line descriptions: ui (components), block, chart, or all.',
|
|
103
92
|
},
|
|
104
|
-
|
|
105
|
-
required: [],
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/** `ui` lists the @webjsdev/ui kit, or details one component by `name`. */
|
|
109
|
-
const UI_SCHEMA = {
|
|
110
|
-
type: 'object',
|
|
111
|
-
properties: {
|
|
112
|
-
name: {
|
|
93
|
+
category: {
|
|
113
94
|
type: 'string',
|
|
114
|
-
description: '
|
|
95
|
+
description: 'List the blocks and charts in one category (login, signup, sidebar, dashboard, area, ...) with descriptions.',
|
|
96
|
+
},
|
|
97
|
+
source: {
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
description: 'With a block `name`: include the file sources. Off by default, since installing the block and reading the files is cheaper.',
|
|
115
100
|
},
|
|
116
101
|
},
|
|
117
102
|
required: [],
|
|
@@ -135,42 +120,35 @@ const SOURCE_SCHEMA = {
|
|
|
135
120
|
},
|
|
136
121
|
package: {
|
|
137
122
|
type: 'string',
|
|
138
|
-
description: '
|
|
123
|
+
description: 'List one package\'s source entry points (core, server, cli, intellisense, ui).',
|
|
139
124
|
},
|
|
140
125
|
},
|
|
141
126
|
required: [],
|
|
142
127
|
};
|
|
143
128
|
|
|
144
129
|
/**
|
|
145
|
-
* The tools. The
|
|
146
|
-
* function (read-only, appDir-scoped). `init`
|
|
147
|
-
* framework
|
|
148
|
-
*
|
|
149
|
-
* model picks the right tool without reading source.
|
|
130
|
+
* The tools. The introspection tools project an EXISTING @webjsdev/server
|
|
131
|
+
* function (read-only, appDir-scoped). `init` returns the WebJs brief, `source`
|
|
132
|
+
* reads the framework's own source, and `ui` reads the @webjsdev/ui kit.
|
|
133
|
+
* Descriptions are crisp so a model picks the right tool without reading source.
|
|
150
134
|
*/
|
|
151
135
|
const TOOL_DEFS = [
|
|
152
136
|
{
|
|
153
137
|
name: 'init',
|
|
154
138
|
description:
|
|
155
|
-
'READ THIS FIRST before writing or editing a
|
|
139
|
+
'READ THIS FIRST before writing or editing a WebJs app. Returns the WebJs brief (Next.js App Router routing plus Lit component APIs, imported from @webjsdev/*, with the few differences that matter) and what each other tool is for. Read-only.',
|
|
156
140
|
inputSchema: INIT_SCHEMA,
|
|
157
141
|
},
|
|
158
|
-
{
|
|
159
|
-
name: 'docs',
|
|
160
|
-
description:
|
|
161
|
-
'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.',
|
|
162
|
-
inputSchema: DOCS_SCHEMA,
|
|
163
|
-
},
|
|
164
142
|
{
|
|
165
143
|
name: 'source',
|
|
166
144
|
description:
|
|
167
|
-
'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 `export` for the signature plus doc comment of ONE export (the cheap way to check a contract: `export: "createAuth"`), `path` to read a file (e.g. server/src/ssr/head.js; the bare server/src/ssr.js is a barrel that re-exports from the sibling directory), `query` to grep the @webjsdev/* src trees,
|
|
145
|
+
'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 `export` for the signature plus doc comment of ONE export (the cheap way to check a contract: `export: "createAuth"`), `path` to read a file (e.g. server/src/ssr/head.js; the bare server/src/ssr.js is a barrel that re-exports from the sibling directory), `query` to grep the @webjsdev/* src trees, `package` to list one package\'s entry points, or NO ARGS for the feature catalogue: what WebJs provides by need (auth, sessions, caching, rate limits, uploads, realtime, SEO, test helpers), each name one `export` call away from its signature and example. Call it before hand-writing any of those. Read-only.',
|
|
168
146
|
inputSchema: SOURCE_SCHEMA,
|
|
169
147
|
},
|
|
170
148
|
{
|
|
171
149
|
name: 'ui',
|
|
172
150
|
description:
|
|
173
|
-
'The @webjsdev/ui
|
|
151
|
+
'The @webjsdev/ui kit (shadcn-style, copied into your repo): components, plus blocks and charts (a block is a ready-made view you install and then edit: a login or signup page, a sidebar shell, a dashboard, a chart). No args returns the component inventory (tier, helper signatures, npm deps) and the block and chart NAMES grouped by category; `type` / `category` list them with one-line descriptions. Pass `name` for one component (helper signatures, the paste-ready example, the a11y header, deps) or one block (description, dependencies, the install command, and the files it writes with their targets; `source: true` adds the sources). Before hand-writing a page, a dashboard, a sidebar shell, an auth form or a chart, look for a block and install it. Reads the kit, not your app. Read-only.',
|
|
174
152
|
inputSchema: UI_SCHEMA,
|
|
175
153
|
},
|
|
176
154
|
{
|
|
@@ -191,12 +169,6 @@ const TOOL_DEFS = [
|
|
|
191
169
|
'List registered custom-element tags: tag name, defining file, and class name. Read-only.',
|
|
192
170
|
inputSchema: APPDIR_SCHEMA,
|
|
193
171
|
},
|
|
194
|
-
{
|
|
195
|
-
name: 'list_elision',
|
|
196
|
-
description:
|
|
197
|
-
'Report the display-only elision verdict: every component module with whether it is elided (the browser never downloads it) or shipped plus the evidence that produced the verdict, every page/layout route module as inert / import-only / shipped (with the first client-effecting blocker that pins it), and any ORPHAN component class, one that either has no registration call at all or registers a computed tag, which the scanner cannot see either way, so it always loses its elision verdict, its tag-to-module registry entry, and its preload hint (and never upgrades at all with no registration call, or when a computed-tag class has no importer that ships whole). Identical to `webjs elision --json`. Read-only.',
|
|
198
|
-
inputSchema: APPDIR_SCHEMA,
|
|
199
|
-
},
|
|
200
172
|
{
|
|
201
173
|
name: 'check',
|
|
202
174
|
description:
|
|
@@ -332,7 +304,7 @@ export function extractActionConfig(src) {
|
|
|
332
304
|
* JSON-serialisable projection of an existing server data function. All are
|
|
333
305
|
* read-only.
|
|
334
306
|
*
|
|
335
|
-
* @param {{ buildRouteTable: Function, buildActionIndex: Function, hashFile: Function, scanComponents: Function,
|
|
307
|
+
* @param {{ buildRouteTable: Function, buildActionIndex: Function, hashFile: Function, scanComponents: Function, checkConventions: Function, projectCheck: Function, readFile: Function }} deps
|
|
336
308
|
*/
|
|
337
309
|
export function makeToolRunners(deps) {
|
|
338
310
|
const {
|
|
@@ -340,7 +312,6 @@ export function makeToolRunners(deps) {
|
|
|
340
312
|
buildActionIndex,
|
|
341
313
|
hashFile,
|
|
342
314
|
scanComponents,
|
|
343
|
-
analyzeAppElision,
|
|
344
315
|
checkConventions,
|
|
345
316
|
projectCheck,
|
|
346
317
|
readFile,
|
|
@@ -404,15 +375,6 @@ export function makeToolRunners(deps) {
|
|
|
404
375
|
.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
405
376
|
},
|
|
406
377
|
|
|
407
|
-
async list_elision(appDir) {
|
|
408
|
-
// The whole report IS the contract: `analyzeAppElision` already returns
|
|
409
|
-
// an app-relative, sorted, JSON-serializable object, so unlike
|
|
410
|
-
// `list_routes` there is no projector leaf here and nothing to keep in
|
|
411
|
-
// sync. `webjs elision --json` prints this same object, and a drift test
|
|
412
|
-
// asserts the two are equal.
|
|
413
|
-
return analyzeAppElision(appDir);
|
|
414
|
-
},
|
|
415
|
-
|
|
416
378
|
async check(appDir) {
|
|
417
379
|
const violations = await checkConventions(appDir);
|
|
418
380
|
return projectCheck(violations);
|
|
@@ -420,16 +382,6 @@ export function makeToolRunners(deps) {
|
|
|
420
382
|
};
|
|
421
383
|
}
|
|
422
384
|
|
|
423
|
-
/**
|
|
424
|
-
* Run the `ui` tool: the @webjsdev/ui kit inventory (no args) or one component's
|
|
425
|
-
* projection (helper signatures + paste-ready example + a11y/description header
|
|
426
|
-
* + deps). Projects the SHARED `@webjsdev/ui/registry/extract` leaf, the same
|
|
427
|
-
* one `webjsui view` renders, so the CLI and MCP cannot drift (#983, the #979
|
|
428
|
-
* shared-projector pattern). `uiDeps` is injected so tests need no real package.
|
|
429
|
-
*
|
|
430
|
-
* @param {{ uiInventory: Function, uiComponent: Function }} uiDeps
|
|
431
|
-
* @param {{ name?: string }} args
|
|
432
|
-
*/
|
|
433
385
|
/**
|
|
434
386
|
* Resolve the `ui` tool's deps from the shared `@webjsdev/ui/registry/extract`
|
|
435
387
|
* leaf, GUARDED (#983): if `@webjsdev/ui` is absent or too old to carry the
|
|
@@ -439,29 +391,70 @@ export function makeToolRunners(deps) {
|
|
|
439
391
|
* top-level import would instead reject during bootstrap and sink the whole
|
|
440
392
|
* server. `importer` is injected so the failure path is unit-testable.
|
|
441
393
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
394
|
+
* A `@webjsdev/ui` that predates blocks (no `uiBlocks` export) still serves
|
|
395
|
+
* the components: the block deps then answer with nothing rather than throw.
|
|
396
|
+
*
|
|
397
|
+
* @param {() => Promise<any>} importer
|
|
398
|
+
* @returns {Promise<UiDeps>}
|
|
444
399
|
*/
|
|
445
400
|
export async function loadUiDeps(importer) {
|
|
446
401
|
try {
|
|
447
402
|
const ui = await importer();
|
|
448
|
-
return {
|
|
403
|
+
return {
|
|
404
|
+
uiInventory: ui.uiInventory,
|
|
405
|
+
uiComponent: ui.uiComponent,
|
|
406
|
+
uiBlocks: ui.uiBlocks ?? (() => []),
|
|
407
|
+
uiBlockIndex: ui.uiBlockIndex ?? (() => ({ blocks: {}, charts: {} })),
|
|
408
|
+
uiBlock: ui.uiBlock ?? (() => null),
|
|
409
|
+
};
|
|
449
410
|
} catch {
|
|
450
411
|
const unavailable = () => {
|
|
451
412
|
throw new Error('@webjsdev/ui is not available (install/upgrade @webjsdev/ui to use the ui tool)');
|
|
452
413
|
};
|
|
453
|
-
return { uiInventory: unavailable, uiComponent: unavailable };
|
|
414
|
+
return { uiInventory: unavailable, uiComponent: unavailable, uiBlocks: unavailable, uiBlockIndex: unavailable, uiBlock: unavailable };
|
|
454
415
|
}
|
|
455
416
|
}
|
|
456
417
|
|
|
418
|
+
/**
|
|
419
|
+
* @typedef {{
|
|
420
|
+
* uiInventory: Function, uiComponent: Function,
|
|
421
|
+
* uiBlocks: Function, uiBlockIndex: Function, uiBlock: Function,
|
|
422
|
+
* }} UiDeps
|
|
423
|
+
*/
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Run the `ui` tool. No args: the component inventory plus the block and chart
|
|
427
|
+
* names grouped by category. `type` / `category`: one compact entry per match
|
|
428
|
+
* with its description. A component `name`: its projection (helper signatures,
|
|
429
|
+
* paste-ready example, a11y header, deps). A block `name`: its description,
|
|
430
|
+
* dependencies, install command and files with targets (sources with
|
|
431
|
+
* `source: true`). Projects the SHARED `@webjsdev/ui/registry/extract` leaf,
|
|
432
|
+
* the same one `webjsui view` renders, so the CLI and MCP cannot drift (#983,
|
|
433
|
+
* the #979 shared-projector pattern). `uiDeps` is injected so tests need no
|
|
434
|
+
* real package.
|
|
435
|
+
*
|
|
436
|
+
* @param {UiDeps} uiDeps
|
|
437
|
+
* @param {{ name?: string, type?: string, category?: string, source?: boolean }} args
|
|
438
|
+
*/
|
|
457
439
|
export function runUiTool(uiDeps, args) {
|
|
458
440
|
const name = typeof args.name === 'string' && args.name ? args.name : null;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
441
|
+
const type = typeof args.type === 'string' && args.type ? args.type : null;
|
|
442
|
+
if (type && !['ui', 'block', 'chart', 'all'].includes(type)) {
|
|
443
|
+
throw new Error(`Unknown type "${type}". Use ui, block, chart or all.`);
|
|
444
|
+
}
|
|
445
|
+
const category = typeof args.category === 'string' && args.category ? args.category : null;
|
|
446
|
+
if (!name) {
|
|
447
|
+
// A filter lists one kind with its one-line descriptions. The bare call
|
|
448
|
+
// stays cheap: the component inventory plus the block and chart NAMES by
|
|
449
|
+
// category (D18), and a test caps its size.
|
|
450
|
+
if (type || category) return { items: uiDeps.uiBlocks({ type: type ?? undefined, category: category ?? undefined }) };
|
|
451
|
+
return { inventory: uiDeps.uiInventory(), ...uiDeps.uiBlockIndex() };
|
|
463
452
|
}
|
|
464
|
-
|
|
453
|
+
const component = uiDeps.uiComponent(name);
|
|
454
|
+
if (component) return component;
|
|
455
|
+
const block = uiDeps.uiBlock(name, { source: args.source === true });
|
|
456
|
+
if (block) return block;
|
|
457
|
+
throw new Error(`Unknown component or block "${String(name)}". Call ui with no args for the inventory.`);
|
|
465
458
|
}
|
|
466
459
|
|
|
467
460
|
/**
|
|
@@ -519,7 +512,6 @@ export async function runMcpServer(opts) {
|
|
|
519
512
|
buildActionIndex: server.buildActionIndex,
|
|
520
513
|
hashFile: server.hashFile,
|
|
521
514
|
scanComponents: server.scanComponents,
|
|
522
|
-
analyzeAppElision: server.analyzeAppElision,
|
|
523
515
|
checkConventions: check.checkConventions,
|
|
524
516
|
projectCheck,
|
|
525
517
|
readFile,
|
|
@@ -527,47 +519,16 @@ export async function runMcpServer(opts) {
|
|
|
527
519
|
}
|
|
528
520
|
const runners = makeToolRunners(deps);
|
|
529
521
|
|
|
530
|
-
// The
|
|
531
|
-
//
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
// Resolved PER CALL and memoized by appDir (#1319), not once at boot. `appDir`
|
|
536
|
-
// is a per-call tool argument, so a boot-time resolution would pin the corpus
|
|
537
|
-
// to the launch directory forever and defeat the app-corpus rung for any host
|
|
538
|
-
// that overrides it. `resources/list` and `resources/read` carry no appDir in
|
|
539
|
-
// the MCP protocol, so they resolve from `cwd`, which is the same value a
|
|
540
|
-
// `tools/call` with no `appDir` argument defaults to; the surfaces can only
|
|
541
|
-
// diverge when a caller deliberately asks about a different app.
|
|
542
|
-
let docsFs = null;
|
|
543
|
-
if (!opts.docsDeps) {
|
|
522
|
+
// The `init` deps: fs readers for the stale-server check, which compares the
|
|
523
|
+
// app's installed @webjsdev/mcp with this server's version (#1319). Resolved
|
|
524
|
+
// per call from the tool's `appDir`. Injectable for tests via `initFs`.
|
|
525
|
+
let initFs = opts.initFs;
|
|
526
|
+
if (!initFs) {
|
|
544
527
|
const { readFile } = await import('node:fs/promises');
|
|
545
|
-
const {
|
|
546
|
-
|
|
528
|
+
const { existsSync } = await import('node:fs');
|
|
529
|
+
initFs = { readFile, exists: existsSync };
|
|
547
530
|
}
|
|
548
|
-
|
|
549
|
-
const docsDepsCache = new Map();
|
|
550
|
-
/** The docs deps for one appDir. An injected `opts.docsDeps` wins for every appDir. */
|
|
551
|
-
const docsDepsFor = (dir) => {
|
|
552
|
-
if (opts.docsDeps) return opts.docsDeps;
|
|
553
|
-
const key = typeof dir === 'string' ? dir : '';
|
|
554
|
-
let built = docsDepsCache.get(key);
|
|
555
|
-
if (!built) {
|
|
556
|
-
const loc = resolveDocsLocation(import.meta.url, key);
|
|
557
|
-
built = {
|
|
558
|
-
docsDir: loc.docsDir,
|
|
559
|
-
agentsPath: loc.agentsPath,
|
|
560
|
-
skillPath: loc.skillPath,
|
|
561
|
-
corpusPath: loc.corpusPath,
|
|
562
|
-
corpusSource: loc.corpusSource,
|
|
563
|
-
appDir: key,
|
|
564
|
-
serverVersion: version,
|
|
565
|
-
...docsFs,
|
|
566
|
-
};
|
|
567
|
-
docsDepsCache.set(key, built);
|
|
568
|
-
}
|
|
569
|
-
return built;
|
|
570
|
-
};
|
|
531
|
+
const initDepsFor = (dir) => ({ ...initFs, appDir: dir, serverVersion: version });
|
|
571
532
|
|
|
572
533
|
// The `source` tool (#378): read the framework's own source from
|
|
573
534
|
// node_modules/@webjsdev/*/src (no-build, so it is the real JSDoc). Roots are
|
|
@@ -575,8 +536,8 @@ export async function runMcpServer(opts) {
|
|
|
575
536
|
let sourceDeps = opts.sourceDeps;
|
|
576
537
|
if (!sourceDeps) {
|
|
577
538
|
const { readFile } = await import('node:fs/promises');
|
|
578
|
-
const {
|
|
579
|
-
const readdir =
|
|
539
|
+
const { existsSync, realpathSync } = await import('node:fs');
|
|
540
|
+
const readdir = listDir;
|
|
580
541
|
sourceDeps = {
|
|
581
542
|
roots: resolveFrameworkRoots(cwd, { exists: existsSync }),
|
|
582
543
|
readFile,
|
|
@@ -613,7 +574,7 @@ export async function runMcpServer(opts) {
|
|
|
613
574
|
if (method === 'initialize') {
|
|
614
575
|
return rpcResult(id, {
|
|
615
576
|
protocolVersion: PROTOCOL_VERSION,
|
|
616
|
-
capabilities: { tools: {}
|
|
577
|
+
capabilities: { tools: {} },
|
|
617
578
|
serverInfo: { name: 'webjs', version },
|
|
618
579
|
});
|
|
619
580
|
}
|
|
@@ -631,39 +592,12 @@ export async function runMcpServer(opts) {
|
|
|
631
592
|
});
|
|
632
593
|
}
|
|
633
594
|
|
|
634
|
-
// Knowledge layer (#376): the framework docs as MCP resources.
|
|
635
|
-
if (method === 'resources/list') {
|
|
636
|
-
return rpcResult(id, { resources: listResources(docsDepsFor(cwd)) });
|
|
637
|
-
}
|
|
638
|
-
if (method === 'resources/read') {
|
|
639
|
-
const uri = ((msg && msg.params) || {}).uri;
|
|
640
|
-
try {
|
|
641
|
-
const r = await readResource(docsDepsFor(cwd), uri);
|
|
642
|
-
return rpcResult(id, { contents: [r] });
|
|
643
|
-
} catch (e) {
|
|
644
|
-
return rpcError(id, -32602, e && e.message ? e.message : String(e));
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// Knowledge layer (#376): the recipes as guided-workflow prompts.
|
|
649
|
-
if (method === 'prompts/list') {
|
|
650
|
-
return rpcResult(id, { prompts: PROMPTS });
|
|
651
|
-
}
|
|
652
|
-
if (method === 'prompts/get') {
|
|
653
|
-
const params = (msg && msg.params) || {};
|
|
654
|
-
try {
|
|
655
|
-
return rpcResult(id, getPrompt(params.name, params.arguments));
|
|
656
|
-
} catch (e) {
|
|
657
|
-
return rpcError(id, -32602, e && e.message ? e.message : String(e));
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
595
|
if (method === 'tools/call') {
|
|
662
596
|
const params = (msg && msg.params) || {};
|
|
663
597
|
const name = params.name;
|
|
664
598
|
const args = params.arguments || {};
|
|
665
|
-
// The knowledge tools
|
|
666
|
-
const isKnowledgeTool = name === 'init' || name === '
|
|
599
|
+
// The knowledge tools (the brief, the framework source) return text.
|
|
600
|
+
const isKnowledgeTool = name === 'init' || name === 'source';
|
|
667
601
|
// `ui` is kit-scoped (not app-scoped), so it is dispatched separately.
|
|
668
602
|
const isUiTool = name === 'ui';
|
|
669
603
|
if (!isKnowledgeTool && !isUiTool && !runners[name]) {
|
|
@@ -673,10 +607,8 @@ export async function runMcpServer(opts) {
|
|
|
673
607
|
try {
|
|
674
608
|
const result = isKnowledgeTool
|
|
675
609
|
? name === 'init'
|
|
676
|
-
? await initText(
|
|
677
|
-
:
|
|
678
|
-
? await searchDocs(docsDepsFor(appDir), args)
|
|
679
|
-
: await runSourceTool(sourceDeps, args)
|
|
610
|
+
? await initText(initDepsFor(appDir))
|
|
611
|
+
: await runSourceTool(sourceDeps, args)
|
|
680
612
|
: isUiTool
|
|
681
613
|
? runUiTool(uiDeps, args)
|
|
682
614
|
: await runners[name](appDir);
|