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.
- package/AGENTS.md +3 -2
- package/README.md +49 -11
- package/agents/project/AGENTS.md +43 -5
- package/agents/project/diagnostics.md +6 -2
- package/agents/project/optimizations.md +1 -0
- package/agents/project/root/AGENTS.md +14 -5
- package/agents/project/tests/AGENTS.md +6 -0
- package/agents/project/tests/e2e/AGENTS.md +13 -0
- package/agents/project/tests/e2e/REAL_WORLD_JOURNEY_TESTS.md +192 -0
- package/cli/commands/connect.ts +40 -4
- package/cli/commands/diagnose.ts +136 -5
- package/cli/commands/doctor.ts +24 -4
- package/cli/commands/explain.ts +105 -6
- package/cli/commands/mcp.ts +16 -0
- package/cli/commands/orient.ts +66 -3
- package/cli/commands/perf.ts +118 -13
- package/cli/commands/runtime.ts +151 -0
- package/cli/commands/trace.ts +116 -21
- package/cli/mcp/provider.ts +365 -0
- package/cli/mcp/stdio.ts +16 -0
- package/cli/presentation/commands.ts +77 -20
- package/cli/presentation/devSession.ts +2 -0
- package/cli/runtime/commands.ts +95 -12
- package/cli/utils/agentOutput.ts +46 -0
- package/cli/utils/agents.ts +116 -49
- package/common/dev/inspection.ts +14 -6
- package/common/dev/mcpPayloads.ts +736 -0
- package/common/dev/mcpServer.ts +254 -0
- package/docs/agent-routing.md +126 -0
- package/docs/dev-commands.md +2 -0
- package/docs/dev-sessions.md +2 -1
- package/docs/diagnostics.md +68 -23
- package/docs/mcp.md +149 -0
- package/docs/migrate-from-2.1.3.md +15 -5
- package/docs/request-tracing.md +12 -6
- package/package.json +2 -1
- package/server/app/devMcp.ts +159 -0
- package/server/services/router/http/cache.ts +116 -0
- package/server/services/router/http/index.ts +94 -35
- package/server/services/router/index.ts +8 -11
- package/tests/agents-utils.test.cjs +36 -13
- package/tests/dev-transpile-watch.test.cjs +117 -8
- package/tests/inspection.test.cjs +67 -0
- package/tests/mcp.test.cjs +127 -0
- package/tests/router-cache-config.test.cjs +74 -0
package/docs/mcp.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Proteum MCP
|
|
2
|
+
|
|
3
|
+
Proteum exposes read-only MCP surfaces for agents that need repeated, compact access to project and runtime state.
|
|
4
|
+
|
|
5
|
+
There are two entrypoints:
|
|
6
|
+
|
|
7
|
+
- `proteum mcp`: a stdio MCP server launched from an app or worktree.
|
|
8
|
+
- `proteum dev`: a dev-hosted MCP endpoint at `/__proteum/mcp`.
|
|
9
|
+
|
|
10
|
+
Both entrypoints expose the same tool/resource contract. The CLI remains the source of truth for `dev`, `build`, `check`, `refresh`, migrations, and reproducible terminal validation. MCP is for low-token reads, runtime snapshots, trace/perf/log summaries, and progressive detail loading.
|
|
11
|
+
|
|
12
|
+
## Stdio Server
|
|
13
|
+
|
|
14
|
+
Configure an MCP client to launch the server from the app root:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
proteum mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Useful options:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
proteum mcp --cwd /path/to/app
|
|
24
|
+
proteum mcp --url http://localhost:3101
|
|
25
|
+
proteum mcp --session-file var/run/proteum/dev/agents/task.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The stdio server reads manifest, instruction, and tracked-session data from disk. When a live dev server is known through `--url`, a tracked session file, or the manifest router port, runtime tools read the dev endpoints directly instead of spawning CLI commands.
|
|
29
|
+
|
|
30
|
+
Use stdio MCP when the agent environment can launch a long-lived tool server but does not already have direct access to the running `proteum dev` HTTP transport.
|
|
31
|
+
|
|
32
|
+
## Dev Runtime Endpoint
|
|
33
|
+
|
|
34
|
+
During `proteum dev`, the app exposes the same MCP contract through the official streamable HTTP transport:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
POST /__proteum/mcp
|
|
38
|
+
GET /__proteum/mcp
|
|
39
|
+
DELETE /__proteum/mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This endpoint is dev-only and local-tooling-only. It uses the running app's in-memory diagnostics, trace, perf, and log stores where possible, so runtime tools avoid process startup and avoid dumping full trace payloads by default.
|
|
43
|
+
|
|
44
|
+
The dev session UI and ready banner print:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
mcp http://localhost:<port>/__proteum/mcp
|
|
48
|
+
MCP: http://localhost:<port>/__proteum/mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use dev-hosted MCP when an agent is iterating against an already running app. It is the fastest path for repeated `runtime_status`, `orient`, `diagnose`, `trace_*`, `perf_*`, and `logs_tail` reads.
|
|
52
|
+
|
|
53
|
+
## Output Contract
|
|
54
|
+
|
|
55
|
+
MCP tool and resource payloads are compact single-line JSON strings in this shape:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{"ok":true,"format":"proteum-mcp-v1","summary":"...","data":{},"nextActions":[],"omitted":[]}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Outputs are capped by default:
|
|
62
|
+
|
|
63
|
+
- trace output shows counts, failed calls, error events, hot calls, and hot SQL first
|
|
64
|
+
- logs are limited and truncated
|
|
65
|
+
- diagnostics and perf rows are capped
|
|
66
|
+
- full trace detail is paginated with `detail: "full"`, `limit`, and `offset`
|
|
67
|
+
|
|
68
|
+
Do not make MCP tools return pretty-printed JSON or raw trace/log dumps by default. Pretty output belongs to human CLI/UI surfaces; MCP output is optimized for agent context.
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
The v1 tools are read-only:
|
|
73
|
+
|
|
74
|
+
| Tool | Purpose |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `runtime_status` | Manifest summary, selected runtime, tracked sessions, health, and MCP URL |
|
|
77
|
+
| `orient` | Owner, instruction routing, connected boundaries, and next actions |
|
|
78
|
+
| `instructions_resolve` | Selected instruction files for a query, with short previews |
|
|
79
|
+
| `explain_summary` | Compact manifest summary or owner lookup |
|
|
80
|
+
| `doctor` | Compact manifest and optional contract diagnostics |
|
|
81
|
+
| `diagnose` | Composite diagnosis for an existing route, query, or request trace |
|
|
82
|
+
| `trace_latest` | Compact latest trace summary, with optional paginated detail |
|
|
83
|
+
| `trace_show` | Compact or paginated detail for a specific request trace |
|
|
84
|
+
| `perf_top` | Hot-path perf rollup |
|
|
85
|
+
| `perf_request` | One-request waterfall and attribution |
|
|
86
|
+
| `logs_tail` | Capped recent server logs |
|
|
87
|
+
|
|
88
|
+
MCP v1 intentionally does not start/stop dev servers, refresh generated files, arm traces, export traces, write files, run migrations, or execute app commands.
|
|
89
|
+
|
|
90
|
+
## Resources
|
|
91
|
+
|
|
92
|
+
Static resources expose common compact reads:
|
|
93
|
+
|
|
94
|
+
- `proteum://runtime/status`
|
|
95
|
+
- `proteum://instructions/router`
|
|
96
|
+
- `proteum://manifest/summary`
|
|
97
|
+
- `proteum://trace/latest/summary`
|
|
98
|
+
- `proteum://perf/top`
|
|
99
|
+
|
|
100
|
+
## CLI Boundary
|
|
101
|
+
|
|
102
|
+
Use CLI commands when the result must be reproducible as a terminal step, CI-like validation, or human-shareable command output:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
proteum dev
|
|
106
|
+
proteum build --prod
|
|
107
|
+
proteum check
|
|
108
|
+
proteum refresh
|
|
109
|
+
proteum diagnose /dashboard --port 3101
|
|
110
|
+
proteum verify request /dashboard --port 3101
|
|
111
|
+
proteum trace show <requestId> --events --port 3101
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use MCP when an agent is asking the same running app for repeated state:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
runtime_status
|
|
118
|
+
instructions_resolve
|
|
119
|
+
orient
|
|
120
|
+
diagnose
|
|
121
|
+
trace_latest
|
|
122
|
+
perf_request
|
|
123
|
+
logs_tail
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Routing Guidance
|
|
127
|
+
|
|
128
|
+
Use these surfaces in this order:
|
|
129
|
+
|
|
130
|
+
1. Agent instructions for hard safety policy and routing rules.
|
|
131
|
+
2. MCP for repeated reads, runtime status, instruction selection, traces, perf, and logs.
|
|
132
|
+
3. Compact CLI for reproducible terminal validation and CI-like checks.
|
|
133
|
+
4. Full CLI escape hatches only after compact MCP/CLI output identifies the missing detail.
|
|
134
|
+
|
|
135
|
+
## Benchmark
|
|
136
|
+
|
|
137
|
+
The Product `/domains` diagnostic loop measured on May 7, 2026 used `ceil(UTF-8 bytes / 4)` as an output-token estimate:
|
|
138
|
+
|
|
139
|
+
| Workflow | Approx output tokens | Elapsed |
|
|
140
|
+
| --- | ---: | ---: |
|
|
141
|
+
| Compact CLI single loop | 6,286 | 4,809 ms |
|
|
142
|
+
| Dev-hosted HTTP MCP single loop | 5,211 | 232 ms |
|
|
143
|
+
| Stdio MCP single loop | 5,526 | 900 ms |
|
|
144
|
+
| Compact CLI repeated reads x3 | 11,660 | 9,572 ms |
|
|
145
|
+
| Dev-hosted HTTP MCP repeated reads x3 | 10,537 | 214 ms |
|
|
146
|
+
|
|
147
|
+
The benchmark included the routed instruction docs separately. Reading the four selected instruction files once was about 4,881 estimated output tokens; refreshing the instruction routing through MCP `instructions_resolve` was about 722 estimated output tokens.
|
|
148
|
+
|
|
149
|
+
The practical rule from the benchmark is: use CLI for the first reproducible check and validation record, then use MCP for repeated reads against the same app/runtime.
|
|
@@ -307,13 +307,18 @@ This is usually only needed for workspace or hoisted installs. Standalone apps w
|
|
|
307
307
|
|
|
308
308
|
These are worth updating even though they are not core app-code migrations.
|
|
309
309
|
|
|
310
|
-
### Use
|
|
310
|
+
### Use compact CLI output for automation
|
|
311
311
|
|
|
312
|
-
Only bare `proteum build` and bare `proteum dev` runs print a banner. For scripts, CI helpers, or editor tooling, prefer:
|
|
312
|
+
Only bare `proteum build` and bare `proteum dev` runs print a banner. Other CLI diagnostics are optimized for agents and return compact machine-readable output by default. For scripts, CI helpers, or editor tooling, prefer:
|
|
313
313
|
|
|
314
|
-
- `npx proteum
|
|
315
|
-
- `npx proteum
|
|
316
|
-
- `npx proteum
|
|
314
|
+
- `npx proteum orient <query>`
|
|
315
|
+
- `npx proteum runtime status`
|
|
316
|
+
- `npx proteum explain`
|
|
317
|
+
- `npx proteum doctor`
|
|
318
|
+
- `npx proteum connect`
|
|
319
|
+
- `npx proteum mcp --url http://localhost:<port>` for repeated agent reads against a running dev app
|
|
320
|
+
|
|
321
|
+
CLI output uses compact `proteum-agent-v1` JSON for reproducible command evidence. MCP output uses compact `proteum-mcp-v1` JSON for repeated runtime reads. Use `npx proteum explain --manifest`, `npx proteum diagnose <target> --full`, or `npx proteum trace show <requestId> --events` only when the compact output is insufficient.
|
|
317
322
|
|
|
318
323
|
### Use tracked dev sessions
|
|
319
324
|
|
|
@@ -331,6 +336,9 @@ These are new capabilities, not migration requirements, but they are the fastest
|
|
|
331
336
|
|
|
332
337
|
- `npx proteum connect --strict`
|
|
333
338
|
- `npx proteum explain --connected --controllers`
|
|
339
|
+
- `npx proteum runtime status`
|
|
340
|
+
- `npx proteum mcp`
|
|
341
|
+
- `npx proteum mcp --url http://localhost:<port>`
|
|
334
342
|
- `npx proteum diagnose / --port <port>`
|
|
335
343
|
- `npx proteum perf top --port <port>`
|
|
336
344
|
- `npx proteum trace latest --port <port>`
|
|
@@ -370,6 +378,8 @@ Then boot the app and verify the live runtime:
|
|
|
370
378
|
|
|
371
379
|
```bash
|
|
372
380
|
npx proteum dev --port 3010
|
|
381
|
+
npx proteum runtime status
|
|
382
|
+
npx proteum mcp --url http://localhost:3010
|
|
373
383
|
npx proteum diagnose / --port 3010
|
|
374
384
|
npx proteum trace latest --port 3010
|
|
375
385
|
```
|
package/docs/request-tracing.md
CHANGED
|
@@ -10,7 +10,7 @@ The same API and SQL instrumentation feeds both shapes. Dev trace keeps the in-m
|
|
|
10
10
|
## Scope
|
|
11
11
|
|
|
12
12
|
- retained dev tracing is available only when the app runs with `profile: dev`
|
|
13
|
-
- traces are exposed through `proteum trace`, `proteum perf`, and the dev-only `__proteum/trace` and
|
|
13
|
+
- traces are exposed through `proteum trace`, `proteum perf`, MCP `trace_*`/`perf_*` tools, and the dev-only `__proteum/trace`, `__proteum/perf`, and `/__proteum/mcp` HTTP endpoints
|
|
14
14
|
- `proteum diagnose` is a separate composite surface that reads the same framework diagnostics plus one matching request trace and buffered server logs; see [diagnostics.md](diagnostics.md)
|
|
15
15
|
- `ENABLE_PROFILER=true` enables reduced request-local profiling in any environment, including production
|
|
16
16
|
|
|
@@ -20,6 +20,7 @@ The same API and SQL instrumentation feeds both shapes. Dev trace keeps the in-m
|
|
|
20
20
|
proteum trace requests
|
|
21
21
|
proteum trace latest
|
|
22
22
|
proteum trace show <requestId>
|
|
23
|
+
proteum trace show <requestId> --events
|
|
23
24
|
proteum trace arm --capture deep
|
|
24
25
|
proteum trace export <requestId>
|
|
25
26
|
proteum trace latest --url http://127.0.0.1:3010
|
|
@@ -30,26 +31,31 @@ proteum perf compare --baseline yesterday --target today --group-by route
|
|
|
30
31
|
proteum perf memory --since 1h --group-by controller
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
Default trace output is compact `proteum-agent-v1` JSON with counts, failed calls, error events, hot calls, and hot SQL. Use `--events` or `--full` only when raw event details, payload summaries, or SQL text are needed.
|
|
35
|
+
|
|
36
|
+
When an MCP client is available, use MCP `trace_latest`, `trace_show`, `perf_top`, and `perf_request` for repeated reads against the same running app. Keep the CLI commands for reproducible terminal evidence and final verification logs.
|
|
37
|
+
|
|
33
38
|
Before reproducing a bug or starting a new test pass:
|
|
34
39
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- if
|
|
40
|
+
- run `proteum runtime status` to reuse a tracked dev session when possible
|
|
41
|
+
- use MCP `runtime_status` for repeated status checks against the same running server
|
|
42
|
+
- if a server is already running, inspect `proteum trace requests`, `proteum trace latest`, and compact `proteum diagnose <path>` first so you can capture past errors without dumping raw events
|
|
38
43
|
|
|
39
44
|
Typical debugging flow:
|
|
40
45
|
|
|
41
46
|
```bash
|
|
42
47
|
proteum orient /dashboard
|
|
48
|
+
proteum runtime status
|
|
43
49
|
proteum diagnose /dashboard --hit /dashboard --port 3103
|
|
44
50
|
proteum perf request /dashboard --port 3103
|
|
45
|
-
proteum trace show <requestId> --port 3103
|
|
51
|
+
proteum trace show <requestId> --events --port 3103
|
|
46
52
|
```
|
|
47
53
|
|
|
48
54
|
Use `--url http://host:port` when the dev server is reachable on a non-standard host and `--port` is not enough.
|
|
49
55
|
|
|
50
56
|
If the request under test is protected and login UX is not the feature under test, mint an auth cookie with `proteum session <email> --role <role>` before reproducing the request. This keeps the trace focused on the protected behavior instead of the login flow.
|
|
51
57
|
|
|
52
|
-
If you already know the failing path and want a one-shot suspect list before reading raw events, start with `proteum diagnose <path> --port <port>` and `proteum perf request <path> --port <port>` first, then drop into `proteum trace show <requestId
|
|
58
|
+
If you already know the failing path and want a one-shot suspect list before reading raw events, start with `proteum diagnose <path> --port <port>` and `proteum perf request <path> --port <port>` first, then drop into `proteum trace show <requestId> --events` only when the lower-level event stream is still needed.
|
|
53
59
|
|
|
54
60
|
Use ad hoc database queries or one-off scripts only after `orient`, `diagnose`, `perf request`, and `trace show` still leave the request chain unclear.
|
|
55
61
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proteum",
|
|
3
3
|
"description": "LLM-first Opinionated Typescript Framework for web applications.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.3.0",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/proteum.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@babel/traverse": "^7.29.0",
|
|
22
22
|
"@babel/types": "^7.29.0",
|
|
23
23
|
"@inkjs/ui": "^2.0.0",
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
24
25
|
"@prisma/adapter-mariadb": "7.2.0",
|
|
25
26
|
"@prisma/client": "7.2.0",
|
|
26
27
|
"@rspack/core": "^1.7.9",
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { buildContractsDoctorResponse } from '@common/dev/contractsDoctor';
|
|
2
|
+
import { buildDoctorResponse } from '@common/dev/diagnostics';
|
|
3
|
+
import { buildOrientationResponse, explainOwner } from '@common/dev/inspection';
|
|
4
|
+
import {
|
|
5
|
+
buildRuntimeStatusPayload,
|
|
6
|
+
compactDiagnoseResponse,
|
|
7
|
+
compactDoctorResponse,
|
|
8
|
+
compactExplainSummary,
|
|
9
|
+
compactLogsResponse,
|
|
10
|
+
compactOrientationResponse,
|
|
11
|
+
compactPerfRequestResponse,
|
|
12
|
+
compactPerfTopResponse,
|
|
13
|
+
compactTraceResponse,
|
|
14
|
+
resolveInstructionRouting,
|
|
15
|
+
} from '@common/dev/mcpPayloads';
|
|
16
|
+
import type { TProteumMcpProvider } from '@common/dev/mcpServer';
|
|
17
|
+
import type { TDevConsoleLogLevel } from '@common/dev/console';
|
|
18
|
+
import type { TPerfGroupBy } from '@common/dev/performance';
|
|
19
|
+
|
|
20
|
+
import type { Application } from './index';
|
|
21
|
+
|
|
22
|
+
export const createRuntimeProteumMcpProvider = ({
|
|
23
|
+
app,
|
|
24
|
+
publicUrl,
|
|
25
|
+
routerPort,
|
|
26
|
+
}: {
|
|
27
|
+
app: Application;
|
|
28
|
+
publicUrl: string;
|
|
29
|
+
routerPort: number;
|
|
30
|
+
}): TProteumMcpProvider => {
|
|
31
|
+
const diagnostics = () => app.getDevDiagnostics();
|
|
32
|
+
const readManifest = () => diagnostics().readManifest();
|
|
33
|
+
const assertTraceEnabled = () => {
|
|
34
|
+
if (!app.container.Trace.isDevTraceEnabled()) {
|
|
35
|
+
throw new Error('Proteum dev trace is not enabled for this runtime.');
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const provider: TProteumMcpProvider = {
|
|
40
|
+
async runtimeStatus(_input: Record<string, never> = {}) {
|
|
41
|
+
const manifest = readManifest();
|
|
42
|
+
const doctor = buildDoctorResponse(manifest);
|
|
43
|
+
const contracts = buildContractsDoctorResponse(manifest);
|
|
44
|
+
|
|
45
|
+
return buildRuntimeStatusPayload({
|
|
46
|
+
appRoot: app.container.path.root,
|
|
47
|
+
health: {
|
|
48
|
+
reachable: true,
|
|
49
|
+
doctor: doctor.summary,
|
|
50
|
+
contracts: contracts.summary,
|
|
51
|
+
},
|
|
52
|
+
manifest,
|
|
53
|
+
runtime: {
|
|
54
|
+
publicUrl,
|
|
55
|
+
routerPort,
|
|
56
|
+
source: 'proteum-dev-runtime',
|
|
57
|
+
mcpUrl: `${publicUrl}/__proteum/mcp`,
|
|
58
|
+
traceEnabled: app.container.Trace.isDevTraceEnabled(),
|
|
59
|
+
profilerEnabled: app.container.Trace.isProfilingEnabled(),
|
|
60
|
+
connectedProjects: Object.entries(app.connectedProjects || {}).map(([namespace, project]) => ({
|
|
61
|
+
namespace,
|
|
62
|
+
urlInternal: (project as { urlInternal?: string }).urlInternal,
|
|
63
|
+
})),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
async orient({ query }) {
|
|
68
|
+
return compactOrientationResponse(buildOrientationResponse(readManifest(), query));
|
|
69
|
+
},
|
|
70
|
+
async instructionsResolve({ query }) {
|
|
71
|
+
return resolveInstructionRouting({ appRoot: app.container.path.root, query });
|
|
72
|
+
},
|
|
73
|
+
async explainSummary({ query }) {
|
|
74
|
+
const manifest = readManifest();
|
|
75
|
+
const normalizedQuery = query?.trim();
|
|
76
|
+
|
|
77
|
+
return compactExplainSummary({
|
|
78
|
+
manifest,
|
|
79
|
+
owner: normalizedQuery ? explainOwner(manifest, normalizedQuery) : undefined,
|
|
80
|
+
query: normalizedQuery,
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
async doctor({ contracts = true }) {
|
|
84
|
+
const manifest = readManifest();
|
|
85
|
+
|
|
86
|
+
return compactDoctorResponse({
|
|
87
|
+
contracts: contracts ? buildContractsDoctorResponse(manifest) : undefined,
|
|
88
|
+
doctor: buildDoctorResponse(manifest),
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
async diagnose({
|
|
92
|
+
logsLevel = 'warn',
|
|
93
|
+
logsLimit = 40,
|
|
94
|
+
path,
|
|
95
|
+
query,
|
|
96
|
+
requestId,
|
|
97
|
+
}: {
|
|
98
|
+
logsLevel?: TDevConsoleLogLevel;
|
|
99
|
+
logsLimit?: number;
|
|
100
|
+
path?: string;
|
|
101
|
+
query?: string;
|
|
102
|
+
requestId?: string;
|
|
103
|
+
}) {
|
|
104
|
+
return compactDiagnoseResponse(
|
|
105
|
+
diagnostics().diagnose({
|
|
106
|
+
logsLevel,
|
|
107
|
+
logsLimit,
|
|
108
|
+
path,
|
|
109
|
+
query,
|
|
110
|
+
requestId,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
async traceLatest({ detail, limit, offset }) {
|
|
115
|
+
assertTraceEnabled();
|
|
116
|
+
const request = app.container.Trace.getLatestRequest();
|
|
117
|
+
if (!request) throw new Error('No request trace is available yet.');
|
|
118
|
+
|
|
119
|
+
return compactTraceResponse({ detail, limit, offset, request });
|
|
120
|
+
},
|
|
121
|
+
async traceShow({ detail, limit, offset, requestId }) {
|
|
122
|
+
assertTraceEnabled();
|
|
123
|
+
const request = app.container.Trace.getRequest(requestId);
|
|
124
|
+
if (!request) throw new Error(`Trace ${requestId} was not found.`);
|
|
125
|
+
|
|
126
|
+
return compactTraceResponse({ detail, limit, offset, request });
|
|
127
|
+
},
|
|
128
|
+
async perfTop({ groupBy = 'path', limit = 12, since = 'today' }) {
|
|
129
|
+
return compactPerfTopResponse(
|
|
130
|
+
diagnostics().perfTop({
|
|
131
|
+
groupBy: groupBy as TPerfGroupBy,
|
|
132
|
+
limit,
|
|
133
|
+
since,
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
async perfRequest({ query }) {
|
|
138
|
+
return compactPerfRequestResponse(diagnostics().perfRequest(query));
|
|
139
|
+
},
|
|
140
|
+
async logsTail({ level = 'warn', limit = 40 }) {
|
|
141
|
+
return compactLogsResponse({
|
|
142
|
+
level,
|
|
143
|
+
limit,
|
|
144
|
+
response: diagnostics().readLogs(limit, level),
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
async readResource(uri) {
|
|
148
|
+
if (uri === 'proteum://runtime/status') return await provider.runtimeStatus({});
|
|
149
|
+
if (uri === 'proteum://instructions/router') return await provider.instructionsResolve({});
|
|
150
|
+
if (uri === 'proteum://manifest/summary') return await provider.explainSummary({});
|
|
151
|
+
if (uri === 'proteum://trace/latest/summary') return await provider.traceLatest({});
|
|
152
|
+
if (uri === 'proteum://perf/top') return await provider.perfTop({});
|
|
153
|
+
|
|
154
|
+
throw new Error(`Unknown Proteum MCP resource: ${uri}`);
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return provider;
|
|
159
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
export type THttpCacheHeadersConfig = {
|
|
4
|
+
cacheControl?: string;
|
|
5
|
+
surrogateControl?: string | false;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type TPublicAssetsCacheConfig = {
|
|
9
|
+
dev?: string;
|
|
10
|
+
versioned?: string;
|
|
11
|
+
unversioned?: string;
|
|
12
|
+
etag?: boolean;
|
|
13
|
+
lastModified?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type THttpCacheConfig = {
|
|
17
|
+
html?: {
|
|
18
|
+
dynamic?: THttpCacheHeadersConfig;
|
|
19
|
+
static?: THttpCacheHeadersConfig;
|
|
20
|
+
};
|
|
21
|
+
publicAssets?: TPublicAssetsCacheConfig;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type TResolvedHttpCacheHeadersConfig = {
|
|
25
|
+
cacheControl: string;
|
|
26
|
+
surrogateControl: string | false;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type TResolvedPublicAssetsCacheConfig = {
|
|
30
|
+
dev: string;
|
|
31
|
+
versioned: string;
|
|
32
|
+
unversioned: string;
|
|
33
|
+
etag?: boolean;
|
|
34
|
+
lastModified?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type TResolvedHttpCacheConfig = {
|
|
38
|
+
html: {
|
|
39
|
+
dynamic: TResolvedHttpCacheHeadersConfig;
|
|
40
|
+
static: TResolvedHttpCacheHeadersConfig;
|
|
41
|
+
};
|
|
42
|
+
publicAssets: TResolvedPublicAssetsCacheConfig;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const defaultHttpCacheConfig = {
|
|
46
|
+
html: {
|
|
47
|
+
dynamic: {
|
|
48
|
+
cacheControl: 'no-store, no-cache, must-revalidate, proxy-revalidate',
|
|
49
|
+
surrogateControl: 'no-store',
|
|
50
|
+
},
|
|
51
|
+
static: {
|
|
52
|
+
cacheControl: 'public, max-age=0, must-revalidate',
|
|
53
|
+
surrogateControl: false,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
publicAssets: {
|
|
57
|
+
dev: 'no-store',
|
|
58
|
+
versioned: 'public, max-age=31536000, immutable',
|
|
59
|
+
unversioned: 'public, max-age=0, must-revalidate',
|
|
60
|
+
},
|
|
61
|
+
} satisfies TResolvedHttpCacheConfig;
|
|
62
|
+
|
|
63
|
+
type TPublicAssetRequest = {
|
|
64
|
+
originalUrl?: string;
|
|
65
|
+
url?: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type TPublicAssetResponse = {
|
|
69
|
+
req?: TPublicAssetRequest;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const hashedPublicAssetPattern = /(^|[-_.])[a-f0-9]{6,}(?=(\.[^.]+)+$)/i;
|
|
73
|
+
|
|
74
|
+
export const resolveHttpCacheConfig = (config?: THttpCacheConfig): TResolvedHttpCacheConfig => ({
|
|
75
|
+
html: {
|
|
76
|
+
dynamic: {
|
|
77
|
+
...defaultHttpCacheConfig.html.dynamic,
|
|
78
|
+
...(config?.html?.dynamic || {}),
|
|
79
|
+
},
|
|
80
|
+
static: {
|
|
81
|
+
...defaultHttpCacheConfig.html.static,
|
|
82
|
+
...(config?.html?.static || {}),
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
publicAssets: {
|
|
86
|
+
...defaultHttpCacheConfig.publicAssets,
|
|
87
|
+
...(config?.publicAssets || {}),
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const isVersionedPublicAssetRequest = (
|
|
92
|
+
res: undefined | TPublicAssetResponse,
|
|
93
|
+
filePath: string,
|
|
94
|
+
) => {
|
|
95
|
+
const requestUrl = res?.req?.originalUrl || res?.req?.url || '';
|
|
96
|
+
const searchParams = new URL(requestUrl, 'http://proteum.local').searchParams;
|
|
97
|
+
if (searchParams.has('v')) return true;
|
|
98
|
+
|
|
99
|
+
return hashedPublicAssetPattern.test(path.basename(filePath));
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const resolvePublicAssetCacheControl = ({
|
|
103
|
+
res,
|
|
104
|
+
filePath,
|
|
105
|
+
profile,
|
|
106
|
+
cache,
|
|
107
|
+
}: {
|
|
108
|
+
res: undefined | TPublicAssetResponse;
|
|
109
|
+
filePath: string;
|
|
110
|
+
profile: string;
|
|
111
|
+
cache: TResolvedPublicAssetsCacheConfig;
|
|
112
|
+
}) => {
|
|
113
|
+
if (profile === 'dev') return cache.dev;
|
|
114
|
+
|
|
115
|
+
return isVersionedPublicAssetRequest(res, filePath) ? cache.versioned : cache.unversioned;
|
|
116
|
+
};
|