@stone-js/mcp-dev 0.8.7 → 0.8.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.
@@ -1,5 +1,5 @@
1
1
  import { IContainer, IncomingEvent } from '@stone-js/core';
2
- import { CommandOptions } from '../declarations';
2
+ import { CommandOptions } from '../declarations.js';
3
3
  /**
4
4
  * Configuration for the `mcp` command.
5
5
  */
@@ -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
- inputSchema?: Record<string, unknown>;
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 './commands/McpCommand';
3
- export * from './constants';
4
- export * from './declarations';
5
- export * from './decorators/McpDev';
6
- export * from './errors/McpDevError';
7
- export * from './introspection';
8
- export * from './knowledge';
9
- export * from './llms';
10
- export * from './mcpJson';
11
- export * from './middleware/BlueprintMiddleware';
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, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
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
- * @param command - The launcher command (defaults to `stone`).
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: ['mcp'] };
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
- server.registerTool(tool.name, { description: tool.description, inputSchema: (tool.inputSchema ?? {}) }, createToolCallback(tool, log));
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 routes = () => blueprint.get('stone.router.definitions', []);
558
- const commands = () => blueprint.get('stone.adapter.commands', []);
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: blueprint.get('stone.name'),
565
- env: blueprint.get('stone.env'),
566
- platform: blueprint.get('stone.adapter.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: blueprint.get('stone.providers', []).length,
571
- adapters: blueprint.get('stone.adapters', []).length
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: blueprint.get('stone.adapter.platform'),
595
- adapters: blueprint.get('stone.adapters', []).map((a) => clean({
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: () => blueprint.get('stone.providers', []).map(moduleName)
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 = blueprint.get('stone.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: () => blueprint.get('stone.keyRouting.definitions', []).map((d) => clean({
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(blueprint.get('stone', {}));
714
+ return Object.keys(source.get('stone', {}));
636
715
  }
637
- return sanitize(blueprint.get(key));
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 };
@@ -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[];
@@ -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
- * @param command - The launcher command (defaults to `stone`).
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.7",
3
+ "version": "0.8.9",
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
- "browser": {
37
- "types": "./dist/index.d.ts",
38
- "default": "./dist/browser.js"
39
- },
40
- "default": {
41
- "types": "./dist/index.d.ts",
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.7"
48
+ "@stone-js/core": "0.8.9"
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.9"
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",
@@ -47,7 +47,7 @@ The same class can be served over HTTP, in a Lambda, in the browser, or on the e
47
47
  ## Workflow
48
48
 
49
49
  1. **Before writing code, query the framework.** Call the `stone_search`, `stone_concept`,
50
- `stone_modules`, and `stone_docs` MCP tools (served by `stone mcp`) to confirm the current
50
+ `stone_modules`, and `stone_brief` MCP tools (served by `stone mcp`) to confirm the current
51
51
  conventions and the right module for the job, instead of guessing from generic Node knowledge.
52
52
  2. **Inspect the app** with `stone_app`, `stone_routes`, `stone_commands`, `stone_adapters`,
53
53
  `stone_providers`, `stone_kernel`, and `stone_config` to see what it actually declares.
@@ -53,5 +53,5 @@ platforms, and the environment decides which context applies.
53
53
  ## Verify with the MCP tools
54
54
 
55
55
  Call `stone_adapters` to list registered adapters (platform, alias, default/current) and the active
56
- platform. Use `stone_app` for a quick summary, and `stone_docs` for the adapter guides. `stone_search`
56
+ platform. Use `stone_app` for a quick summary, and `stone_adapters` for what is registered. `stone_search`
57
57
  the knowledge base when picking a target or debugging selection.
@@ -57,7 +57,7 @@ class UserController {
57
57
 
58
58
  After adding routes, call `stone_routes` to confirm the resolved tree (path, methods, name,
59
59
  handler, middleware). Use `stone_kernel` to see the middleware pipeline that every route traverses.
60
- When unsure of an option, `stone_search` the knowledge base or `stone_docs` for the routing guide.
60
+ When unsure of an option, `stone_search` the knowledge base or read `stone_brief` for the full brief.
61
61
 
62
62
  ## Do not
63
63