proteum 2.2.9 → 2.3.0

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.
Files changed (45) hide show
  1. package/AGENTS.md +3 -2
  2. package/README.md +49 -11
  3. package/agents/project/AGENTS.md +43 -5
  4. package/agents/project/diagnostics.md +6 -2
  5. package/agents/project/optimizations.md +1 -0
  6. package/agents/project/root/AGENTS.md +14 -5
  7. package/agents/project/tests/AGENTS.md +6 -0
  8. package/agents/project/tests/e2e/AGENTS.md +13 -0
  9. package/agents/project/tests/e2e/REAL_WORLD_JOURNEY_TESTS.md +192 -0
  10. package/cli/commands/connect.ts +40 -4
  11. package/cli/commands/diagnose.ts +136 -5
  12. package/cli/commands/doctor.ts +24 -4
  13. package/cli/commands/explain.ts +105 -6
  14. package/cli/commands/mcp.ts +16 -0
  15. package/cli/commands/orient.ts +66 -3
  16. package/cli/commands/perf.ts +118 -13
  17. package/cli/commands/runtime.ts +151 -0
  18. package/cli/commands/trace.ts +116 -21
  19. package/cli/mcp/provider.ts +365 -0
  20. package/cli/mcp/stdio.ts +16 -0
  21. package/cli/presentation/commands.ts +77 -20
  22. package/cli/presentation/devSession.ts +2 -0
  23. package/cli/runtime/commands.ts +95 -12
  24. package/cli/utils/agentOutput.ts +46 -0
  25. package/cli/utils/agents.ts +116 -49
  26. package/common/dev/inspection.ts +14 -6
  27. package/common/dev/mcpPayloads.ts +736 -0
  28. package/common/dev/mcpServer.ts +254 -0
  29. package/docs/agent-routing.md +126 -0
  30. package/docs/dev-commands.md +2 -0
  31. package/docs/dev-sessions.md +2 -1
  32. package/docs/diagnostics.md +68 -23
  33. package/docs/mcp.md +149 -0
  34. package/docs/migrate-from-2.1.3.md +15 -5
  35. package/docs/request-tracing.md +12 -6
  36. package/package.json +2 -1
  37. package/server/app/devMcp.ts +159 -0
  38. package/server/services/router/http/cache.ts +116 -0
  39. package/server/services/router/http/index.ts +94 -35
  40. package/server/services/router/index.ts +8 -11
  41. package/tests/agents-utils.test.cjs +36 -13
  42. package/tests/dev-transpile-watch.test.cjs +117 -8
  43. package/tests/inspection.test.cjs +67 -0
  44. package/tests/mcp.test.cjs +127 -0
  45. package/tests/router-cache-config.test.cjs +74 -0
