@frontmcp/sdk 0.1.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/README.md +11 -0
- package/package.json +29 -0
- package/src/constants.d.ts +30 -0
- package/src/constants.js +36 -0
- package/src/constants.js.map +1 -0
- package/src/decorators/adapter.decorator.d.ts +7 -0
- package/src/decorators/adapter.decorator.js +20 -0
- package/src/decorators/adapter.decorator.js.map +1 -0
- package/src/decorators/app.decorator.d.ts +7 -0
- package/src/decorators/app.decorator.js +44 -0
- package/src/decorators/app.decorator.js.map +1 -0
- package/src/decorators/auth-provider.decorator.d.ts +7 -0
- package/src/decorators/auth-provider.decorator.js +20 -0
- package/src/decorators/auth-provider.decorator.js.map +1 -0
- package/src/decorators/flow-hooks.decorator.d.ts +12 -0
- package/src/decorators/flow-hooks.decorator.js +63 -0
- package/src/decorators/flow-hooks.decorator.js.map +1 -0
- package/src/decorators/flow.decorator.d.ts +6 -0
- package/src/decorators/flow.decorator.js +19 -0
- package/src/decorators/flow.decorator.js.map +1 -0
- package/src/decorators/front-mcp.decorator.d.ts +6 -0
- package/src/decorators/front-mcp.decorator.js +40 -0
- package/src/decorators/front-mcp.decorator.js.map +1 -0
- package/src/decorators/index.d.ts +12 -0
- package/src/decorators/index.js +16 -0
- package/src/decorators/index.js.map +1 -0
- package/src/decorators/logger.decorator.d.ts +7 -0
- package/src/decorators/logger.decorator.js +20 -0
- package/src/decorators/logger.decorator.js.map +1 -0
- package/src/decorators/plugin.decorator.d.ts +7 -0
- package/src/decorators/plugin.decorator.js +38 -0
- package/src/decorators/plugin.decorator.js.map +1 -0
- package/src/decorators/prompt.decorator.d.ts +13 -0
- package/src/decorators/prompt.decorator.js +38 -0
- package/src/decorators/prompt.decorator.js.map +1 -0
- package/src/decorators/provider.decorator.d.ts +7 -0
- package/src/decorators/provider.decorator.js +20 -0
- package/src/decorators/provider.decorator.js.map +1 -0
- package/src/decorators/resource.decorator.d.ts +17 -0
- package/src/decorators/resource.decorator.js +52 -0
- package/src/decorators/resource.decorator.js.map +1 -0
- package/src/decorators/tool.decorator.d.ts +14 -0
- package/src/decorators/tool.decorator.js +38 -0
- package/src/decorators/tool.decorator.js.map +1 -0
- package/src/decorators-old/async-with.decorator.d.ts +9 -0
- package/src/decorators-old/async-with.decorator.js +23 -0
- package/src/decorators-old/async-with.decorator.js.map +1 -0
- package/src/decorators-old/auth-hook.decorator.d.ts +14 -0
- package/src/decorators-old/auth-hook.decorator.js +27 -0
- package/src/decorators-old/auth-hook.decorator.js.map +1 -0
- package/src/decorators-old/session-hook.decorator.d.ts +14 -0
- package/src/decorators-old/session-hook.decorator.js +27 -0
- package/src/decorators-old/session-hook.decorator.js.map +1 -0
- package/src/decorators-old/tool-hook.decorator.d.ts +14 -0
- package/src/decorators-old/tool-hook.decorator.js +27 -0
- package/src/decorators-old/tool-hook.decorator.js.map +1 -0
- package/src/dynamic/dynamic.adapter.d.ts +42 -0
- package/src/dynamic/dynamic.adapter.js +28 -0
- package/src/dynamic/dynamic.adapter.js.map +1 -0
- package/src/dynamic/dynamic.plugin.d.ts +52 -0
- package/src/dynamic/dynamic.plugin.js +33 -0
- package/src/dynamic/dynamic.plugin.js.map +1 -0
- package/src/dynamic/dynamic.utils.d.ts +3 -0
- package/src/dynamic/dynamic.utils.js +27 -0
- package/src/dynamic/dynamic.utils.js.map +1 -0
- package/src/dynamic/index.d.ts +2 -0
- package/src/dynamic/index.js +6 -0
- package/src/dynamic/index.js.map +1 -0
- package/src/entries/adapter.entry.d.ts +6 -0
- package/src/entries/adapter.entry.js +8 -0
- package/src/entries/adapter.entry.js.map +1 -0
- package/src/entries/app.entry.d.ts +13 -0
- package/src/entries/app.entry.js +9 -0
- package/src/entries/app.entry.js.map +1 -0
- package/src/entries/auth-provider.entry.d.ts +6 -0
- package/src/entries/auth-provider.entry.js +8 -0
- package/src/entries/auth-provider.entry.js.map +1 -0
- package/src/entries/base.entry.d.ts +20 -0
- package/src/entries/base.entry.js +17 -0
- package/src/entries/base.entry.js.map +1 -0
- package/src/entries/flow.entry.d.ts +15 -0
- package/src/entries/flow.entry.js +21 -0
- package/src/entries/flow.entry.js.map +1 -0
- package/src/entries/index.d.ts +12 -0
- package/src/entries/index.js +16 -0
- package/src/entries/index.js.map +1 -0
- package/src/entries/logger.entry.d.ts +6 -0
- package/src/entries/logger.entry.js +8 -0
- package/src/entries/logger.entry.js.map +1 -0
- package/src/entries/plugin.entry.d.ts +6 -0
- package/src/entries/plugin.entry.js +8 -0
- package/src/entries/plugin.entry.js.map +1 -0
- package/src/entries/prompt.entry.d.ts +6 -0
- package/src/entries/prompt.entry.js +8 -0
- package/src/entries/prompt.entry.js.map +1 -0
- package/src/entries/provider.entry.d.ts +7 -0
- package/src/entries/provider.entry.js +8 -0
- package/src/entries/provider.entry.js.map +1 -0
- package/src/entries/resource.entry.d.ts +7 -0
- package/src/entries/resource.entry.js +11 -0
- package/src/entries/resource.entry.js.map +1 -0
- package/src/entries/scope.entry.d.ts +17 -0
- package/src/entries/scope.entry.js +8 -0
- package/src/entries/scope.entry.js.map +1 -0
- package/src/entries/tool.entry.d.ts +15 -0
- package/src/entries/tool.entry.js +11 -0
- package/src/entries/tool.entry.js.map +1 -0
- package/src/index.d.ts +18 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/interfaces/adapter.interface.d.ts +20 -0
- package/src/interfaces/adapter.interface.js +3 -0
- package/src/interfaces/adapter.interface.js.map +1 -0
- package/src/interfaces/app.interface.d.ts +6 -0
- package/src/interfaces/app.interface.js +3 -0
- package/src/interfaces/app.interface.js.map +1 -0
- package/src/interfaces/auth-hook.interface.d.ts +126 -0
- package/src/interfaces/auth-hook.interface.js +135 -0
- package/src/interfaces/auth-hook.interface.js.map +1 -0
- package/src/interfaces/auth-provider.interface.d.ts +22 -0
- package/src/interfaces/auth-provider.interface.js +18 -0
- package/src/interfaces/auth-provider.interface.js.map +1 -0
- package/src/interfaces/base.interface.d.ts +77 -0
- package/src/interfaces/base.interface.js +3 -0
- package/src/interfaces/base.interface.js.map +1 -0
- package/src/interfaces/flow.interface.d.ts +38 -0
- package/src/interfaces/flow.interface.js +69 -0
- package/src/interfaces/flow.interface.js.map +1 -0
- package/src/interfaces/front-mcp.interface.d.ts +5 -0
- package/src/interfaces/front-mcp.interface.js +3 -0
- package/src/interfaces/front-mcp.interface.js.map +1 -0
- package/src/interfaces/index.d.ts +15 -0
- package/src/interfaces/index.js +19 -0
- package/src/interfaces/index.js.map +1 -0
- package/src/interfaces/internal/flow.utils.d.ts +24 -0
- package/src/interfaces/internal/flow.utils.js +83 -0
- package/src/interfaces/internal/flow.utils.js.map +1 -0
- package/src/interfaces/internal/index.d.ts +2 -0
- package/src/interfaces/internal/index.js +7 -0
- package/src/interfaces/internal/index.js.map +1 -0
- package/src/interfaces/internal/primary-auth-provider.interface.d.ts +24 -0
- package/src/interfaces/internal/primary-auth-provider.interface.js +33 -0
- package/src/interfaces/internal/primary-auth-provider.interface.js.map +1 -0
- package/src/interfaces/internal/registry.interface.d.ts +71 -0
- package/src/interfaces/internal/registry.interface.js +3 -0
- package/src/interfaces/internal/registry.interface.js.map +1 -0
- package/src/interfaces/logger.interface.d.ts +42 -0
- package/src/interfaces/logger.interface.js +10 -0
- package/src/interfaces/logger.interface.js.map +1 -0
- package/src/interfaces/plugin.interface.d.ts +8 -0
- package/src/interfaces/plugin.interface.js +3 -0
- package/src/interfaces/plugin.interface.js.map +1 -0
- package/src/interfaces/prompt.interface.d.ts +5 -0
- package/src/interfaces/prompt.interface.js +3 -0
- package/src/interfaces/prompt.interface.js.map +1 -0
- package/src/interfaces/provider.interface.d.ts +20 -0
- package/src/interfaces/provider.interface.js +18 -0
- package/src/interfaces/provider.interface.js.map +1 -0
- package/src/interfaces/resource.interface.d.ts +21 -0
- package/src/interfaces/resource.interface.js +3 -0
- package/src/interfaces/resource.interface.js.map +1 -0
- package/src/interfaces/scope.interface.d.ts +5 -0
- package/src/interfaces/scope.interface.js +3 -0
- package/src/interfaces/scope.interface.js.map +1 -0
- package/src/interfaces/server.interface.d.ts +46 -0
- package/src/interfaces/server.interface.js +18 -0
- package/src/interfaces/server.interface.js.map +1 -0
- package/src/interfaces/session-hook.interface.d.ts +131 -0
- package/src/interfaces/session-hook.interface.js +140 -0
- package/src/interfaces/session-hook.interface.js.map +1 -0
- package/src/interfaces/tool-hook.interface.d.ts +80 -0
- package/src/interfaces/tool-hook.interface.js +92 -0
- package/src/interfaces/tool-hook.interface.js.map +1 -0
- package/src/interfaces/tool.interface.d.ts +45 -0
- package/src/interfaces/tool.interface.js +89 -0
- package/src/interfaces/tool.interface.js.map +1 -0
- package/src/metadata/adapter.metadata.d.ts +22 -0
- package/src/metadata/adapter.metadata.js +10 -0
- package/src/metadata/adapter.metadata.js.map +1 -0
- package/src/metadata/app.metadata.d.ts +872 -0
- package/src/metadata/app.metadata.js +30 -0
- package/src/metadata/app.metadata.js.map +1 -0
- package/src/metadata/auth-provider.metadata.d.ts +33 -0
- package/src/metadata/auth-provider.metadata.js +19 -0
- package/src/metadata/auth-provider.metadata.js.map +1 -0
- package/src/metadata/flow-hooks.metadata.d.ts +20 -0
- package/src/metadata/flow-hooks.metadata.js +3 -0
- package/src/metadata/flow-hooks.metadata.js.map +1 -0
- package/src/metadata/flow.metadata.d.ts +75 -0
- package/src/metadata/flow.metadata.js +15 -0
- package/src/metadata/flow.metadata.js.map +1 -0
- package/src/metadata/front-mcp.metadata.d.ts +1144 -0
- package/src/metadata/front-mcp.metadata.js +25 -0
- package/src/metadata/front-mcp.metadata.js.map +1 -0
- package/src/metadata/index.d.ts +12 -0
- package/src/metadata/index.js +16 -0
- package/src/metadata/index.js.map +1 -0
- package/src/metadata/logger.metadata.d.ts +39 -0
- package/src/metadata/logger.metadata.js +10 -0
- package/src/metadata/logger.metadata.js.map +1 -0
- package/src/metadata/plugin.metadata.d.ts +93 -0
- package/src/metadata/plugin.metadata.js +18 -0
- package/src/metadata/plugin.metadata.js.map +1 -0
- package/src/metadata/prompt.metadata.d.ts +226 -0
- package/src/metadata/prompt.metadata.js +27 -0
- package/src/metadata/prompt.metadata.js.map +1 -0
- package/src/metadata/provider.metadata.d.ts +34 -0
- package/src/metadata/provider.metadata.js +20 -0
- package/src/metadata/provider.metadata.js.map +1 -0
- package/src/metadata/resource.metadata.d.ts +199 -0
- package/src/metadata/resource.metadata.js +22 -0
- package/src/metadata/resource.metadata.js.map +1 -0
- package/src/metadata/tool.metadata.d.ts +278 -0
- package/src/metadata/tool.metadata.js +28 -0
- package/src/metadata/tool.metadata.js.map +1 -0
- package/src/providers/session.provider.d.ts +13 -0
- package/src/providers/session.provider.js +27 -0
- package/src/providers/session.provider.js.map +1 -0
- package/src/records/adapter.record.d.ts +26 -0
- package/src/records/adapter.record.js +11 -0
- package/src/records/adapter.record.js.map +1 -0
- package/src/records/app.record.d.ts +19 -0
- package/src/records/app.record.js +9 -0
- package/src/records/app.record.js.map +1 -0
- package/src/records/auth-provider.record.d.ts +37 -0
- package/src/records/auth-provider.record.js +12 -0
- package/src/records/auth-provider.record.js.map +1 -0
- package/src/records/flow.record.d.ts +11 -0
- package/src/records/flow.record.js +8 -0
- package/src/records/flow.record.js.map +1 -0
- package/src/records/index.d.ts +11 -0
- package/src/records/index.js +15 -0
- package/src/records/index.js.map +1 -0
- package/src/records/logger.record.d.ts +11 -0
- package/src/records/logger.record.js +8 -0
- package/src/records/logger.record.js.map +1 -0
- package/src/records/plugin.record.d.ts +21 -0
- package/src/records/plugin.record.js +11 -0
- package/src/records/plugin.record.js.map +1 -0
- package/src/records/prompt.record.d.ts +18 -0
- package/src/records/prompt.record.js +9 -0
- package/src/records/prompt.record.js.map +1 -0
- package/src/records/provider.record.d.ts +36 -0
- package/src/records/provider.record.js +14 -0
- package/src/records/provider.record.js.map +1 -0
- package/src/records/resource.record.d.ts +18 -0
- package/src/records/resource.record.js +14 -0
- package/src/records/resource.record.js.map +1 -0
- package/src/records/scope.record.d.ts +18 -0
- package/src/records/scope.record.js +9 -0
- package/src/records/scope.record.js.map +1 -0
- package/src/records/tool.record.d.ts +17 -0
- package/src/records/tool.record.js +9 -0
- package/src/records/tool.record.js.map +1 -0
- package/src/schemas/annotated-class.schema.d.ts +11 -0
- package/src/schemas/annotated-class.schema.js +40 -0
- package/src/schemas/annotated-class.schema.js.map +1 -0
- package/src/schemas/http-input.schema.d.ts +33 -0
- package/src/schemas/http-input.schema.js +13 -0
- package/src/schemas/http-input.schema.js.map +1 -0
- package/src/schemas/http-output.schema.d.ts +2011 -0
- package/src/schemas/http-output.schema.js +283 -0
- package/src/schemas/http-output.schema.js.map +1 -0
- package/src/schemas/index.d.ts +3 -0
- package/src/schemas/index.js +7 -0
- package/src/schemas/index.js.map +1 -0
- package/src/tokens/adapter.tokens.d.ts +3 -0
- package/src/tokens/adapter.tokens.js +11 -0
- package/src/tokens/adapter.tokens.js.map +1 -0
- package/src/tokens/app.tokens.d.ts +4 -0
- package/src/tokens/app.tokens.js +30 -0
- package/src/tokens/app.tokens.js.map +1 -0
- package/src/tokens/auth-provider.tokens.d.ts +3 -0
- package/src/tokens/auth-provider.tokens.js +12 -0
- package/src/tokens/auth-provider.tokens.js.map +1 -0
- package/src/tokens/base.tokens.d.ts +5 -0
- package/src/tokens/base.tokens.js +9 -0
- package/src/tokens/base.tokens.js.map +1 -0
- package/src/tokens/flow-hook.tokens.d.ts +4 -0
- package/src/tokens/flow-hook.tokens.js +9 -0
- package/src/tokens/flow-hook.tokens.js.map +1 -0
- package/src/tokens/flow.tokens.d.ts +11 -0
- package/src/tokens/flow.tokens.js +16 -0
- package/src/tokens/flow.tokens.js.map +1 -0
- package/src/tokens/front-mcp.tokens.d.ts +3 -0
- package/src/tokens/front-mcp.tokens.js +19 -0
- package/src/tokens/front-mcp.tokens.js.map +1 -0
- package/src/tokens/index.d.ts +13 -0
- package/src/tokens/index.js +17 -0
- package/src/tokens/index.js.map +1 -0
- package/src/tokens/logger.tokens.d.ts +3 -0
- package/src/tokens/logger.tokens.js +11 -0
- package/src/tokens/logger.tokens.js.map +1 -0
- package/src/tokens/plugin.tokens.d.ts +13 -0
- package/src/tokens/plugin.tokens.js +18 -0
- package/src/tokens/plugin.tokens.js.map +1 -0
- package/src/tokens/prompt.tokens.d.ts +9 -0
- package/src/tokens/prompt.tokens.js +14 -0
- package/src/tokens/prompt.tokens.js.map +1 -0
- package/src/tokens/provider.tokens.d.ts +3 -0
- package/src/tokens/provider.tokens.js +12 -0
- package/src/tokens/provider.tokens.js.map +1 -0
- package/src/tokens/resource.tokens.d.ts +20 -0
- package/src/tokens/resource.tokens.js +25 -0
- package/src/tokens/resource.tokens.js.map +1 -0
- package/src/tokens/server.tokens.d.ts +6 -0
- package/src/tokens/server.tokens.js +11 -0
- package/src/tokens/server.tokens.js.map +1 -0
- package/src/tokens/tool.tokens.d.ts +13 -0
- package/src/tokens/tool.tokens.js +18 -0
- package/src/tokens/tool.tokens.js.map +1 -0
- package/src/types/auth/index.d.ts +2 -0
- package/src/types/auth/index.js +6 -0
- package/src/types/auth/index.js.map +1 -0
- package/src/types/auth/jwt.types.d.ts +112 -0
- package/src/types/auth/jwt.types.js +36 -0
- package/src/types/auth/jwt.types.js.map +1 -0
- package/src/types/auth/session.types.d.ts +263 -0
- package/src/types/auth/session.types.js +40 -0
- package/src/types/auth/session.types.js.map +1 -0
- package/src/types/common.types.d.ts +11 -0
- package/src/types/common.types.js +3 -0
- package/src/types/common.types.js.map +1 -0
- package/src/types/index.d.ts +3 -0
- package/src/types/index.js +7 -0
- package/src/types/index.js.map +1 -0
- package/src/types/options/auth.options.d.ts +513 -0
- package/src/types/options/auth.options.js +53 -0
- package/src/types/options/auth.options.js.map +1 -0
- package/src/types/options/http.options.d.ts +22 -0
- package/src/types/options/http.options.js +10 -0
- package/src/types/options/http.options.js.map +1 -0
- package/src/types/options/index.d.ts +5 -0
- package/src/types/options/index.js +9 -0
- package/src/types/options/index.js.map +1 -0
- package/src/types/options/logging.options.d.ts +39 -0
- package/src/types/options/logging.options.js +37 -0
- package/src/types/options/logging.options.js.map +1 -0
- package/src/types/options/server-info.options.d.ts +48 -0
- package/src/types/options/server-info.options.js +13 -0
- package/src/types/options/server-info.options.js.map +1 -0
- package/src/types/options/session.options.d.ts +67 -0
- package/src/types/options/session.options.js +9 -0
- package/src/types/options/session.options.js.map +1 -0
- package/src/utils/decide-request-intent.utils.d.ts +79 -0
- package/src/utils/decide-request-intent.utils.js +326 -0
- package/src/utils/decide-request-intent.utils.js.map +1 -0
- package/src/utils/index.d.ts +2 -0
- package/src/utils/index.js +6 -0
- package/src/utils/index.js.map +1 -0
- package/src/utils/path.utils.d.ts +20 -0
- package/src/utils/path.utils.js +66 -0
- package/src/utils/path.utils.js.map +1 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decisionSchema = exports.intentSchema = void 0;
|
|
4
|
+
exports.decideIntent = decideIntent;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
/* --------------------------------- Schemas --------------------------------- */
|
|
7
|
+
exports.intentSchema = zod_1.z.union([
|
|
8
|
+
zod_1.z.literal('legacy-sse'),
|
|
9
|
+
zod_1.z.literal('sse'),
|
|
10
|
+
zod_1.z.literal('streamable-http'),
|
|
11
|
+
zod_1.z.literal('stateful-http'),
|
|
12
|
+
zod_1.z.literal('stateless-http'),
|
|
13
|
+
zod_1.z.literal('unknown'),
|
|
14
|
+
]);
|
|
15
|
+
exports.decisionSchema = zod_1.z.object({
|
|
16
|
+
intent: exports.intentSchema,
|
|
17
|
+
reasons: zod_1.z.array(zod_1.z.string()),
|
|
18
|
+
recommendation: zod_1.z
|
|
19
|
+
.object({
|
|
20
|
+
httpStatus: zod_1.z.number(),
|
|
21
|
+
message: zod_1.z.string(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
// Echo back bits for debugging/telemetry if you like
|
|
25
|
+
debug: zod_1.z
|
|
26
|
+
.object({
|
|
27
|
+
key: zod_1.z.number(),
|
|
28
|
+
channel: zod_1.z.number(),
|
|
29
|
+
flags: zod_1.z.number(),
|
|
30
|
+
})
|
|
31
|
+
.optional(),
|
|
32
|
+
});
|
|
33
|
+
/* ------------------------------- Bit layout ---------------------------------
|
|
34
|
+
|
|
35
|
+
bits 0..2 CHANNEL
|
|
36
|
+
000 OTHER
|
|
37
|
+
001 GET_SSE (GET + Accept: text/event-stream)
|
|
38
|
+
010 POST_INIT_JSON (POST initialize + Accept: application/json or default JSON)
|
|
39
|
+
011 POST_INIT_SSE (POST initialize + Accept: text/event-stream)
|
|
40
|
+
100 POST_JSON (POST non-init + Accept: application/json or default JSON)
|
|
41
|
+
101 POST_SSE (POST non-init + Accept: text/event-stream)
|
|
42
|
+
110 POST_MESSAGE (POST to /message) ← legacy bridge
|
|
43
|
+
|
|
44
|
+
bit 3 HAS_SESSION (Mcp-Session-Id present)
|
|
45
|
+
bit 4 STATELESS_EN (config: enableStatelessHttp)
|
|
46
|
+
bit 5 REQ_SESSION (config: requireSessionForStreamable)
|
|
47
|
+
bit 6 LEGACY_HINT (x-legacy-sse=true OR session.transportType=legacy-sse)
|
|
48
|
+
bit 7 SSE_LISTENER_EN (config: enableSseListener)
|
|
49
|
+
bit 8 STREAMABLE_EN (config: enableStreamableHttp)
|
|
50
|
+
bit 9 STATEFUL_EN (config: enableStatefulHttp)
|
|
51
|
+
bit 10 LEGACY_EN (config: enableLegacySSE)
|
|
52
|
+
|
|
53
|
+
----------------------------------------------------------------------------- */
|
|
54
|
+
// --- Channels ---------------------------------------------------------------
|
|
55
|
+
const CH_OTHER = 0b000;
|
|
56
|
+
const CH_GET_SSE = 0b001;
|
|
57
|
+
const CH_POST_INIT_JSON = 0b010;
|
|
58
|
+
const CH_POST_INIT_SSE = 0b011;
|
|
59
|
+
const CH_POST_JSON = 0b100;
|
|
60
|
+
const CH_POST_SSE = 0b101;
|
|
61
|
+
const CH_POST_MESSAGE = 0b110; // NEW: POST /message (legacy bridge)
|
|
62
|
+
const CH_MASK = 0b00000111;
|
|
63
|
+
// --- Flags ------------------------------------------------------------------
|
|
64
|
+
const B_HAS_SESSION = 1 << 3; // 8
|
|
65
|
+
const B_STATELESS_EN = 1 << 4; // 16
|
|
66
|
+
const B_REQ_SESSION = 1 << 5; // 32
|
|
67
|
+
const B_LEGACY_HINT = 1 << 6; // 64 (kept for telemetry; not required by merged rules)
|
|
68
|
+
const B_SSE_LISTENER_EN = 1 << 7; // 128
|
|
69
|
+
const B_STREAMABLE_EN = 1 << 8; // 256
|
|
70
|
+
const B_STATEFUL_EN = 1 << 9; // 512
|
|
71
|
+
const B_LEGACY_EN = 1 << 10; // 1024
|
|
72
|
+
// --- Minimal helpers ---------------------------------------------------------
|
|
73
|
+
const h = (req, name) => Object.entries(req.headers).find(([k]) => k.toLowerCase() === name.toLowerCase())?.[1];
|
|
74
|
+
const wantsSSE = (accept) => (accept ?? '').toLowerCase().includes('text/event-stream');
|
|
75
|
+
const wantsJSON = (accept) => (accept ?? '').toLowerCase().includes('application/json');
|
|
76
|
+
const isInitialize = (body) => !!body && typeof body === 'object' && body.method === 'initialize';
|
|
77
|
+
// Robust path extractor (supports absolute/relative)
|
|
78
|
+
function pathOf(req) {
|
|
79
|
+
const anyReq = req;
|
|
80
|
+
const raw = anyReq.path ?? anyReq.pathname ?? anyReq.url ?? '/';
|
|
81
|
+
try {
|
|
82
|
+
const u = new URL(String(raw), 'http://local');
|
|
83
|
+
return u.pathname || '/';
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return String(raw).split('?')[0] || '/';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Optionally extract transportType from base64 JSON session id, if you embed it. */
|
|
90
|
+
function tryDecodeTransportType(sessionId) {
|
|
91
|
+
if (!sessionId)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
const b64 = sessionId.replace(/-/g, '+').replace(/_/g, '/');
|
|
95
|
+
const obj = JSON.parse(Buffer.from(b64, 'base64').toString('utf8'));
|
|
96
|
+
return obj?.transportType;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// --- Build the 11-bit key ----------------------------------------------------
|
|
103
|
+
function computeBitmap(req, cfg) {
|
|
104
|
+
const method = req.method.toUpperCase();
|
|
105
|
+
const accept = h(req, 'accept');
|
|
106
|
+
const sessionId = h(req, 'mcp-session-id');
|
|
107
|
+
const legacyHeader = h(req, 'x-legacy-sse') === 'true';
|
|
108
|
+
const transportType = tryDecodeTransportType(sessionId);
|
|
109
|
+
const path = pathOf(req);
|
|
110
|
+
const acceptSSE = wantsSSE(accept);
|
|
111
|
+
const acceptJSON = wantsJSON(accept) || (!accept && cfg.tolerateMissingAccept);
|
|
112
|
+
const init = method === 'POST' && isInitialize(req.body);
|
|
113
|
+
const postToMessage = method === 'POST' && path === '/message';
|
|
114
|
+
const channel = method === 'POST' && postToMessage
|
|
115
|
+
? CH_POST_MESSAGE
|
|
116
|
+
: method === 'GET' && acceptSSE
|
|
117
|
+
? CH_GET_SSE
|
|
118
|
+
: method === 'POST' && init && acceptSSE
|
|
119
|
+
? CH_POST_INIT_SSE
|
|
120
|
+
: method === 'POST' && init && acceptJSON
|
|
121
|
+
? CH_POST_INIT_JSON
|
|
122
|
+
: method === 'POST' && !init && acceptSSE
|
|
123
|
+
? CH_POST_SSE
|
|
124
|
+
: method === 'POST' && !init && acceptJSON
|
|
125
|
+
? CH_POST_JSON
|
|
126
|
+
: CH_OTHER;
|
|
127
|
+
let flags = 0;
|
|
128
|
+
if (sessionId)
|
|
129
|
+
flags |= B_HAS_SESSION;
|
|
130
|
+
if (cfg.enableStatelessHttp)
|
|
131
|
+
flags |= B_STATELESS_EN;
|
|
132
|
+
if (cfg.requireSessionForStreamable)
|
|
133
|
+
flags |= B_REQ_SESSION;
|
|
134
|
+
if (legacyHeader || transportType === 'legacy-sse')
|
|
135
|
+
flags |= B_LEGACY_HINT; // informational
|
|
136
|
+
if (cfg.enableSseListener)
|
|
137
|
+
flags |= B_SSE_LISTENER_EN;
|
|
138
|
+
if (cfg.enableStreamableHttp)
|
|
139
|
+
flags |= B_STREAMABLE_EN;
|
|
140
|
+
if (cfg.enableStatefulHttp)
|
|
141
|
+
flags |= B_STATEFUL_EN;
|
|
142
|
+
if (cfg.enableLegacySSE)
|
|
143
|
+
flags |= B_LEGACY_EN;
|
|
144
|
+
return { key: (channel & CH_MASK) | flags, channel, flags, init };
|
|
145
|
+
}
|
|
146
|
+
// --- Rules (merged semantics) -----------------------------------------------
|
|
147
|
+
const RULES = [
|
|
148
|
+
// A) Legacy SSE (GET SSE without session) → requires legacy enabled
|
|
149
|
+
{
|
|
150
|
+
care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,
|
|
151
|
+
match: CH_GET_SSE | B_LEGACY_EN, // HAS_SESSION must be 0; it's in 'care' but not in 'match'
|
|
152
|
+
outcome: { intent: 'legacy-sse', reason: 'GET SSE without Mcp-Session-Id → legacy SSE.' },
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,
|
|
156
|
+
match: CH_GET_SSE /* legacy disabled; HAS_SESSION=0 */,
|
|
157
|
+
outcome: {
|
|
158
|
+
intent: 'unknown',
|
|
159
|
+
reason: 'Legacy SSE disabled.',
|
|
160
|
+
recommendation: { httpStatus: 405, message: 'Legacy SSE disabled' },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
// B) Legacy SSE: POST /message WITH session id
|
|
164
|
+
{
|
|
165
|
+
care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,
|
|
166
|
+
match: CH_POST_MESSAGE | B_HAS_SESSION | B_LEGACY_EN,
|
|
167
|
+
outcome: { intent: 'legacy-sse', reason: 'POST /message with Mcp-Session-Id → legacy SSE bridge.' },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,
|
|
171
|
+
match: CH_POST_MESSAGE | B_HAS_SESSION /* legacy disabled */,
|
|
172
|
+
outcome: {
|
|
173
|
+
intent: 'unknown',
|
|
174
|
+
reason: 'Legacy /message endpoint disabled.',
|
|
175
|
+
recommendation: { httpStatus: 405, message: 'Legacy SSE disabled' },
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
// C) Modern SSE (GET SSE with session) → requires SSE listener enabled
|
|
179
|
+
{
|
|
180
|
+
care: CH_MASK | B_SSE_LISTENER_EN | B_HAS_SESSION,
|
|
181
|
+
match: CH_GET_SSE | B_HAS_SESSION /* listener disabled */,
|
|
182
|
+
outcome: {
|
|
183
|
+
intent: 'unknown',
|
|
184
|
+
reason: 'SSE listener disabled.',
|
|
185
|
+
recommendation: { httpStatus: 405, message: 'SSE listener disabled' },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
care: CH_MASK | B_SSE_LISTENER_EN | B_HAS_SESSION,
|
|
190
|
+
match: CH_GET_SSE | B_SSE_LISTENER_EN | B_HAS_SESSION,
|
|
191
|
+
outcome: { intent: 'sse', reason: 'GET SSE with Mcp-Session-Id.' },
|
|
192
|
+
},
|
|
193
|
+
// D) Initialize (POST → SSE)
|
|
194
|
+
{
|
|
195
|
+
care: CH_MASK | B_STREAMABLE_EN,
|
|
196
|
+
match: CH_POST_INIT_SSE /* streamable disabled */,
|
|
197
|
+
outcome: {
|
|
198
|
+
intent: 'unknown',
|
|
199
|
+
reason: 'Streamable HTTP disabled.',
|
|
200
|
+
recommendation: { httpStatus: 405, message: 'Streamable HTTP disabled' },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
care: CH_MASK | B_STREAMABLE_EN,
|
|
205
|
+
match: CH_POST_INIT_SSE | B_STREAMABLE_EN,
|
|
206
|
+
outcome: { intent: 'streamable-http', reason: 'Initialize with SSE.' },
|
|
207
|
+
},
|
|
208
|
+
// E) Initialize (POST → JSON)
|
|
209
|
+
{
|
|
210
|
+
care: CH_MASK | B_STREAMABLE_EN,
|
|
211
|
+
match: CH_POST_INIT_JSON /* streamable disabled */,
|
|
212
|
+
outcome: {
|
|
213
|
+
intent: 'unknown',
|
|
214
|
+
reason: 'JSON mode disabled.',
|
|
215
|
+
recommendation: { httpStatus: 405, message: 'JSON mode disabled' },
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
care: CH_MASK | B_STREAMABLE_EN,
|
|
220
|
+
match: CH_POST_INIT_JSON | B_STREAMABLE_EN,
|
|
221
|
+
outcome: { intent: 'stateful-http', reason: 'Initialize with JSON.' },
|
|
222
|
+
},
|
|
223
|
+
// F) POST non-init → SSE
|
|
224
|
+
{
|
|
225
|
+
care: CH_MASK | B_REQ_SESSION | B_HAS_SESSION | B_STATELESS_EN,
|
|
226
|
+
match: CH_POST_SSE | B_REQ_SESSION /* !HAS_SESSION & !STATELESS_EN */,
|
|
227
|
+
outcome: {
|
|
228
|
+
intent: 'unknown',
|
|
229
|
+
reason: 'POST SSE requires session.',
|
|
230
|
+
recommendation: { httpStatus: 400, message: 'Initialize required (no Mcp-Session-Id)' },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
care: CH_MASK | B_STREAMABLE_EN,
|
|
235
|
+
match: CH_POST_SSE /* streamable disabled */,
|
|
236
|
+
outcome: {
|
|
237
|
+
intent: 'unknown',
|
|
238
|
+
reason: 'Streamable HTTP disabled.',
|
|
239
|
+
recommendation: { httpStatus: 405, message: 'Streamable HTTP disabled' },
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
|
|
244
|
+
match: CH_POST_SSE | B_STATELESS_EN /* no session */,
|
|
245
|
+
outcome: { intent: 'stateless-http', reason: 'Stateless short-lived SSE.' },
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
care: CH_MASK,
|
|
249
|
+
match: CH_POST_SSE,
|
|
250
|
+
outcome: { intent: 'streamable-http', reason: 'Short-lived SSE for this request.' },
|
|
251
|
+
},
|
|
252
|
+
// G) POST non-init → JSON
|
|
253
|
+
{
|
|
254
|
+
care: CH_MASK | B_REQ_SESSION | B_HAS_SESSION | B_STATELESS_EN,
|
|
255
|
+
match: CH_POST_JSON | B_REQ_SESSION /* !HAS_SESSION & !STATELESS_EN */,
|
|
256
|
+
outcome: {
|
|
257
|
+
intent: 'unknown',
|
|
258
|
+
reason: 'POST JSON requires session.',
|
|
259
|
+
recommendation: { httpStatus: 400, message: 'Initialize required (no Mcp-Session-Id)' },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
care: CH_MASK | B_STATEFUL_EN | B_STREAMABLE_EN,
|
|
264
|
+
match: CH_POST_JSON /* neither enabled */,
|
|
265
|
+
outcome: {
|
|
266
|
+
intent: 'unknown',
|
|
267
|
+
reason: 'JSON mode disabled.',
|
|
268
|
+
recommendation: { httpStatus: 405, message: 'JSON mode disabled' },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
|
|
273
|
+
match: CH_POST_JSON | B_STATELESS_EN /* no session */,
|
|
274
|
+
outcome: { intent: 'stateless-http', reason: 'Stateless JSON request.' },
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
care: CH_MASK,
|
|
278
|
+
match: CH_POST_JSON,
|
|
279
|
+
outcome: { intent: 'stateful-http', reason: 'Aggregated JSON response.' },
|
|
280
|
+
},
|
|
281
|
+
// H) Fallback
|
|
282
|
+
{
|
|
283
|
+
care: CH_MASK,
|
|
284
|
+
match: CH_OTHER,
|
|
285
|
+
outcome: {
|
|
286
|
+
intent: 'unknown',
|
|
287
|
+
reason: 'Method/Accept not allowed.',
|
|
288
|
+
recommendation: { httpStatus: 405, message: 'Method/Accept not allowed' },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
// --- Public API --------------------------------------------------------------
|
|
293
|
+
function decideIntent(req, cfg) {
|
|
294
|
+
const reasons = [];
|
|
295
|
+
const { key, channel, flags, init } = computeBitmap(req, cfg);
|
|
296
|
+
// Optional strict Accept validation for POST (incl. /message)
|
|
297
|
+
if (req.method.toUpperCase() === 'POST') {
|
|
298
|
+
const accept = h(req, 'accept');
|
|
299
|
+
const acceptsSSE = wantsSSE(accept);
|
|
300
|
+
const acceptsJSON = wantsJSON(accept) || (!accept && cfg.tolerateMissingAccept);
|
|
301
|
+
if (!acceptsSSE && !acceptsJSON) {
|
|
302
|
+
return {
|
|
303
|
+
intent: 'unknown',
|
|
304
|
+
reasons: ['Client must accept application/json or text/event-stream.'],
|
|
305
|
+
recommendation: { httpStatus: 406, message: 'Not acceptable' },
|
|
306
|
+
debug: { key, channel, flags },
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
for (const rule of RULES) {
|
|
311
|
+
if ((key & rule.care) === rule.match) {
|
|
312
|
+
const { reason, ...rest } = rule.outcome;
|
|
313
|
+
reasons.push(reason);
|
|
314
|
+
if (init)
|
|
315
|
+
reasons.push('Initialize body detected.');
|
|
316
|
+
return { ...rest, reasons, debug: { key, channel, flags } };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
intent: 'unknown',
|
|
321
|
+
reasons: ['No matching rule.'],
|
|
322
|
+
recommendation: { httpStatus: 500, message: 'Unroutable request' },
|
|
323
|
+
debug: { key, channel, flags },
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=decide-request-intent.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decide-request-intent.utils.js","sourceRoot":"","sources":["../../../src/utils/decide-request-intent.utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { ServerRequest } from '@frontmcp/sdk';\nimport { z } from 'zod';\n\n/* --------------------------------- Schemas --------------------------------- */\n\nexport const intentSchema = z.union([\n z.literal('legacy-sse'),\n z.literal('sse'),\n z.literal('streamable-http'),\n z.literal('stateful-http'),\n z.literal('stateless-http'),\n z.literal('unknown'),\n]);\n\nexport const decisionSchema = z.object({\n intent: intentSchema,\n reasons: z.array(z.string()),\n recommendation: z\n .object({\n httpStatus: z.number(),\n message: z.string(),\n })\n .optional(),\n // Echo back bits for debugging/telemetry if you like\n debug: z\n .object({\n key: z.number(),\n channel: z.number(),\n flags: z.number(),\n })\n .optional(),\n});\n\nexport type HttpRequestIntent =\n | 'legacy-sse'\n | 'sse'\n | 'streamable-http'\n | 'stateful-http'\n | 'stateless-http';\n\nexport type Intent = HttpRequestIntent | 'unknown';\n\nexport type Decision = {\n intent: Intent;\n reasons: string[];\n recommendation?: { httpStatus: number; message: string };\n // Echo back bits for debugging/telemetry if you like\n debug?: { key: number; channel: number; flags: number };\n};\n\nexport interface Config {\n enableLegacySSE: boolean;\n enableSseListener: boolean;\n enableStreamableHttp: boolean;\n enableStatefulHttp: boolean;\n enableStatelessHttp: boolean;\n requireSessionForStreamable: boolean;\n tolerateMissingAccept: boolean;\n}\n\n/* ------------------------------- Bit layout ---------------------------------\n\nbits 0..2 CHANNEL\n 000 OTHER\n 001 GET_SSE (GET + Accept: text/event-stream)\n 010 POST_INIT_JSON (POST initialize + Accept: application/json or default JSON)\n 011 POST_INIT_SSE (POST initialize + Accept: text/event-stream)\n 100 POST_JSON (POST non-init + Accept: application/json or default JSON)\n 101 POST_SSE (POST non-init + Accept: text/event-stream)\n 110 POST_MESSAGE (POST to /message) ← legacy bridge\n\nbit 3 HAS_SESSION (Mcp-Session-Id present)\nbit 4 STATELESS_EN (config: enableStatelessHttp)\nbit 5 REQ_SESSION (config: requireSessionForStreamable)\nbit 6 LEGACY_HINT (x-legacy-sse=true OR session.transportType=legacy-sse)\nbit 7 SSE_LISTENER_EN (config: enableSseListener)\nbit 8 STREAMABLE_EN (config: enableStreamableHttp)\nbit 9 STATEFUL_EN (config: enableStatefulHttp)\nbit 10 LEGACY_EN (config: enableLegacySSE)\n\n----------------------------------------------------------------------------- */\n\n// --- Channels ---------------------------------------------------------------\n\nconst CH_OTHER = 0b000;\nconst CH_GET_SSE = 0b001;\nconst CH_POST_INIT_JSON = 0b010;\nconst CH_POST_INIT_SSE = 0b011;\nconst CH_POST_JSON = 0b100;\nconst CH_POST_SSE = 0b101;\nconst CH_POST_MESSAGE = 0b110; // NEW: POST /message (legacy bridge)\n\nconst CH_MASK = 0b00000111;\n\n// --- Flags ------------------------------------------------------------------\n\nconst B_HAS_SESSION = 1 << 3; // 8\nconst B_STATELESS_EN = 1 << 4; // 16\nconst B_REQ_SESSION = 1 << 5; // 32\nconst B_LEGACY_HINT = 1 << 6; // 64 (kept for telemetry; not required by merged rules)\nconst B_SSE_LISTENER_EN = 1 << 7; // 128\nconst B_STREAMABLE_EN = 1 << 8; // 256\nconst B_STATEFUL_EN = 1 << 9; // 512\nconst B_LEGACY_EN = 1 << 10; // 1024\n\n// --- Minimal helpers ---------------------------------------------------------\n\nconst h = (req: ServerRequest, name: string) =>\n Object.entries(req.headers).find(([k]) => k.toLowerCase() === name.toLowerCase())?.[1];\n\nconst wantsSSE = (accept?: string) => (accept ?? '').toLowerCase().includes('text/event-stream');\nconst wantsJSON = (accept?: string) => (accept ?? '').toLowerCase().includes('application/json');\nconst isInitialize = (body: unknown) =>\n !!body && typeof body === 'object' && (body as any).method === 'initialize';\n\n// Robust path extractor (supports absolute/relative)\nfunction pathOf(req: ServerRequest): string {\n const anyReq = req as any;\n const raw = anyReq.path ?? anyReq.pathname ?? anyReq.url ?? '/';\n try {\n const u = new URL(String(raw), 'http://local');\n return u.pathname || '/';\n } catch {\n return String(raw).split('?')[0] || '/';\n }\n}\n\n/** Optionally extract transportType from base64 JSON session id, if you embed it. */\nfunction tryDecodeTransportType(sessionId?: string): string | undefined {\n if (!sessionId) return;\n try {\n const b64 = sessionId.replace(/-/g, '+').replace(/_/g, '/');\n const obj = JSON.parse(Buffer.from(b64, 'base64').toString('utf8'));\n return obj?.transportType;\n } catch {\n return;\n }\n}\n\n// --- Build the 11-bit key ----------------------------------------------------\n\nfunction computeBitmap(req: ServerRequest, cfg: Config) {\n const method = req.method.toUpperCase();\n const accept = h(req, 'accept');\n const sessionId = h(req, 'mcp-session-id');\n const legacyHeader = h(req, 'x-legacy-sse') === 'true';\n const transportType = tryDecodeTransportType(sessionId);\n const path = pathOf(req);\n\n const acceptSSE = wantsSSE(accept);\n const acceptJSON = wantsJSON(accept) || (!accept && cfg.tolerateMissingAccept);\n const init = method === 'POST' && isInitialize(req.body);\n const postToMessage = method === 'POST' && path === '/message';\n\n const channel =\n method === 'POST' && postToMessage\n ? CH_POST_MESSAGE\n : method === 'GET' && acceptSSE\n ? CH_GET_SSE\n : method === 'POST' && init && acceptSSE\n ? CH_POST_INIT_SSE\n : method === 'POST' && init && acceptJSON\n ? CH_POST_INIT_JSON\n : method === 'POST' && !init && acceptSSE\n ? CH_POST_SSE\n : method === 'POST' && !init && acceptJSON\n ? CH_POST_JSON\n : CH_OTHER;\n\n let flags = 0;\n if (sessionId) flags |= B_HAS_SESSION;\n if (cfg.enableStatelessHttp) flags |= B_STATELESS_EN;\n if (cfg.requireSessionForStreamable) flags |= B_REQ_SESSION;\n if (legacyHeader || transportType === 'legacy-sse') flags |= B_LEGACY_HINT; // informational\n if (cfg.enableSseListener) flags |= B_SSE_LISTENER_EN;\n if (cfg.enableStreamableHttp) flags |= B_STREAMABLE_EN;\n if (cfg.enableStatefulHttp) flags |= B_STATEFUL_EN;\n if (cfg.enableLegacySSE) flags |= B_LEGACY_EN;\n\n return { key: (channel & CH_MASK) | flags, channel, flags, init };\n}\n\n// --- Rule definition ---------------------------------------------------------\n\ntype Rule = {\n care: number; // which bits matter\n match: number; // the exact bit pattern to match (after masking)\n outcome: Omit<Decision, 'reasons' | 'debug'> & { reason: string };\n};\n\n// --- Rules (merged semantics) -----------------------------------------------\n\nconst RULES: Rule[] = [\n // A) Legacy SSE (GET SSE without session) → requires legacy enabled\n {\n care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,\n match: CH_GET_SSE | B_LEGACY_EN, // HAS_SESSION must be 0; it's in 'care' but not in 'match'\n outcome: { intent: 'legacy-sse', reason: 'GET SSE without Mcp-Session-Id → legacy SSE.' },\n },\n {\n care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,\n match: CH_GET_SSE /* legacy disabled; HAS_SESSION=0 */,\n outcome: {\n intent: 'unknown',\n reason: 'Legacy SSE disabled.',\n recommendation: { httpStatus: 405, message: 'Legacy SSE disabled' },\n },\n },\n\n // B) Legacy SSE: POST /message WITH session id\n {\n care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,\n match: CH_POST_MESSAGE | B_HAS_SESSION | B_LEGACY_EN,\n outcome: { intent: 'legacy-sse', reason: 'POST /message with Mcp-Session-Id → legacy SSE bridge.' },\n },\n {\n care: CH_MASK | B_HAS_SESSION | B_LEGACY_EN,\n match: CH_POST_MESSAGE | B_HAS_SESSION /* legacy disabled */,\n outcome: {\n intent: 'unknown',\n reason: 'Legacy /message endpoint disabled.',\n recommendation: { httpStatus: 405, message: 'Legacy SSE disabled' },\n },\n },\n\n // C) Modern SSE (GET SSE with session) → requires SSE listener enabled\n {\n care: CH_MASK | B_SSE_LISTENER_EN | B_HAS_SESSION,\n match: CH_GET_SSE | B_HAS_SESSION /* listener disabled */,\n outcome: {\n intent: 'unknown',\n reason: 'SSE listener disabled.',\n recommendation: { httpStatus: 405, message: 'SSE listener disabled' },\n },\n },\n {\n care: CH_MASK | B_SSE_LISTENER_EN | B_HAS_SESSION,\n match: CH_GET_SSE | B_SSE_LISTENER_EN | B_HAS_SESSION,\n outcome: { intent: 'sse', reason: 'GET SSE with Mcp-Session-Id.' },\n },\n\n // D) Initialize (POST → SSE)\n {\n care: CH_MASK | B_STREAMABLE_EN,\n match: CH_POST_INIT_SSE /* streamable disabled */,\n outcome: {\n intent: 'unknown',\n reason: 'Streamable HTTP disabled.',\n recommendation: { httpStatus: 405, message: 'Streamable HTTP disabled' },\n },\n },\n {\n care: CH_MASK | B_STREAMABLE_EN,\n match: CH_POST_INIT_SSE | B_STREAMABLE_EN,\n outcome: { intent: 'streamable-http', reason: 'Initialize with SSE.' },\n },\n\n // E) Initialize (POST → JSON)\n {\n care: CH_MASK | B_STREAMABLE_EN,\n match: CH_POST_INIT_JSON /* streamable disabled */,\n outcome: {\n intent: 'unknown',\n reason: 'JSON mode disabled.',\n recommendation: { httpStatus: 405, message: 'JSON mode disabled' },\n },\n },\n {\n care: CH_MASK | B_STREAMABLE_EN,\n match: CH_POST_INIT_JSON | B_STREAMABLE_EN,\n outcome: { intent: 'stateful-http', reason: 'Initialize with JSON.' },\n },\n\n // F) POST non-init → SSE\n {\n care: CH_MASK | B_REQ_SESSION | B_HAS_SESSION | B_STATELESS_EN,\n match: CH_POST_SSE | B_REQ_SESSION /* !HAS_SESSION & !STATELESS_EN */,\n outcome: {\n intent: 'unknown',\n reason: 'POST SSE requires session.',\n recommendation: { httpStatus: 400, message: 'Initialize required (no Mcp-Session-Id)' },\n },\n },\n {\n care: CH_MASK | B_STREAMABLE_EN,\n match: CH_POST_SSE /* streamable disabled */,\n outcome: {\n intent: 'unknown',\n reason: 'Streamable HTTP disabled.',\n recommendation: { httpStatus: 405, message: 'Streamable HTTP disabled' },\n },\n },\n {\n care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,\n match: CH_POST_SSE | B_STATELESS_EN /* no session */,\n outcome: { intent: 'stateless-http', reason: 'Stateless short-lived SSE.' },\n },\n {\n care: CH_MASK,\n match: CH_POST_SSE,\n outcome: { intent: 'streamable-http', reason: 'Short-lived SSE for this request.' },\n },\n\n // G) POST non-init → JSON\n {\n care: CH_MASK | B_REQ_SESSION | B_HAS_SESSION | B_STATELESS_EN,\n match: CH_POST_JSON | B_REQ_SESSION /* !HAS_SESSION & !STATELESS_EN */,\n outcome: {\n intent: 'unknown',\n reason: 'POST JSON requires session.',\n recommendation: { httpStatus: 400, message: 'Initialize required (no Mcp-Session-Id)' },\n },\n },\n {\n care: CH_MASK | B_STATEFUL_EN | B_STREAMABLE_EN,\n match: CH_POST_JSON /* neither enabled */,\n outcome: {\n intent: 'unknown',\n reason: 'JSON mode disabled.',\n recommendation: { httpStatus: 405, message: 'JSON mode disabled' },\n },\n },\n {\n care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,\n match: CH_POST_JSON | B_STATELESS_EN /* no session */,\n outcome: { intent: 'stateless-http', reason: 'Stateless JSON request.' },\n },\n {\n care: CH_MASK,\n match: CH_POST_JSON,\n outcome: { intent: 'stateful-http', reason: 'Aggregated JSON response.' },\n },\n\n // H) Fallback\n {\n care: CH_MASK,\n match: CH_OTHER,\n outcome: {\n intent: 'unknown',\n reason: 'Method/Accept not allowed.',\n recommendation: { httpStatus: 405, message: 'Method/Accept not allowed' },\n },\n },\n];\n\n// --- Public API --------------------------------------------------------------\n\nexport function decideIntent(req: ServerRequest, cfg: Config): Decision {\n const reasons: string[] = [];\n const { key, channel, flags, init } = computeBitmap(req, cfg);\n\n // Optional strict Accept validation for POST (incl. /message)\n if (req.method.toUpperCase() === 'POST') {\n const accept = h(req, 'accept');\n const acceptsSSE = wantsSSE(accept);\n const acceptsJSON = wantsJSON(accept) || (!accept && cfg.tolerateMissingAccept);\n if (!acceptsSSE && !acceptsJSON) {\n return {\n intent: 'unknown',\n reasons: ['Client must accept application/json or text/event-stream.'],\n recommendation: { httpStatus: 406, message: 'Not acceptable' },\n debug: { key, channel, flags },\n };\n }\n }\n\n for (const rule of RULES) {\n if ((key & rule.care) === rule.match) {\n const { reason, ...rest } = rule.outcome;\n reasons.push(reason);\n if (init) reasons.push('Initialize body detected.');\n return { ...rest, reasons, debug: { key, channel, flags } };\n }\n }\n\n return {\n intent: 'unknown',\n reasons: ['No matching rule.'],\n recommendation: { httpStatus: 500, message: 'Unroutable request' },\n debug: { key, channel, flags },\n };\n}"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./decide-request-intent.utils"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./path.utils"), exports);
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":";;;AAAA,wEAA6C;AAC7C,uDAA4B","sourcesContent":["export * from './decide-request-intent.utils'\nexport * from './path.utils'"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ServerRequest } from '@frontmcp/sdk';
|
|
2
|
+
export declare function trimSlashes(s: string): string;
|
|
3
|
+
/** Normalize entryPath (gateway prefix) to "" or "/mcp" */
|
|
4
|
+
export declare function normalizeEntryPrefix(entryPath?: string): string;
|
|
5
|
+
/** Normalize a scope base (per-app or per-auth) to "" or "/app1" */
|
|
6
|
+
export declare function normalizeScopeBase(scopeBase?: string): string;
|
|
7
|
+
/** Join URL path segments with a single slash and no trailing slash */
|
|
8
|
+
export declare function joinPath(...parts: string[]): string;
|
|
9
|
+
export declare function getRequestBaseUrl(req: ServerRequest, entryPath?: string): string;
|
|
10
|
+
export declare function computeIssuer(req: ServerRequest, entryPath: string, scopeBase: string): string;
|
|
11
|
+
export declare function computeResource(req: ServerRequest, entryPath: string, scopeBase: string): string;
|
|
12
|
+
/** Derive a safe provider id from a URL when no id is provided. */
|
|
13
|
+
export declare function urlToSafeId(url: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Build all path variants for a given well-known name:
|
|
16
|
+
* - reversed under root: /.well-known/<name><entryPrefix><scopeBase>
|
|
17
|
+
* - in prefix root: <entryPrefix>/.well-known/<name><scopeBase>
|
|
18
|
+
* - in prefix + scope: <entryPrefix><scopeBase>/.well-known/<name>
|
|
19
|
+
*/
|
|
20
|
+
export declare function makeWellKnownPaths(name: string, entryPrefix: string, scopeBase?: string): Set<string>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// auth/path.utils.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.trimSlashes = trimSlashes;
|
|
5
|
+
exports.normalizeEntryPrefix = normalizeEntryPrefix;
|
|
6
|
+
exports.normalizeScopeBase = normalizeScopeBase;
|
|
7
|
+
exports.joinPath = joinPath;
|
|
8
|
+
exports.getRequestBaseUrl = getRequestBaseUrl;
|
|
9
|
+
exports.computeIssuer = computeIssuer;
|
|
10
|
+
exports.computeResource = computeResource;
|
|
11
|
+
exports.urlToSafeId = urlToSafeId;
|
|
12
|
+
exports.makeWellKnownPaths = makeWellKnownPaths;
|
|
13
|
+
function trimSlashes(s) {
|
|
14
|
+
return (s ?? '').replace(/^\/+|\/+$/g, '');
|
|
15
|
+
}
|
|
16
|
+
/** Normalize entryPath (gateway prefix) to "" or "/mcp" */
|
|
17
|
+
function normalizeEntryPrefix(entryPath) {
|
|
18
|
+
const t = trimSlashes(entryPath ?? '');
|
|
19
|
+
return t ? `/${t}` : '';
|
|
20
|
+
}
|
|
21
|
+
/** Normalize a scope base (per-app or per-auth) to "" or "/app1" */
|
|
22
|
+
function normalizeScopeBase(scopeBase) {
|
|
23
|
+
const t = trimSlashes(scopeBase ?? '');
|
|
24
|
+
return t ? `/${t}` : '';
|
|
25
|
+
}
|
|
26
|
+
/** Join URL path segments with a single slash and no trailing slash */
|
|
27
|
+
function joinPath(...parts) {
|
|
28
|
+
const cleaned = parts.map((p) => trimSlashes(p)).filter(Boolean);
|
|
29
|
+
return cleaned.length ? `/${cleaned.join('/')}` : '';
|
|
30
|
+
}
|
|
31
|
+
function getRequestBaseUrl(req, entryPath) {
|
|
32
|
+
const proto = req.headers['x-forwarded-proto'] || req.protocol || 'http';
|
|
33
|
+
const host = req.headers['x-forwarded-host'] || req.headers['host'];
|
|
34
|
+
return `${proto}://${host}${entryPath ?? ''}`;
|
|
35
|
+
}
|
|
36
|
+
function computeIssuer(req, entryPath, scopeBase) {
|
|
37
|
+
const entryPrefix = normalizeEntryPrefix(entryPath);
|
|
38
|
+
const scope = normalizeScopeBase(scopeBase);
|
|
39
|
+
return `${getRequestBaseUrl(req)}${entryPrefix}${scope}`;
|
|
40
|
+
}
|
|
41
|
+
function computeResource(req, entryPath, scopeBase) {
|
|
42
|
+
const entryPrefix = normalizeEntryPrefix(entryPath);
|
|
43
|
+
const scope = normalizeScopeBase(scopeBase);
|
|
44
|
+
return `${getRequestBaseUrl(req)}${entryPrefix}${scope}`;
|
|
45
|
+
}
|
|
46
|
+
/** Derive a safe provider id from a URL when no id is provided. */
|
|
47
|
+
function urlToSafeId(url) {
|
|
48
|
+
const u = new URL(url);
|
|
49
|
+
const raw = (u.host + (u.pathname && u.pathname !== '/' ? u.pathname : '')).replace(/\/+$/, '');
|
|
50
|
+
return trimSlashes(raw).replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build all path variants for a given well-known name:
|
|
54
|
+
* - reversed under root: /.well-known/<name><entryPrefix><scopeBase>
|
|
55
|
+
* - in prefix root: <entryPrefix>/.well-known/<name><scopeBase>
|
|
56
|
+
* - in prefix + scope: <entryPrefix><scopeBase>/.well-known/<name>
|
|
57
|
+
*/
|
|
58
|
+
function makeWellKnownPaths(name, entryPrefix, scopeBase = '') {
|
|
59
|
+
const prefix = normalizeEntryPrefix(entryPrefix);
|
|
60
|
+
const scope = normalizeScopeBase(scopeBase);
|
|
61
|
+
const reversed = joinPath('.well-known', name) + `${prefix}${scope}`; // /.well-known/name + /mcp/app1
|
|
62
|
+
const inPrefixRoot = `${prefix}${joinPath('.well-known', name)}${scope}`; // /mcp/.well-known/name + /app1
|
|
63
|
+
const inPrefixScope = `${prefix}${scope}${joinPath('.well-known', name)}`; // /mcp/app1/.well-known/name
|
|
64
|
+
return new Set([reversed, inPrefixRoot, inPrefixScope]);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=path.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.utils.js","sourceRoot":"","sources":["../../../src/utils/path.utils.ts"],"names":[],"mappings":";AAAA,qBAAqB;;AAIrB,kCAEC;AAGD,oDAGC;AAGD,gDAGC;AAGD,4BAGC;AAGD,8CAIC;AAED,sCAIC;AAED,0CAIC;AAGD,kCAIC;AAQD,gDAOC;AA7DD,SAAgB,WAAW,CAAC,CAAS;IACnC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,2DAA2D;AAC3D,SAAgB,oBAAoB,CAAC,SAAkB;IACrD,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED,oEAAoE;AACpE,SAAgB,kBAAkB,CAAC,SAAkB;IACnD,MAAM,CAAC,GAAG,WAAW,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED,uEAAuE;AACvE,SAAgB,QAAQ,CAAC,GAAG,KAAe;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC;AAGD,SAAgB,iBAAiB,CAAC,GAAkB,EAAE,SAAkB;IACtE,MAAM,KAAK,GAAI,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAY,IAAK,GAAW,CAAC,QAAQ,IAAI,MAAM,CAAC;IAC9F,MAAM,IAAI,GAAI,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAY,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChF,OAAO,GAAG,KAAK,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,EAAE,CAAC;AAChD,CAAC;AAED,SAAgB,aAAa,CAAC,GAAkB,EAAE,SAAiB,EAAE,SAAiB;IACpF,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;AAC3D,CAAC;AAED,SAAgB,eAAe,CAAC,GAAkB,EAAE,SAAiB,EAAE,SAAiB;IACtF,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;AAC3D,CAAC;AAED,mEAAmE;AACnE,SAAgB,WAAW,CAAC,GAAW;IACrC,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChG,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,IAAY,EAAE,WAAmB,EAAE,SAAS,GAAG,EAAE;IAClF,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,gCAAgC;IACtG,MAAM,YAAY,GAAG,GAAG,MAAM,GAAG,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,gCAAgC;IAC1G,MAAM,aAAa,GAAG,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,6BAA6B;IACxG,OAAO,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["// auth/path.utils.ts\n\nimport { ServerRequest } from '@frontmcp/sdk';\n\nexport function trimSlashes(s: string) {\n return (s ?? '').replace(/^\\/+|\\/+$/g, '');\n}\n\n/** Normalize entryPath (gateway prefix) to \"\" or \"/mcp\" */\nexport function normalizeEntryPrefix(entryPath?: string): string {\n const t = trimSlashes(entryPath ?? '');\n return t ? `/${t}` : '';\n}\n\n/** Normalize a scope base (per-app or per-auth) to \"\" or \"/app1\" */\nexport function normalizeScopeBase(scopeBase?: string): string {\n const t = trimSlashes(scopeBase ?? '');\n return t ? `/${t}` : '';\n}\n\n/** Join URL path segments with a single slash and no trailing slash */\nexport function joinPath(...parts: string[]) {\n const cleaned = parts.map((p) => trimSlashes(p)).filter(Boolean);\n return cleaned.length ? `/${cleaned.join('/')}` : '';\n}\n\n\nexport function getRequestBaseUrl(req: ServerRequest, entryPath?: string) {\n const proto = (req.headers['x-forwarded-proto'] as string) || (req as any).protocol || 'http';\n const host = (req.headers['x-forwarded-host'] as string) || req.headers['host'];\n return `${proto}://${host}${entryPath ?? ''}`;\n}\n\nexport function computeIssuer(req: ServerRequest, entryPath: string, scopeBase: string) {\n const entryPrefix = normalizeEntryPrefix(entryPath);\n const scope = normalizeScopeBase(scopeBase);\n return `${getRequestBaseUrl(req)}${entryPrefix}${scope}`;\n}\n\nexport function computeResource(req: ServerRequest, entryPath: string, scopeBase: string) {\n const entryPrefix = normalizeEntryPrefix(entryPath);\n const scope = normalizeScopeBase(scopeBase);\n return `${getRequestBaseUrl(req)}${entryPrefix}${scope}`;\n}\n\n/** Derive a safe provider id from a URL when no id is provided. */\nexport function urlToSafeId(url: string): string {\n const u = new URL(url);\n const raw = (u.host + (u.pathname && u.pathname !== '/' ? u.pathname : '')).replace(/\\/+$/, '');\n return trimSlashes(raw).replace(/[^a-zA-Z0-9_-]/g, '-');\n}\n\n/**\n * Build all path variants for a given well-known name:\n * - reversed under root: /.well-known/<name><entryPrefix><scopeBase>\n * - in prefix root: <entryPrefix>/.well-known/<name><scopeBase>\n * - in prefix + scope: <entryPrefix><scopeBase>/.well-known/<name>\n */\nexport function makeWellKnownPaths(name: string, entryPrefix: string, scopeBase = '') {\n const prefix = normalizeEntryPrefix(entryPrefix);\n const scope = normalizeScopeBase(scopeBase);\n const reversed = joinPath('.well-known', name) + `${prefix}${scope}`; // /.well-known/name + /mcp/app1\n const inPrefixRoot = `${prefix}${joinPath('.well-known', name)}${scope}`; // /mcp/.well-known/name + /app1\n const inPrefixScope = `${prefix}${scope}${joinPath('.well-known', name)}`; // /mcp/app1/.well-known/name\n return new Set([reversed, inPrefixRoot, inPrefixScope]);\n}\n"]}
|