@stone-js/mcp-dev 0.8.8 → 0.8.10
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 +60 -12
- package/dist/McpDevServer.d.ts +1 -1
- package/dist/appContext.d.ts +60 -0
- package/dist/cli.d.ts +53 -0
- package/dist/cli.js +915 -0
- package/dist/commands/McpCommand.d.ts +1 -1
- package/dist/declarations.d.ts +16 -1
- package/dist/index.d.ts +11 -13
- package/dist/index.js +179 -109
- package/dist/introspection.d.ts +21 -2
- package/dist/knowledge.d.ts +1 -1
- package/dist/llms.d.ts +1 -1
- package/dist/mcpJson.d.ts +8 -2
- package/dist/tools.d.ts +1 -1
- package/package.json +15 -12
- package/dist/browser/decorators/McpDev.d.ts +0 -19
- package/dist/browser/options/McpDevBlueprint.d.ts +0 -18
- package/dist/browser.js +0 -37
- package/dist/decorators/McpDev.d.ts +0 -24
- package/dist/middleware/BlueprintMiddleware.d.ts +0 -17
- package/dist/options/McpDevBlueprint.d.ts +0 -34
package/dist/declarations.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ZodRawShape } from 'zod';
|
|
1
2
|
/** A core architectural concept of Stone.js. */
|
|
2
3
|
export interface Concept {
|
|
3
4
|
/** Slug id (e.g. `continuum`). */
|
|
@@ -50,7 +51,14 @@ export interface KnowledgeBase {
|
|
|
50
51
|
export interface McpToolDef {
|
|
51
52
|
name: string;
|
|
52
53
|
description?: string;
|
|
53
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The arguments the tool accepts, as a Zod shape (`{ query: z.string() }`).
|
|
56
|
+
*
|
|
57
|
+
* Required for any tool whose handler reads `args`: a tool that publishes no schema is advertised
|
|
58
|
+
* as taking none, and an MCP client drops the arguments before sending them, so the handler runs
|
|
59
|
+
* with `{}` and there is no error anywhere to say so.
|
|
60
|
+
*/
|
|
61
|
+
inputSchema?: ZodRawShape;
|
|
54
62
|
handler: (args: Record<string, unknown>) => unknown;
|
|
55
63
|
}
|
|
56
64
|
/**
|
|
@@ -112,4 +120,11 @@ export interface McpDevOptions {
|
|
|
112
120
|
report?: ReportToolsOptions;
|
|
113
121
|
/** Silence the stderr activity log. */
|
|
114
122
|
quiet?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Whether a running application publishes its resolved configuration for the MCP server to read.
|
|
125
|
+
*
|
|
126
|
+
* Defaults to on outside production. It is what lets `stone mcp` describe the application actually
|
|
127
|
+
* running rather than the one the console command boots, so leave it on while developing.
|
|
128
|
+
*/
|
|
129
|
+
publishContext?: boolean;
|
|
115
130
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
export * from './McpDevServer';
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './errors/McpDevError';
|
|
7
|
-
export * from './introspection';
|
|
8
|
-
export * from './knowledge';
|
|
9
|
-
export * from './llms';
|
|
10
|
-
export * from './mcpJson';
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './options/McpDevBlueprint';
|
|
13
|
-
export * from './tools';
|
|
1
|
+
export * from './McpDevServer.js';
|
|
2
|
+
export * from './appContext.js';
|
|
3
|
+
export * from './commands/McpCommand.js';
|
|
4
|
+
export * from './constants.js';
|
|
5
|
+
export * from './declarations.js';
|
|
6
|
+
export * from './errors/McpDevError.js';
|
|
7
|
+
export * from './introspection.js';
|
|
8
|
+
export * from './knowledge.js';
|
|
9
|
+
export * from './llms.js';
|
|
10
|
+
export * from './mcpJson.js';
|
|
11
|
+
export * from './tools.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { join, dirname } from 'node:path';
|
|
2
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
-
import { RuntimeError
|
|
6
|
+
import { RuntimeError } from '@stone-js/core';
|
|
6
7
|
|
|
7
8
|
/* v8 ignore start -- thin filesystem defaults */
|
|
8
9
|
const defaultIo = {
|
|
@@ -14,11 +15,17 @@ const defaultIo = {
|
|
|
14
15
|
/**
|
|
15
16
|
* The `.mcp.json` server entry that launches this dev server.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
+
* It goes through `npx` because `@stone-js/cli` is a project dev dependency: a bare `stone` only
|
|
19
|
+
* resolves when the CLI is also installed globally, so the entry an agent spawns would fail with
|
|
20
|
+
* ENOENT on a normal project. `npx` resolves the project-local binary first, and still finds a
|
|
21
|
+
* global install, so the generated file works either way.
|
|
22
|
+
*
|
|
23
|
+
* @param command - The launcher command (defaults to `npx`).
|
|
24
|
+
* @param args - The launcher arguments (defaults to `stone mcp`).
|
|
18
25
|
* @returns The MCP server entry.
|
|
19
26
|
*/
|
|
20
|
-
function mcpServerEntry(command = 'stone') {
|
|
21
|
-
return { command, args
|
|
27
|
+
function mcpServerEntry(command = 'npx', args = ['stone', 'mcp']) {
|
|
28
|
+
return { command, args };
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Merge the `stone` server into an existing `.mcp.json` object without clobbering anything.
|
|
@@ -96,6 +103,11 @@ const modules = [
|
|
|
96
103
|
{ package: '@stone-js/router', summary: 'Universal router (node & browser).', tier: 'crosscutting' },
|
|
97
104
|
{ package: '@stone-js/env', summary: 'Environment access with masking.', tier: 'crosscutting' },
|
|
98
105
|
{ package: '@stone-js/filesystem', summary: 'Filesystem + file abstractions.', tier: 'crosscutting' },
|
|
106
|
+
{ package: '@stone-js/cache', summary: 'Platform-agnostic caching with pluggable stores.', tier: 'crosscutting' },
|
|
107
|
+
{ package: '@stone-js/cloud-file', summary: 'Cloud object-storage drivers extending the filesystem abstractions.', tier: 'crosscutting' },
|
|
108
|
+
{ package: '@stone-js/i18n', summary: 'Runtime localization and translation services.', tier: 'crosscutting' },
|
|
109
|
+
{ package: '@stone-js/queue', summary: 'Background job queues with pluggable drivers.', tier: 'crosscutting' },
|
|
110
|
+
{ package: '@stone-js/realtime', summary: 'Realtime channels, rooms and presence.', tier: 'crosscutting' },
|
|
99
111
|
{ package: '@stone-js/browser-core', summary: 'Browser-side primitives.', tier: 'crosscutting' },
|
|
100
112
|
{ package: '@stone-js/node-http-adapter', summary: 'Node HTTP server adapter.', tier: 'adapter' },
|
|
101
113
|
{ package: '@stone-js/node-cli-adapter', summary: 'Node CLI adapter.', tier: 'adapter' },
|
|
@@ -128,13 +140,8 @@ const bestPractices = [
|
|
|
128
140
|
{ rule: 'Attach request-scoped state via setMetadataValue/getMetadataValue; the principal via setUserResolver.', why: 'The idiomatic per-event carriers.' }
|
|
129
141
|
];
|
|
130
142
|
const gaps = [
|
|
131
|
-
{ name: 'queue/jobs', status: 'planned', note: 'Background jobs (in-memory, Redis/BullMQ, SQS, Cloud Tasks).' },
|
|
132
|
-
{ name: 'cache', status: 'planned', note: 'Agnostic cache (memory, Redis, CF KV) — the legitimate shared scope.' },
|
|
133
143
|
{ name: 'mail/notifications', status: 'planned', note: 'Multi-channel notifications.' },
|
|
134
144
|
{ name: 'rate-limiting', status: 'planned', note: 'Edge-friendly throttling.' },
|
|
135
|
-
{ name: 'i18n', status: 'planned', note: 'Runtime localization.' },
|
|
136
|
-
{ name: 'websocket/realtime', status: 'planned', note: 'Channels/rooms/presence, agnostic drivers.' },
|
|
137
|
-
{ name: 'cloud file drivers', status: 'planned', note: 'S3/R2/GCS drivers extending @stone-js/filesystem.' },
|
|
138
145
|
{ name: 'ORM', status: 'missing', note: 'By design: integrate Drizzle/Prisma/Kysely via providers — Stone.js will not ship an ORM.' }
|
|
139
146
|
];
|
|
140
147
|
/**
|
|
@@ -248,11 +255,17 @@ const stoneMcpTools = [
|
|
|
248
255
|
{
|
|
249
256
|
name: 'stone_search',
|
|
250
257
|
description: 'Search the Stone.js knowledge base (concepts, modules, best-practices, gaps).',
|
|
258
|
+
inputSchema: {
|
|
259
|
+
query: z.string().describe('What to look for, matched against concepts, modules, best practices and gaps.')
|
|
260
|
+
},
|
|
251
261
|
handler: (args) => searchKnowledge(String(args.query ?? ''))
|
|
252
262
|
},
|
|
253
263
|
{
|
|
254
264
|
name: 'stone_concept',
|
|
255
265
|
description: 'Explain a core Stone.js concept by id (omit id to list them all).',
|
|
266
|
+
inputSchema: {
|
|
267
|
+
id: z.string().optional().describe('The concept id. Omit it to list every concept instead.')
|
|
268
|
+
},
|
|
256
269
|
handler: (args) => {
|
|
257
270
|
const id = String(args.id ?? '');
|
|
258
271
|
if (id.length === 0) {
|
|
@@ -311,11 +324,19 @@ function createReportTools(options) {
|
|
|
311
324
|
{
|
|
312
325
|
name: 'stone_report_bug',
|
|
313
326
|
description: 'Open a bug report as a GitHub issue on the Stone.js repository.',
|
|
327
|
+
inputSchema: {
|
|
328
|
+
title: z.string().describe('One line naming the defect.'),
|
|
329
|
+
body: z.string().describe('What happens, what was expected, and how to reproduce it.')
|
|
330
|
+
},
|
|
314
331
|
handler: async (args) => await openIssue(String(args.title ?? 'Bug report'), String(args.body ?? ''), 'bug')
|
|
315
332
|
},
|
|
316
333
|
{
|
|
317
334
|
name: 'stone_request_feature',
|
|
318
335
|
description: 'Open a feature request as a GitHub issue on the Stone.js repository.',
|
|
336
|
+
inputSchema: {
|
|
337
|
+
title: z.string().describe('One line naming the feature.'),
|
|
338
|
+
body: z.string().describe('The problem it solves, and how it should behave.')
|
|
339
|
+
},
|
|
319
340
|
handler: async (args) => await openIssue(String(args.title ?? 'Feature request'), String(args.body ?? ''), 'enhancement')
|
|
320
341
|
}
|
|
321
342
|
];
|
|
@@ -413,7 +434,10 @@ function createToolCallback(tool, log) {
|
|
|
413
434
|
function buildMcpServer(options, log) {
|
|
414
435
|
const server = new McpServer({ name: options.name ?? DEFAULT_MCP_SERVER_NAME, version: options.version ?? DEFAULT_MCP_SERVER_VERSION }, { instructions: options.instructions ?? DEFAULT_MCP_INSTRUCTIONS });
|
|
415
436
|
for (const tool of resolveTools(options)) {
|
|
416
|
-
|
|
437
|
+
// A tool with no schema is registered with none at all, rather than with an empty one: both are
|
|
438
|
+
// advertised as taking no arguments, but the empty shape reads like a declared contract when it
|
|
439
|
+
// is the absence of one.
|
|
440
|
+
server.registerTool(tool.name, { description: tool.description, inputSchema: tool.inputSchema }, createToolCallback(tool, log));
|
|
417
441
|
}
|
|
418
442
|
return server;
|
|
419
443
|
}
|
|
@@ -543,6 +567,52 @@ function countRoutes(defs) {
|
|
|
543
567
|
return total + 1 + countRoutes(children);
|
|
544
568
|
}, 0);
|
|
545
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* What the tools are describing, and how they know.
|
|
572
|
+
*
|
|
573
|
+
* The MCP server is a console command, so the blueprint it holds is the one a *console* boot resolves:
|
|
574
|
+
* its adapter, its response type and every platform-conditional contribution belong to a different
|
|
575
|
+
* application than the one running under `stone dev`. When the running application has published its
|
|
576
|
+
* own configuration, that is the better answer and it is used; otherwise the console boot still
|
|
577
|
+
* answers for everything platform-independent — routes, providers, the kernel handler — and says which
|
|
578
|
+
* of its answers not to trust, rather than pretending to be the running app.
|
|
579
|
+
*
|
|
580
|
+
* @param blueprint - The blueprint of the process the MCP server runs in.
|
|
581
|
+
* @param cwd - The project root.
|
|
582
|
+
* @returns The reader to introspect, and a description of it.
|
|
583
|
+
*/
|
|
584
|
+
function resolveSource(blueprint, cwd) {
|
|
585
|
+
const published = readAppContext(cwd);
|
|
586
|
+
if (published === undefined) {
|
|
587
|
+
return {
|
|
588
|
+
source: blueprint,
|
|
589
|
+
describes: {
|
|
590
|
+
source: 'console-boot',
|
|
591
|
+
platform: blueprint.get('stone.adapter.platform'),
|
|
592
|
+
accurate: ['stone_routes', 'stone_commands', 'stone_providers', 'stone_kernel', 'stone_key_routes'],
|
|
593
|
+
unreliable: ['stone_adapters', 'stone_config'],
|
|
594
|
+
why: 'No running application has published its configuration, so this describes what a console ' +
|
|
595
|
+
'boot resolves. Anything platform-dependent therefore belongs to the console platform, not ' +
|
|
596
|
+
'to the application you are running. Install `@stone-js/mcp-dev` as a devDependency and run ' +
|
|
597
|
+
`the app once (\`stone dev\`): the build injects a publisher that writes ${APP_CONTEXT_FILE}, ` +
|
|
598
|
+
'and these tools then describe that application.'
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
source: contextReader(published),
|
|
604
|
+
describes: {
|
|
605
|
+
source: 'running-app',
|
|
606
|
+
platform: published.platform,
|
|
607
|
+
env: published.env,
|
|
608
|
+
name: published.name,
|
|
609
|
+
file: APP_CONTEXT_FILE,
|
|
610
|
+
why: 'This describes the application that actually ran, as it resolved itself: its platform, its ' +
|
|
611
|
+
'adapters and its configuration. Values that change after boot, such as a `live` ' +
|
|
612
|
+
'configuration, are as of that boot.'
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
}
|
|
546
616
|
/**
|
|
547
617
|
* Build the read-only introspection tools bound to the app's resolved blueprint.
|
|
548
618
|
*
|
|
@@ -553,22 +623,28 @@ function countRoutes(defs) {
|
|
|
553
623
|
* @param blueprint - The resolved application blueprint.
|
|
554
624
|
* @returns The introspection tools.
|
|
555
625
|
*/
|
|
556
|
-
function createIntrospectionTools(blueprint) {
|
|
557
|
-
const
|
|
558
|
-
const
|
|
626
|
+
function createIntrospectionTools(blueprint, cwd) {
|
|
627
|
+
const { source, describes } = resolveSource(blueprint, cwd);
|
|
628
|
+
const routes = () => source.get('stone.router.definitions', []);
|
|
629
|
+
const commands = () => source.get('stone.adapter.commands', []);
|
|
559
630
|
return [
|
|
631
|
+
{
|
|
632
|
+
name: 'stone_describes',
|
|
633
|
+
description: 'Say which application the introspection tools are describing, and how they know.',
|
|
634
|
+
handler: () => describes
|
|
635
|
+
},
|
|
560
636
|
{
|
|
561
637
|
name: 'stone_app',
|
|
562
638
|
description: 'Summarize the current Stone.js app: name, env, active platform, and counts of routes/commands/providers/adapters.',
|
|
563
639
|
handler: () => clean({
|
|
564
|
-
name:
|
|
565
|
-
env:
|
|
566
|
-
platform:
|
|
640
|
+
name: source.get('stone.name'),
|
|
641
|
+
env: source.get('stone.env'),
|
|
642
|
+
platform: source.get('stone.adapter.platform'),
|
|
567
643
|
counts: {
|
|
568
644
|
routes: countRoutes(routes()),
|
|
569
645
|
commands: commands().length,
|
|
570
|
-
providers:
|
|
571
|
-
adapters:
|
|
646
|
+
providers: source.get('stone.providers', []).length,
|
|
647
|
+
adapters: source.get('stone.adapters', []).length
|
|
572
648
|
}
|
|
573
649
|
})
|
|
574
650
|
},
|
|
@@ -591,8 +667,8 @@ function createIntrospectionTools(blueprint) {
|
|
|
591
667
|
name: 'stone_adapters',
|
|
592
668
|
description: 'List the registered adapters (platform, alias, default/current) and the active platform.',
|
|
593
669
|
handler: () => ({
|
|
594
|
-
active:
|
|
595
|
-
adapters:
|
|
670
|
+
active: source.get('stone.adapter.platform'),
|
|
671
|
+
adapters: source.get('stone.adapters', []).map((a) => clean({
|
|
596
672
|
platform: a.platform,
|
|
597
673
|
alias: a.alias,
|
|
598
674
|
current: a.current,
|
|
@@ -603,13 +679,13 @@ function createIntrospectionTools(blueprint) {
|
|
|
603
679
|
{
|
|
604
680
|
name: 'stone_providers',
|
|
605
681
|
description: 'List the app\'s service providers.',
|
|
606
|
-
handler: () =>
|
|
682
|
+
handler: () => source.get('stone.providers', []).map(moduleName)
|
|
607
683
|
},
|
|
608
684
|
{
|
|
609
685
|
name: 'stone_kernel',
|
|
610
686
|
description: 'Show the kernel pipeline: the event handler, middleware, and registered error handlers.',
|
|
611
687
|
handler: () => {
|
|
612
|
-
const kernel =
|
|
688
|
+
const kernel = source.get('stone.kernel', {});
|
|
613
689
|
return clean({
|
|
614
690
|
eventHandler: kernel.eventHandler !== undefined ? moduleName(kernel.eventHandler) : undefined,
|
|
615
691
|
middleware: (kernel.middleware ?? []).map(moduleName),
|
|
@@ -620,7 +696,7 @@ function createIntrospectionTools(blueprint) {
|
|
|
620
696
|
{
|
|
621
697
|
name: 'stone_key_routes',
|
|
622
698
|
description: 'List the key-routing definitions (event-bus / realtime / keyed events): key to handler.',
|
|
623
|
-
handler: () =>
|
|
699
|
+
handler: () => source.get('stone.keyRouting.definitions', []).map((d) => clean({
|
|
624
700
|
key: d.key,
|
|
625
701
|
action: d.action,
|
|
626
702
|
handler: d.module !== undefined ? moduleName(d.module) : undefined
|
|
@@ -629,17 +705,91 @@ function createIntrospectionTools(blueprint) {
|
|
|
629
705
|
{
|
|
630
706
|
name: 'stone_config',
|
|
631
707
|
description: 'Read a resolved config value by dotted key under `stone.*` (secrets redacted). Omit `key` to list the top-level `stone` keys.',
|
|
708
|
+
inputSchema: {
|
|
709
|
+
key: z.string().optional().describe('A dotted key such as `stone.router`. Omit it to list the top-level `stone` keys instead.')
|
|
710
|
+
},
|
|
632
711
|
handler: (args) => {
|
|
633
712
|
const key = String(args.key ?? '');
|
|
634
713
|
if (key.length === 0) {
|
|
635
|
-
return Object.keys(
|
|
714
|
+
return Object.keys(source.get('stone', {}));
|
|
636
715
|
}
|
|
637
|
-
return sanitize(
|
|
716
|
+
return sanitize(source.get(key));
|
|
638
717
|
}
|
|
639
718
|
}
|
|
640
719
|
];
|
|
641
720
|
}
|
|
642
721
|
|
|
722
|
+
/** Where a running application leaves its resolved configuration for the MCP server to read. */
|
|
723
|
+
const APP_CONTEXT_FILE = join('.stone', 'app-context.json');
|
|
724
|
+
/**
|
|
725
|
+
* Publish an application's resolved configuration.
|
|
726
|
+
*
|
|
727
|
+
* This is what makes `stone mcp` describe the application a developer is actually running. The MCP
|
|
728
|
+
* server is a console command: booting the app itself gives it the *console* platform, so its
|
|
729
|
+
* adapters, its response type and every platform-conditional contribution belong to a different
|
|
730
|
+
* application than the one under `stone dev`. The running app knows the truth, so the running app
|
|
731
|
+
* says it.
|
|
732
|
+
*
|
|
733
|
+
* A file rather than an endpoint, deliberately. The Blueprint is the Setup dimension: assembled once
|
|
734
|
+
* before the first event and then read, so publishing it once at boot is not a snapshot of something
|
|
735
|
+
* moving — it *is* the value. A file also needs no port to discover, no dev-only route in someone's
|
|
736
|
+
* application, no token to protect, and it works for a CLI or an edge context that has no HTTP
|
|
737
|
+
* surface at all. What genuinely moves at run time (a `live` configuration, metrics) is a different
|
|
738
|
+
* question, and belongs to a different tool.
|
|
739
|
+
*
|
|
740
|
+
* @param blueprint - The running application's blueprint.
|
|
741
|
+
* @param cwd - The project root.
|
|
742
|
+
* @returns The file it wrote.
|
|
743
|
+
*/
|
|
744
|
+
function publishAppContext(blueprint, cwd = process.cwd()) {
|
|
745
|
+
const path = join(cwd, APP_CONTEXT_FILE);
|
|
746
|
+
const context = {
|
|
747
|
+
platform: blueprint.get('stone.adapter.platform'),
|
|
748
|
+
env: blueprint.get('stone.env'),
|
|
749
|
+
name: blueprint.get('stone.name'),
|
|
750
|
+
stone: sanitize(blueprint.get('stone', {}))
|
|
751
|
+
};
|
|
752
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
753
|
+
writeFileSync(path, JSON.stringify(context, null, 2), 'utf-8');
|
|
754
|
+
return path;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Read what a running application published, if it published anything.
|
|
758
|
+
*
|
|
759
|
+
* @param cwd - The project root.
|
|
760
|
+
* @returns The context, or `undefined` when no application has run.
|
|
761
|
+
*/
|
|
762
|
+
function readAppContext(cwd = process.cwd()) {
|
|
763
|
+
const path = join(cwd, APP_CONTEXT_FILE);
|
|
764
|
+
if (!existsSync(path)) {
|
|
765
|
+
return undefined;
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
769
|
+
}
|
|
770
|
+
catch {
|
|
771
|
+
// A half-written or hand-edited file is not worth failing the whole MCP server for: the tools
|
|
772
|
+
// fall back to what the console boot knows, and say so.
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* A reader over a published context, answering the same dotted keys a blueprint answers.
|
|
778
|
+
*
|
|
779
|
+
* @param context - The published context.
|
|
780
|
+
* @returns The reader.
|
|
781
|
+
*/
|
|
782
|
+
function contextReader(context) {
|
|
783
|
+
return {
|
|
784
|
+
get: (key, fallback) => {
|
|
785
|
+
const value = key.split('.').reduce((current, segment) => (typeof current === 'object' && current !== null)
|
|
786
|
+
? current[segment]
|
|
787
|
+
: undefined, { stone: context.stone });
|
|
788
|
+
return (value ?? fallback);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
643
793
|
/**
|
|
644
794
|
* Custom error for the MCP dev module.
|
|
645
795
|
*/
|
|
@@ -695,7 +845,7 @@ class McpCommand {
|
|
|
695
845
|
process.stderr.write(changed ? `mcp: registered this server in ${file}\n` : `mcp: ${file} already registers this server\n`);
|
|
696
846
|
return;
|
|
697
847
|
}
|
|
698
|
-
const options = blueprint.get('stone.mcpDev', {});
|
|
848
|
+
const options = blueprint.get('stone.builder.mcpDev', {});
|
|
699
849
|
const name = event.getMetadataValue('name', options.name);
|
|
700
850
|
const quiet = event.getMetadataValue('quiet', options.quiet ?? false);
|
|
701
851
|
const tools = [...createIntrospectionTools(blueprint), ...(options.tools ?? [])];
|
|
@@ -703,84 +853,4 @@ class McpCommand {
|
|
|
703
853
|
}
|
|
704
854
|
}
|
|
705
855
|
|
|
706
|
-
|
|
707
|
-
* Middleware that registers the `mcp` command when the app runs on the Node CLI adapter.
|
|
708
|
-
*
|
|
709
|
-
* It mirrors the router's command registration: contribute a `MetaCommandHandler` to
|
|
710
|
-
* `stone.adapter.commands` so the CLI (itself a Stone.js app on the Node CLI adapter) discovers
|
|
711
|
-
* `stone mcp` by introspection, no hard-coding.
|
|
712
|
-
*
|
|
713
|
-
* @param context - The blueprint context.
|
|
714
|
-
* @param next - The next pipeline function.
|
|
715
|
-
* @returns The updated blueprint.
|
|
716
|
-
*/
|
|
717
|
-
const SetMcpCommandsMiddleware = async (context, next) => {
|
|
718
|
-
if (context.blueprint.get('stone.adapter.platform') === NODE_CONSOLE_PLATFORM) {
|
|
719
|
-
context.blueprint.add('stone.adapter.commands', [{ options: mcpCommandOptions, isClass: true, module: McpCommand }]);
|
|
720
|
-
}
|
|
721
|
-
return await next(context);
|
|
722
|
-
};
|
|
723
|
-
/**
|
|
724
|
-
* The blueprint middleware contributed by the MCP dev module.
|
|
725
|
-
*/
|
|
726
|
-
const metaMcpDevBlueprintMiddleware = [
|
|
727
|
-
{ module: SetMcpCommandsMiddleware, priority: 5 }
|
|
728
|
-
];
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* Opt-in blueprint: import and register it to add the `stone mcp` command.
|
|
732
|
-
*
|
|
733
|
-
* It contributes a blueprint middleware that registers the command on the Node CLI adapter. Add
|
|
734
|
-
* your own tools and the server name/instructions under `stone.mcpDev` (or via `@McpDev()` /
|
|
735
|
-
* `defineMcpDev()`).
|
|
736
|
-
*/
|
|
737
|
-
const mcpDevBlueprint = {
|
|
738
|
-
stone: {
|
|
739
|
-
blueprint: {
|
|
740
|
-
middleware: metaMcpDevBlueprintMiddleware
|
|
741
|
-
},
|
|
742
|
-
mcpDev: {
|
|
743
|
-
tools: []
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
/**
|
|
748
|
-
* Imperative helper: build an MCP dev blueprint with the given options.
|
|
749
|
-
*
|
|
750
|
-
* @param options - The MCP dev options (server name, instructions, your tools, report tools).
|
|
751
|
-
* @returns The blueprint to register in your app.
|
|
752
|
-
*/
|
|
753
|
-
function defineMcpDev(options = {}) {
|
|
754
|
-
return {
|
|
755
|
-
stone: {
|
|
756
|
-
blueprint: {
|
|
757
|
-
middleware: metaMcpDevBlueprintMiddleware
|
|
758
|
-
},
|
|
759
|
-
mcpDev: options
|
|
760
|
-
}
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* A class decorator that adds the `stone mcp` command to your app.
|
|
766
|
-
*
|
|
767
|
-
* Declarative counterpart of registering {@link mcpDevBlueprint}: apply it to your app class to
|
|
768
|
-
* expose the framework-knowledge tools (plus any you declare) to a coding agent over MCP.
|
|
769
|
-
*
|
|
770
|
-
* @param options - The MCP dev options (server name, instructions, your tools).
|
|
771
|
-
* @returns A class decorator.
|
|
772
|
-
*
|
|
773
|
-
* @example
|
|
774
|
-
* ```typescript
|
|
775
|
-
* @McpDev({ tools: [myTool] })
|
|
776
|
-
* @StoneApp({ name: 'my-app' })
|
|
777
|
-
* export class Application {}
|
|
778
|
-
* ```
|
|
779
|
-
*/
|
|
780
|
-
const McpDev = (options = {}) => {
|
|
781
|
-
return classDecoratorLegacyWrapper((target, context) => {
|
|
782
|
-
addBlueprint(target, context, mcpDevBlueprint, { stone: { mcpDev: options } });
|
|
783
|
-
});
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
export { DEFAULT_MCP_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_MCP_SERVER_VERSION, McpCommand, McpDev, McpDevError, NODE_CONSOLE_PLATFORM, SetMcpCommandsMiddleware, buildMcpServer, createIntrospectionTools, createReportTools, createStderrLogger, createToolCallback, defineMcpDev, generateLlmsFullTxt, generateLlmsTxt, getConcept, hasMcpJson, initMcpJson, knowledgeBase, mcpCommandOptions, mcpDevBlueprint, mcpServerEntry, mergeMcpJson, metaMcpDevBlueprintMiddleware, moduleName, resolveTools, sanitize, searchKnowledge, startMcpDevServer, stoneMcpTools, toToolContent };
|
|
856
|
+
export { APP_CONTEXT_FILE, DEFAULT_MCP_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_MCP_SERVER_VERSION, McpCommand, McpDevError, NODE_CONSOLE_PLATFORM, buildMcpServer, contextReader, createIntrospectionTools, createReportTools, createStderrLogger, createToolCallback, generateLlmsFullTxt, generateLlmsTxt, getConcept, hasMcpJson, initMcpJson, knowledgeBase, mcpCommandOptions, mcpServerEntry, mergeMcpJson, moduleName, publishAppContext, readAppContext, resolveSource, resolveTools, sanitize, searchKnowledge, startMcpDevServer, stoneMcpTools, toToolContent };
|
package/dist/introspection.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IBlueprint } from '@stone-js/core';
|
|
2
|
-
import { McpToolDef } from './declarations';
|
|
2
|
+
import { McpToolDef } from './declarations.js';
|
|
3
|
+
import { ContextReader } from './appContext.js';
|
|
3
4
|
/**
|
|
4
5
|
* Best-effort name of a module reference (class, function, or meta-module `{ module }`).
|
|
5
6
|
*
|
|
@@ -16,6 +17,24 @@ export declare function moduleName(value: unknown): string;
|
|
|
16
17
|
* @returns A serializable value.
|
|
17
18
|
*/
|
|
18
19
|
export declare function sanitize(value: unknown, depth?: number): unknown;
|
|
20
|
+
/**
|
|
21
|
+
* What the tools are describing, and how they know.
|
|
22
|
+
*
|
|
23
|
+
* The MCP server is a console command, so the blueprint it holds is the one a *console* boot resolves:
|
|
24
|
+
* its adapter, its response type and every platform-conditional contribution belong to a different
|
|
25
|
+
* application than the one running under `stone dev`. When the running application has published its
|
|
26
|
+
* own configuration, that is the better answer and it is used; otherwise the console boot still
|
|
27
|
+
* answers for everything platform-independent — routes, providers, the kernel handler — and says which
|
|
28
|
+
* of its answers not to trust, rather than pretending to be the running app.
|
|
29
|
+
*
|
|
30
|
+
* @param blueprint - The blueprint of the process the MCP server runs in.
|
|
31
|
+
* @param cwd - The project root.
|
|
32
|
+
* @returns The reader to introspect, and a description of it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSource(blueprint: IBlueprint, cwd?: string): {
|
|
35
|
+
source: ContextReader;
|
|
36
|
+
describes: Record<string, unknown>;
|
|
37
|
+
};
|
|
19
38
|
/**
|
|
20
39
|
* Build the read-only introspection tools bound to the app's resolved blueprint.
|
|
21
40
|
*
|
|
@@ -26,4 +45,4 @@ export declare function sanitize(value: unknown, depth?: number): unknown;
|
|
|
26
45
|
* @param blueprint - The resolved application blueprint.
|
|
27
46
|
* @returns The introspection tools.
|
|
28
47
|
*/
|
|
29
|
-
export declare function createIntrospectionTools(blueprint: IBlueprint): McpToolDef[];
|
|
48
|
+
export declare function createIntrospectionTools(blueprint: IBlueprint, cwd?: string): McpToolDef[];
|
package/dist/knowledge.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Concept, KnowledgeBase } from './declarations';
|
|
1
|
+
import { Concept, KnowledgeBase } from './declarations.js';
|
|
2
2
|
/**
|
|
3
3
|
* The single, curated, machine-readable map of Stone.js. Kept concise and accurate so an agent
|
|
4
4
|
* can consult it in real time instead of scanning every package.
|
package/dist/llms.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KnowledgeBase } from './declarations';
|
|
1
|
+
import { KnowledgeBase } from './declarations.js';
|
|
2
2
|
/**
|
|
3
3
|
* Generates the concise `llms.txt` index (the emerging standard: a short, link-friendly Markdown
|
|
4
4
|
* map an agent can read in one shot). Serve it at `/llms.txt` from the docs site.
|
package/dist/mcpJson.d.ts
CHANGED
|
@@ -9,10 +9,16 @@ export interface McpJsonIo {
|
|
|
9
9
|
/**
|
|
10
10
|
* The `.mcp.json` server entry that launches this dev server.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* It goes through `npx` because `@stone-js/cli` is a project dev dependency: a bare `stone` only
|
|
13
|
+
* resolves when the CLI is also installed globally, so the entry an agent spawns would fail with
|
|
14
|
+
* ENOENT on a normal project. `npx` resolves the project-local binary first, and still finds a
|
|
15
|
+
* global install, so the generated file works either way.
|
|
16
|
+
*
|
|
17
|
+
* @param command - The launcher command (defaults to `npx`).
|
|
18
|
+
* @param args - The launcher arguments (defaults to `stone mcp`).
|
|
13
19
|
* @returns The MCP server entry.
|
|
14
20
|
*/
|
|
15
|
-
export declare function mcpServerEntry(command?: string): {
|
|
21
|
+
export declare function mcpServerEntry(command?: string, args?: string[]): {
|
|
16
22
|
command: string;
|
|
17
23
|
args: string[];
|
|
18
24
|
};
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpToolDef, ReportToolsOptions } from './declarations';
|
|
1
|
+
import { McpToolDef, ReportToolsOptions } from './declarations.js';
|
|
2
2
|
/**
|
|
3
3
|
* The Stone.js framework-knowledge tools served by `stone mcp`. They are registered on the MCP
|
|
4
4
|
* server automatically; point your coding agent at it and it can query the framework in real time
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stone-js/mcp-dev",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"description": "Serve Stone.js's knowledge to your coding agent. A single `stone mcp` command starts an MCP server (stdio) exposing the framework's concepts, modules and best-practices plus your own tools, so the LLM masters the context while you master the domain.",
|
|
5
5
|
"author": "Mr. Stone <evensstone@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,24 +33,23 @@
|
|
|
33
33
|
"types": "./dist/index.d.ts",
|
|
34
34
|
"exports": {
|
|
35
35
|
".": {
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"default": "./dist/index.js"
|
|
43
|
-
}
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./cli": {
|
|
40
|
+
"types": "./dist/cli.d.ts",
|
|
41
|
+
"default": "./dist/cli.js"
|
|
44
42
|
}
|
|
45
43
|
},
|
|
46
44
|
"engines": {
|
|
47
45
|
"node": ">=18.17.0"
|
|
48
46
|
},
|
|
49
47
|
"peerDependencies": {
|
|
50
|
-
"@stone-js/core": "0.8.
|
|
48
|
+
"@stone-js/core": "0.8.10"
|
|
51
49
|
},
|
|
52
50
|
"dependencies": {
|
|
53
|
-
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
52
|
+
"zod": "^3.25.76"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
56
55
|
"@commitlint/cli": "^19.8.1",
|
|
@@ -70,7 +69,8 @@
|
|
|
70
69
|
"typedoc": "^0.28.6",
|
|
71
70
|
"typedoc-plugin-markdown": "^4.7.0",
|
|
72
71
|
"typescript": "^5.6.3",
|
|
73
|
-
"vitest": "^3.2.4"
|
|
72
|
+
"vitest": "^3.2.4",
|
|
73
|
+
"@stone-js/cli": "0.8.10"
|
|
74
74
|
},
|
|
75
75
|
"ts-standard": {
|
|
76
76
|
"globals": [
|
|
@@ -82,6 +82,9 @@
|
|
|
82
82
|
"beforeEach"
|
|
83
83
|
]
|
|
84
84
|
},
|
|
85
|
+
"stone": {
|
|
86
|
+
"cliPlugin": "./dist/cli.js"
|
|
87
|
+
},
|
|
85
88
|
"scripts": {
|
|
86
89
|
"lint": "ts-standard src",
|
|
87
90
|
"lint:fix": "ts-standard --fix src tests",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { McpDevOptions } from '../../declarations';
|
|
2
|
-
import { ClassType } from '@stone-js/core';
|
|
3
|
-
/**
|
|
4
|
-
* Options for the `McpDev` decorator.
|
|
5
|
-
*/
|
|
6
|
-
export interface McpDevDecoratorOptions extends McpDevOptions {
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Browser stub of `@McpDev()`: a no-op.
|
|
10
|
-
*
|
|
11
|
-
* The dev MCP server (`stone mcp`) is a Node-only, development concern. Stubbing the decorator for
|
|
12
|
-
* the browser keeps an isomorphic app compiling and inert there, without dragging the CLI command,
|
|
13
|
-
* the MCP SDK server, or `node:fs` into the browser bundle (which would break a SPA). The real
|
|
14
|
-
* decorator lives in the Node build.
|
|
15
|
-
*
|
|
16
|
-
* @param _options - Ignored in the browser.
|
|
17
|
-
* @returns A no-op class decorator.
|
|
18
|
-
*/
|
|
19
|
-
export declare const McpDev: <T extends ClassType = ClassType>(_options?: McpDevDecoratorOptions) => ClassDecorator;
|