@@ -0,0 +1,365 @@
1
+ import fs from 'fs-extra';
2
+ import got from 'got';
3
+ import path from 'path';
4
+
5
+ import { buildContractsDoctorResponse } from '../../common/dev/contractsDoctor';
6
+ import { buildDoctorResponse, type TDoctorResponse } from '../../common/dev/diagnostics';
7
+ import { buildOrientationResponse, explainOwner } from '../../common/dev/inspection';
8
+ import {
9
+ buildRuntimeStatusPayload,
10
+ compactDiagnoseResponse,
11
+ compactDoctorResponse,
12
+ compactExplainSummary,
13
+ compactLogsResponse,
14
+ compactOrientationResponse,
15
+ compactPerfRequestResponse,
16
+ compactPerfTopResponse,
17
+ compactTraceResponse,
18
+ resolveInstructionRouting,
19
+ } from '../../common/dev/mcpPayloads';
20
+ import type { TProteumMcpProvider } from '../../common/dev/mcpServer';
21
+ import type { TDevConsoleLogLevel, TDevConsoleLogsResponse } from '../../common/dev/console';
22
+ import type { TDiagnoseResponse } from '../../common/dev/inspection';
23
+ import type { TPerfRequestResponse, TPerfTopResponse } from '../../common/dev/performance';
24
+ import type { TProteumManifest } from '../../common/dev/proteumManifest';
25
+ import type { TRequestTraceResponse } from '../../common/dev/requestTrace';
26
+ import { readProteumManifest } from '../compiler/common/proteumManifest';
27
+ import { listDevSessionInspections, type TDevSessionInspection } from '../runtime/devSessions';
28
+
29
+ type TCliProteumMcpProviderArgs = {
30
+ appRoot: string;
31
+ sessionFilePath?: string;
32
+ url?: string;
33
+ };
34
+
35
+ type TRequestOptions = {
36
+ method?: 'GET' | 'POST';
37
+ searchParams?: Record<string, string>;
38
+ };
39
+
40
+ const normalizeBaseUrl = (value: string) => value.replace(/\/+$/, '');
41
+ const dedupe = <TValue>(values: TValue[]) => [...new Set(values)];
42
+
43
+ const buildBaseUrlCandidates = (value: string) => {
44
+ const normalized = normalizeBaseUrl(value);
45
+
46
+ try {
47
+ const parsed = new URL(normalized);
48
+ const port = parsed.port;
49
+ const pathname = parsed.pathname === '/' ? '' : parsed.pathname;
50
+ const search = parsed.search;
51
+ const hash = parsed.hash;
52
+ const buildUrl = (hostname: string) => `${parsed.protocol}//${hostname}${port ? `:${port}` : ''}${pathname}${search}${hash}`;
53
+
54
+ if (parsed.hostname === '127.0.0.1') return dedupe([normalized, buildUrl('localhost'), buildUrl('[::1]')]);
55
+ if (parsed.hostname === 'localhost') return dedupe([normalized, buildUrl('127.0.0.1'), buildUrl('[::1]')]);
56
+ if (parsed.hostname === '[::1]' || parsed.hostname === '::1') return dedupe([normalized, buildUrl('localhost'), buildUrl('127.0.0.1')]);
57
+ } catch (_error) {}
58
+
59
+ return [normalized];
60
+ };
61
+
62
+ const compactSession = (inspection: TDevSessionInspection) => ({
63
+ sessionFilePath: inspection.sessionFilePath,
64
+ live: inspection.live,
65
+ stale: inspection.stale,
66
+ invalid: inspection.invalid,
67
+ parseError: inspection.parseError,
68
+ pid: inspection.record?.pid,
69
+ routerPort: inspection.record?.routerPort,
70
+ publicUrl: inspection.record?.publicUrl,
71
+ state: inspection.record?.state,
72
+ startedAt: inspection.record?.startedAt,
73
+ updatedAt: inspection.record?.updatedAt,
74
+ });
75
+
76
+ const getSessionUrl = (inspection: TDevSessionInspection) => {
77
+ if (!inspection.record) return '';
78
+ if (inspection.record.publicUrl) return inspection.record.publicUrl.replace(/\/+$/, '');
79
+ return `http://localhost:${inspection.record.routerPort}`;
80
+ };
81
+
82
+ export class CliProteumMcpProvider implements TProteumMcpProvider {
83
+ private sessionsPromise?: Promise<TDevSessionInspection[]>;
84
+
85
+ public constructor(private args: TCliProteumMcpProviderArgs) {}
86
+
87
+ private readManifestIfAvailable() {
88
+ const manifestFilepath = path.join(this.args.appRoot, '.proteum', 'manifest.json');
89
+ if (!fs.existsSync(manifestFilepath)) return undefined;
90
+
91
+ try {
92
+ return readProteumManifest(this.args.appRoot);
93
+ } catch (_error) {
94
+ return undefined;
95
+ }
96
+ }
97
+
98
+ private readLocalManifest() {
99
+ const manifest = this.readManifestIfAvailable();
100
+ if (!manifest) {
101
+ throw new Error(
102
+ `Proteum manifest was not found in ${this.args.appRoot}. Run \`proteum refresh\`, \`proteum dev\`, or pass --url for a running dev server.`,
103
+ );
104
+ }
105
+
106
+ return manifest;
107
+ }
108
+
109
+ private async readSessions() {
110
+ this.sessionsPromise ??= listDevSessionInspections({
111
+ appRoot: this.args.appRoot,
112
+ sessionFilePath: this.args.sessionFilePath,
113
+ });
114
+ return await this.sessionsPromise;
115
+ }
116
+
117
+ private async selectSession() {
118
+ const sessions = await this.readSessions();
119
+ const liveSessions = sessions.filter((inspection) => inspection.live && inspection.record);
120
+
121
+ return (
122
+ liveSessions.find((inspection) => inspection.record?.state === 'ready') ||
123
+ liveSessions[0] ||
124
+ sessions.find((inspection) => inspection.record)
125
+ );
126
+ }
127
+
128
+ private async getBaseUrlCandidates() {
129
+ if (this.args.url?.trim()) return buildBaseUrlCandidates(this.args.url.trim());
130
+
131
+ const selectedSession = await this.selectSession();
132
+ const selectedBaseUrl = selectedSession ? getSessionUrl(selectedSession) : '';
133
+ if (selectedBaseUrl) return buildBaseUrlCandidates(selectedBaseUrl);
134
+
135
+ const manifest = this.readManifestIfAvailable();
136
+ const routerPort = manifest?.env.resolved.routerPort;
137
+ if (typeof routerPort === 'number' && routerPort > 0) {
138
+ return dedupe([`http://localhost:${routerPort}`, `http://127.0.0.1:${routerPort}`, `http://[::1]:${routerPort}`]);
139
+ }
140
+
141
+ return [];
142
+ }
143
+
144
+ private async requestJson<TResponse>(pathname: string, options: TRequestOptions = {}) {
145
+ const attempts: string[] = [];
146
+ const baseUrls = await this.getBaseUrlCandidates();
147
+
148
+ for (const baseUrl of baseUrls) {
149
+ const url = `${baseUrl}${pathname}${options.searchParams ? `?${new URLSearchParams(options.searchParams).toString()}` : ''}`;
150
+
151
+ try {
152
+ const response = await got(url, {
153
+ method: options.method || 'GET',
154
+ responseType: 'json',
155
+ retry: { limit: 0 },
156
+ throwHttpErrors: false,
157
+ timeout: { request: 2_500 },
158
+ });
159
+
160
+ if (response.statusCode >= 400) {
161
+ const body = response.body as { error?: string } | undefined;
162
+ throw new Error(body?.error || `Proteum dev endpoint returned HTTP ${response.statusCode}.`);
163
+ }
164
+
165
+ return { baseUrl, body: response.body as TResponse };
166
+ } catch (error) {
167
+ attempts.push(`${url}: ${error instanceof Error ? error.message : String(error)}`);
168
+ }
169
+ }
170
+
171
+ throw new Error(
172
+ [
173
+ 'Could not reach a Proteum dev MCP data source.',
174
+ ...attempts.map((attempt) => `- ${attempt}`),
175
+ 'Start `proteum dev`, pass --url, or use tools that can read the local manifest from disk.',
176
+ ].join('\n'),
177
+ );
178
+ }
179
+
180
+ private async readManifestPreferRuntime() {
181
+ if (this.args.url?.trim()) {
182
+ return (await this.requestJson<TProteumManifest>('/__proteum/explain')).body;
183
+ }
184
+
185
+ const localManifest = this.readManifestIfAvailable();
186
+ if (localManifest) return localManifest;
187
+
188
+ return (await this.requestJson<TProteumManifest>('/__proteum/explain')).body;
189
+ }
190
+
191
+ private async probeRuntimeHealth() {
192
+ try {
193
+ const response = await this.requestJson<TDoctorResponse>('/__proteum/doctor');
194
+ return {
195
+ reachable: true,
196
+ baseUrl: response.baseUrl,
197
+ doctor: response.body.summary,
198
+ };
199
+ } catch (error) {
200
+ return {
201
+ reachable: false,
202
+ error: error instanceof Error ? error.message : String(error),
203
+ };
204
+ }
205
+ }
206
+
207
+ public async runtimeStatus(_input: Record<string, never> = {}) {
208
+ const manifest = this.readManifestIfAvailable();
209
+ const sessions = await this.readSessions();
210
+ const selectedSession = await this.selectSession();
211
+ const health = await this.probeRuntimeHealth();
212
+ const runtime =
213
+ health.reachable && 'baseUrl' in health
214
+ ? {
215
+ publicUrl: health.baseUrl,
216
+ routerPort: selectedSession?.record?.routerPort || manifest?.env.resolved.routerPort,
217
+ source: this.args.url?.trim() ? 'explicit-url' : selectedSession?.record ? 'tracked-session' : 'manifest-port',
218
+ session: selectedSession ? compactSession(selectedSession) : undefined,
219
+ mcpUrl: `${health.baseUrl}/__proteum/mcp`,
220
+ }
221
+ : selectedSession
222
+ ? {
223
+ routerPort: selectedSession.record?.routerPort,
224
+ publicUrl: selectedSession.record?.publicUrl,
225
+ source: 'tracked-session',
226
+ session: compactSession(selectedSession),
227
+ }
228
+ : undefined;
229
+
230
+ return buildRuntimeStatusPayload({
231
+ appRoot: this.args.appRoot,
232
+ health,
233
+ manifest,
234
+ runtime,
235
+ sessions: sessions.map(compactSession),
236
+ });
237
+ }
238
+
239
+ public async orient({ query }: { query: string }) {
240
+ return compactOrientationResponse(buildOrientationResponse(await this.readManifestPreferRuntime(), query));
241
+ }
242
+
243
+ public async instructionsResolve({ query }: { query?: string }) {
244
+ return resolveInstructionRouting({ appRoot: this.args.appRoot, query });
245
+ }
246
+
247
+ public async explainSummary({ query }: { query?: string }) {
248
+ const manifest = await this.readManifestPreferRuntime();
249
+ const normalizedQuery = query?.trim();
250
+
251
+ return compactExplainSummary({
252
+ manifest,
253
+ owner: normalizedQuery ? explainOwner(manifest, normalizedQuery) : undefined,
254
+ query: normalizedQuery,
255
+ });
256
+ }
257
+
258
+ public async doctor({ contracts = true }: { contracts?: boolean }) {
259
+ if (this.args.url?.trim()) {
260
+ const doctor = (await this.requestJson<TDoctorResponse>('/__proteum/doctor')).body;
261
+ const contractDoctor = contracts
262
+ ? (await this.requestJson<TDoctorResponse>('/__proteum/doctor/contracts')).body
263
+ : undefined;
264
+ return compactDoctorResponse({ contracts: contractDoctor, doctor });
265
+ }
266
+
267
+ const manifest = this.readManifestIfAvailable();
268
+ if (manifest) {
269
+ return compactDoctorResponse({
270
+ contracts: contracts ? buildContractsDoctorResponse(manifest) : undefined,
271
+ doctor: buildDoctorResponse(manifest),
272
+ });
273
+ }
274
+
275
+ const doctor = (await this.requestJson<TDoctorResponse>('/__proteum/doctor')).body;
276
+ const contractDoctor = contracts
277
+ ? (await this.requestJson<TDoctorResponse>('/__proteum/doctor/contracts')).body
278
+ : undefined;
279
+ return compactDoctorResponse({ contracts: contractDoctor, doctor });
280
+ }
281
+
282
+ public async diagnose(input: {
283
+ logsLevel?: TDevConsoleLogLevel;
284
+ logsLimit?: number;
285
+ path?: string;
286
+ query?: string;
287
+ requestId?: string;
288
+ }) {
289
+ const searchParams: Record<string, string> = {};
290
+ if (input.logsLevel) searchParams.logsLevel = input.logsLevel;
291
+ if (typeof input.logsLimit === 'number') searchParams.logsLimit = String(input.logsLimit);
292
+ if (input.path) searchParams.path = input.path;
293
+ if (input.query) searchParams.query = input.query;
294
+ if (input.requestId) searchParams.requestId = input.requestId;
295
+
296
+ return compactDiagnoseResponse((await this.requestJson<TDiagnoseResponse>('/__proteum/diagnose', { searchParams })).body);
297
+ }
298
+
299
+ public async traceLatest(input: { detail?: 'compact' | 'full'; limit?: number; offset?: number }) {
300
+ const response = (await this.requestJson<TRequestTraceResponse>('/__proteum/trace/latest')).body;
301
+ return compactTraceResponse({
302
+ detail: input.detail,
303
+ limit: input.limit,
304
+ offset: input.offset,
305
+ request: response.request,
306
+ });
307
+ }
308
+
309
+ public async traceShow(input: { detail?: 'compact' | 'full'; limit?: number; offset?: number; requestId: string }) {
310
+ const response = (await this.requestJson<TRequestTraceResponse>(`/__proteum/trace/requests/${encodeURIComponent(input.requestId)}`))
311
+ .body;
312
+ return compactTraceResponse({
313
+ detail: input.detail,
314
+ limit: input.limit,
315
+ offset: input.offset,
316
+ request: response.request,
317
+ });
318
+ }
319
+
320
+ public async perfTop(input: { groupBy?: 'path' | 'route' | 'controller'; limit?: number; since?: string }) {
321
+ return compactPerfTopResponse(
322
+ (
323
+ await this.requestJson<TPerfTopResponse>('/__proteum/perf/top', {
324
+ searchParams: {
325
+ groupBy: input.groupBy || 'path',
326
+ limit: String(input.limit || 12),
327
+ since: input.since || 'today',
328
+ },
329
+ })
330
+ ).body,
331
+ );
332
+ }
333
+
334
+ public async perfRequest({ query }: { query: string }) {
335
+ return compactPerfRequestResponse(
336
+ (
337
+ await this.requestJson<TPerfRequestResponse>('/__proteum/perf/request', {
338
+ searchParams: { query },
339
+ })
340
+ ).body,
341
+ );
342
+ }
343
+
344
+ public async logsTail({ level = 'warn', limit = 40 }: { level?: TDevConsoleLogLevel; limit?: number }) {
345
+ return compactLogsResponse({
346
+ level,
347
+ limit,
348
+ response: (
349
+ await this.requestJson<TDevConsoleLogsResponse>('/__proteum/logs', {
350
+ searchParams: { level, limit: String(limit) },
351
+ })
352
+ ).body,
353
+ });
354
+ }
355
+
356
+ public async readResource(uri: string) {
357
+ if (uri === 'proteum://runtime/status') return await this.runtimeStatus({});
358
+ if (uri === 'proteum://instructions/router') return await this.instructionsResolve({});
359
+ if (uri === 'proteum://manifest/summary') return await this.explainSummary({});
360
+ if (uri === 'proteum://trace/latest/summary') return await this.traceLatest({});
361
+ if (uri === 'proteum://perf/top') return await this.perfTop({});
362
+
363
+ throw new Error(`Unknown Proteum MCP resource: ${uri}`);
364
+ }
365
+ }
@@ -0,0 +1,16 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+
3
+ import { createProteumMcpServer, type TProteumMcpProvider } from '../../common/dev/mcpServer';
4
+
5
+ export const startProteumMcpStdioServer = async ({
6
+ provider,
7
+ version,
8
+ }: {
9
+ provider: TProteumMcpProvider;
10
+ version: string;
11
+ }) => {
12
+ const server = createProteumMcpServer({ provider, version });
13
+ const transport = new StdioServerTransport();
14
+
15
+ await server.connect(transport);
16
+ };
@@ -21,6 +21,8 @@ export const proteumCommandNames = [
21
21
  'orient',
22
22
  'diagnose',
23
23
  'perf',
24
+ 'runtime',
25
+ 'mcp',
24
26
  'trace',
25
27
  'command',
26
28
  'session',
@@ -49,7 +51,7 @@ export type TProteumCommandDoc = {
49
51
 
50
52
  export const proteumRecommendedFlow: TRow[] = [
51
53
  { label: '1. proteum orient <query>', value: 'Start here for multi-repo, generated, or connected work before reading code.' },
52
- { label: '2. proteum dev', value: 'Start the compiler, SSR server, and hot reload loop.' },
54
+ { label: '2. proteum runtime status', value: 'Reuse an existing tracked dev session before starting a new one.' },
53
55
  { label: '3. proteum diagnose <path> --hit <path>', value: 'Validate the smallest trustworthy request surface before broader checks.' },
54
56
  { label: '4. proteum check', value: 'Refresh, typecheck, and lint before you commit or push.' },
55
57
  ];
@@ -57,7 +59,7 @@ export const proteumRecommendedFlow: TRow[] = [
57
59
  export const proteumCommandGroups: Array<{ title: string; names: TProteumCommandName[] }> = [
58
60
  { title: 'Daily workflow', names: ['dev', 'refresh', 'build'] },
59
61
  { title: 'Quality gates', names: ['typecheck', 'lint', 'check', 'e2e'] },
60
- { title: 'Manifest and contracts', names: ['connect', 'doctor', 'explain', 'orient', 'diagnose', 'perf', 'trace', 'command', 'session', 'verify'] },
62
+ { title: 'Manifest and contracts', names: ['connect', 'doctor', 'explain', 'orient', 'diagnose', 'perf', 'runtime', 'mcp', 'trace', 'command', 'session', 'verify'] },
61
63
  { title: 'Project scaffolding', names: ['init', 'configure', 'create', 'migrate'] },
62
64
  ];
63
65
 
@@ -310,16 +312,17 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
310
312
  name: 'connect',
311
313
  category: 'Manifest and contracts',
312
314
  summary: 'Inspect connected-project config, cached contracts, and imported controllers.',
313
- usage: 'proteum connect [--controllers] [--json] [--strict]',
315
+ usage: 'proteum connect [--controllers] [--full|--human] [--strict]',
314
316
  bestFor:
315
317
  'Auditing the current app connect setup without manually stitching together refresh, explain, env inspection, and contract checks.',
316
318
  examples: [
317
- { description: 'Print a human-readable connected-project summary', command: 'proteum connect' },
319
+ { description: 'Print a compact connected-project summary', command: 'proteum connect' },
318
320
  { description: 'Include imported connected controllers', command: 'proteum connect --controllers' },
319
- { description: 'Emit machine-readable connect output', command: 'proteum connect --json' },
321
+ { description: 'Emit the full connect payload', command: 'proteum connect --full' },
320
322
  { description: 'Fail when connect diagnostics exist', command: 'proteum connect --strict' },
321
323
  ],
322
324
  notes: [
325
+ 'Default output is compact `proteum-agent-v1` JSON.',
323
326
  'Proteum refreshes generated typings before reading the connect manifest state.',
324
327
  'This command inspects explicit `proteum.config.ts` connected sources and URLs, cached `.proteum/connected/*.json` files, and imported connected controllers.',
325
328
  '`--strict` is intended for CI or framework validation when connected contracts must be present and usable.',
@@ -330,27 +333,31 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
330
333
  name: 'doctor',
331
334
  category: 'Manifest and contracts',
332
335
  summary: 'Inspect the generated Proteum manifest diagnostics.',
333
- usage: 'proteum doctor [--contracts] [--json] [--strict]',
336
+ usage: 'proteum doctor [--contracts] [--full|--human] [--strict]',
334
337
  bestFor:
335
338
  'Auditing manifest warnings and errors, especially in CI or when route/controller generation behaves unexpectedly.',
336
339
  examples: [
337
- { description: 'Print a human-readable diagnostic summary', command: 'proteum doctor' },
340
+ { description: 'Print a compact diagnostic summary', command: 'proteum doctor' },
338
341
  { description: 'Inspect missing generated contracts and source files', command: 'proteum doctor --contracts' },
339
342
  { description: 'Fail if any diagnostics exist', command: 'proteum doctor --strict' },
340
- { description: 'Emit machine-readable diagnostics', command: 'proteum doctor --json' },
343
+ { description: 'Emit the full diagnostic payload', command: 'proteum doctor --full' },
344
+ ],
345
+ notes: [
346
+ 'Default output is compact `proteum-agent-v1` JSON.',
347
+ '`--strict` is intended for CI and pre-release verification.',
348
+ '`--contracts` checks manifest-owned source files and expected generated artifacts on disk.',
341
349
  ],
342
- notes: ['`--strict` is intended for CI and pre-release verification.', '`--contracts` checks manifest-owned source files and expected generated artifacts on disk.'],
343
350
  status: 'stable',
344
351
  },
345
352
  explain: {
346
353
  name: 'explain',
347
354
  category: 'Manifest and contracts',
348
355
  summary: 'Explain the generated Proteum manifest.',
349
- usage: 'proteum explain [owner <query>] [--all|--app|--conventions|--env|--connected|--services|--controllers|--commands|--routes|--layouts|--diagnostics] [--json]',
356
+ usage: 'proteum explain [owner <query>] [--manifest|--full|--human|--all|--app|--conventions|--env|--connected|--services|--controllers|--commands|--routes|--layouts|--diagnostics]',
350
357
  bestFor:
351
- 'Inspecting how source files became generated routes, controllers, commands, layouts, services, and diagnostics without reading compiler internals.',
358
+ 'Inspecting the compact generated-app summary first, then opening selected manifest sections only when needed.',
352
359
  examples: [
353
- { description: 'Show the default human summary', command: 'proteum explain' },
360
+ { description: 'Show the default compact agent summary', command: 'proteum explain' },
354
361
  {
355
362
  description: 'Inspect generated routes, controllers, and commands together',
356
363
  command: 'proteum explain --routes --controllers --commands',
@@ -360,9 +367,11 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
360
367
  command: 'proteum explain --connected --controllers',
361
368
  },
362
369
  { description: 'Resolve the most likely manifest owner for a path or file', command: 'proteum explain owner /api/Auth/CurrentUser' },
363
- { description: 'Emit the selected manifest sections as JSON', command: 'proteum explain --routes --json' },
370
+ { description: 'Emit the full manifest only when needed', command: 'proteum explain --manifest' },
364
371
  ],
365
372
  notes: [
373
+ 'Default output is compact `proteum-agent-v1` JSON because the CLI is optimized for agents.',
374
+ '`--full`, `--manifest`, or explicit section flags are the escape hatch for large details.',
366
375
  'Legacy positional section selection remains supported, for example `proteum explain routes services`.',
367
376
  '`proteum explain owner <query>` ranks matching routes, controllers, services, commands, layouts, and diagnostics from the manifest.',
368
377
  'Connected projects are emitted from explicit `proteum.config.ts` `connect.<Namespace>.*` values plus the resolved connected contract.',
@@ -373,17 +382,17 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
373
382
  name: 'orient',
374
383
  category: 'Manifest and contracts',
375
384
  summary: 'Resolve owners, guidance files, connected boundaries, and next steps before opening code.',
376
- usage: 'proteum orient <query> [--port <port>|--url <baseUrl>] [--json]',
385
+ usage: 'proteum orient <query> [--port <port>|--url <baseUrl>] [--full|--human]',
377
386
  bestFor:
378
387
  'Starting multi-repo, generated-artifact, or connected-project work with one explicit orientation step instead of guessing the first files to read.',
379
388
  examples: [
380
389
  { description: 'Orient around a generated controller path', command: 'proteum orient /api/Auth/CurrentUser' },
381
390
  { description: 'Orient around a connected namespace or route', command: 'proteum orient Product.Stats.general' },
382
- { description: 'Use a running dev server when the local manifest is unavailable', command: 'proteum orient /domains --port 3101 --json' },
391
+ { description: 'Use a running dev server when the local manifest is unavailable', command: 'proteum orient /domains --port 3101' },
383
392
  ],
384
393
  notes: [
385
- 'This command combines manifest owner lookup, local or fallback guidance resolution, connected-boundary hints, and three recommended next commands.',
386
- 'Use it before reading source when the query might map to generated code, connected imports, or framework-owned files.',
394
+ 'Default output is compact `proteum-agent-v1` JSON with `mustRead`, conditional docs, owner matches, and next commands.',
395
+ 'Use it before reading source when the query might map to generated code, connected imports, framework-owned files, or area instructions.',
387
396
  'When `--port` or `--url` is provided, Proteum can read the manifest from a running dev server instead of only from disk.',
388
397
  ],
389
398
  status: 'experimental',
@@ -392,7 +401,7 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
392
401
  name: 'diagnose',
393
402
  category: 'Manifest and contracts',
394
403
  summary: 'Combine owner lookup, doctor output, contract checks, traces, and server logs into one report.',
395
- usage: 'proteum diagnose [<query>] [--hit <path>] [--method <verb>] [--data-json <json>] [--session-email <email>] [--session-role <role>] [--port <port>|--url <baseUrl>] [--json]',
404
+ usage: 'proteum diagnose [<query>] [--hit <path>] [--method <verb>] [--data-json <json>] [--session-email <email>] [--session-role <role>] [--port <port>|--url <baseUrl>] [--full|--human]',
396
405
  bestFor:
397
406
  'Collapsing the usual explain + doctor + trace + session + server log loop into one structured debugging pass.',
398
407
  examples: [
@@ -401,6 +410,8 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
401
410
  { description: 'Diagnose an API call with a JSON payload', command: 'proteum diagnose /api/Auth/CurrentUser --hit /api/Auth/CurrentUser --method POST --data-json "{}"' },
402
411
  ],
403
412
  notes: [
413
+ 'Default output is compact `proteum-agent-v1` JSON and omits raw request events, payloads, and SQL text.',
414
+ 'Use `--full` only when the compact response says lower-level detail is required.',
404
415
  'This command talks to the running app over the dev-only diagnostics, trace, and session endpoints.',
405
416
  'When `--hit` is omitted, Proteum diagnoses the latest matching request trace if one already exists.',
406
417
  ],
@@ -410,7 +421,7 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
410
421
  name: 'perf',
411
422
  category: 'Manifest and contracts',
412
423
  summary: 'Inspect shared performance rollups built from live request traces on a running Proteum dev server.',
413
- usage: 'proteum perf [top|request <requestId|path>|compare|memory] [--since <window>] [--baseline <window>] [--target <window>] [--group-by <path|route|controller>] [--limit <n>] [--port <port>|--url <baseUrl>] [--json]',
424
+ usage: 'proteum perf [top|request <requestId|path>|compare|memory] [--since <window>] [--baseline <window>] [--target <window>] [--group-by <path|route|controller>] [--limit <n>] [--port <port>|--url <baseUrl>] [--full|--human]',
414
425
  bestFor:
415
426
  'Finding the routes or controllers with the biggest response-time, CPU, SQL, render, and memory impact without manually stitching traces together.',
416
427
  examples: [
@@ -426,17 +437,61 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
426
437
  },
427
438
  ],
428
439
  notes: [
440
+ 'Default output is compact `proteum-agent-v1` JSON with capped rows and next commands.',
429
441
  'Perf data is derived from the same dev-only request trace buffer used by `proteum trace` and the profiler.',
430
442
  'Window values accept `1h`, `6h`, `24h`, `today`, `yesterday`, or an ISO timestamp.',
431
443
  'Older traces captured before the perf runtime metrics were added may not include CPU or memory deltas.',
432
444
  ],
433
445
  status: 'experimental',
434
446
  },
447
+ runtime: {
448
+ name: 'runtime',
449
+ category: 'Manifest and contracts',
450
+ summary: 'Inspect the current app manifest, tracked dev sessions, and runtime health in one compact response.',
451
+ usage: 'proteum runtime status [--session-file <path>] [--full]',
452
+ bestFor:
453
+ 'Reusing a live dev session and avoiding repeated dev-list, manifest, and health-check commands before request diagnostics.',
454
+ examples: [
455
+ { description: 'Resolve the current runtime status', command: 'proteum runtime status' },
456
+ { description: 'Inspect one explicit session file', command: 'proteum runtime status --session-file var/run/proteum/dev/agents/task.json' },
457
+ ],
458
+ notes: [
459
+ 'Default output is compact `proteum-agent-v1` JSON with the selected session, health, and next command.',
460
+ 'Use `--full` to include every tracked session field.',
461
+ ],
462
+ status: 'experimental',
463
+ },
464
+ mcp: {
465
+ name: 'mcp',
466
+ category: 'Manifest and contracts',
467
+ summary: 'Start a read-only Proteum MCP server for compact agent diagnostics and runtime data.',
468
+ usage: 'proteum mcp [--cwd <path>] [--url <baseUrl>] [--session-file <path>]',
469
+ bestFor:
470
+ 'Agent integrations that need repeated low-token access to Proteum manifest, instruction routing, runtime status, trace, perf, diagnose, and log summaries.',
471
+ examples: [
472
+ { description: 'Start the MCP server for the current app over stdio', command: 'proteum mcp' },
473
+ {
474
+ description: 'Point the MCP server at a running dev server',
475
+ command: 'proteum mcp --url http://localhost:3101',
476
+ },
477
+ {
478
+ description: 'Resolve runtime data from an explicit tracked session file',
479
+ command: 'proteum mcp --session-file var/run/proteum/dev/agents/task.json',
480
+ },
481
+ ],
482
+ notes: [
483
+ '`proteum mcp` is read-only in v1 and does not start/stop dev servers, refresh generated code, write files, or mutate traces.',
484
+ 'Tool and resource payloads are compact single-line `proteum-mcp-v1` JSON for low-token agent reads.',
485
+ 'Use the CLI for reproducible build/dev/check workflows; use MCP for repeated agent reads and progressive detail loading.',
486
+ 'A running `proteum dev` server also exposes the same tool contract at `/__proteum/mcp` for runtime-adjacent access.',
487
+ ],
488
+ status: 'experimental',
489
+ },
435
490
  trace: {
436
491
  name: 'trace',
437
492
  category: 'Manifest and contracts',
438
493
  summary: 'Inspect live in-memory request traces from a running Proteum dev server.',
439
- usage: 'proteum trace [latest|show <requestId>|requests|arm|export <requestId>] [--port <port>|--url <baseUrl>] [--json]',
494
+ usage: 'proteum trace [latest|show <requestId>|requests|arm|export <requestId>] [--port <port>|--url <baseUrl>] [--events|--full|--human]',
440
495
  bestFor:
441
496
  'Debugging route resolution, context creation, SSR payloads, renders, and runtime errors without attaching a debugger.',
442
497
  examples: [
@@ -447,6 +502,8 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
447
502
  { description: 'Target a custom dev base URL directly', command: 'proteum trace latest --url http://127.0.0.1:3010' },
448
503
  ],
449
504
  notes: [
505
+ 'Default output is compact `proteum-agent-v1` JSON with counts, errors, hot calls, and hot SQL only.',
506
+ 'Use `--events` or `--full` to print the raw event stream, payload summaries, and SQL text.',
450
507
  'This command talks to the running app over the dev-only `__proteum/trace` HTTP endpoints.',
451
508
  'Traces are stored in a bounded in-memory buffer with payload summarization and sensitive-field redaction.',
452
509
  'Use `--port` when the app is not running on the router port declared in `PORT`, or `--url` when the host itself is non-standard.',
@@ -35,6 +35,7 @@ export const renderDevSession = async ({
35
35
  { label: 'root', value: appRoot },
36
36
  { label: 'router', value: `http://localhost:${routerPort}` },
37
37
  { label: 'hmr', value: `http://localhost:${devEventPort}/__proteum_hmr` },
38
+ { label: 'mcp', value: `http://localhost:${routerPort}/__proteum/mcp` },
38
39
  ...(connectedProjects && connectedProjects.length > 0
39
40
  ? connectedProjects.map((connectedProject) => ({
40
41
  label: `connect ${connectedProject.namespace}`,
@@ -91,6 +92,7 @@ export const renderServerReadyBanner = async ({
91
92
  createElement(Text, { dimColor: true }, `Diagnose /: proteum diagnose / --port ${routerPort}`),
92
93
  createElement(Text, { dimColor: true }, `Perf top: proteum perf top --port ${routerPort}`),
93
94
  createElement(Text, { dimColor: true }, `Trace latest: proteum trace latest --port ${routerPort}`),
95
+ createElement(Text, { dimColor: true }, `MCP: ${publicUrl}/__proteum/mcp`),
94
96
  );
95
97
  });
96
98