@frontmcp/sdk 1.8.3 → 1.8.4
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/agent/agent-execution-loop.d.ts.map +1 -1
- package/agent/agent.instance.d.ts +6 -1
- package/agent/agent.instance.d.ts.map +1 -1
- package/auth/flows/auth.verify.flow.d.ts.map +1 -1
- package/auth/flows/oauth.authorize.flow.d.ts +20 -11
- package/auth/flows/oauth.authorize.flow.d.ts.map +1 -1
- package/auth/flows/oauth.callback.flow.d.ts +3 -0
- package/auth/flows/oauth.callback.flow.d.ts.map +1 -1
- package/auth/flows/oauth.token.flow.d.ts.map +1 -1
- package/auth/flows/oauth.userinfo.flow.d.ts.map +1 -1
- package/auth/flows/session.verify.flow.d.ts.map +1 -1
- package/auth/flows/well-known.oauth-authorization-server.flow.d.ts.map +1 -1
- package/auth/flows/well-known.prm.flow.d.ts.map +1 -1
- package/auth/instances/instance.local-primary-auth.d.ts +37 -6
- package/auth/instances/instance.local-primary-auth.d.ts.map +1 -1
- package/auth/secure-store/secure-store.providers.d.ts +8 -5
- package/auth/secure-store/secure-store.providers.d.ts.map +1 -1
- package/auth/session-scope-identity.d.ts +18 -0
- package/auth/session-scope-identity.d.ts.map +1 -0
- package/auth/vault/auth-providers.providers.d.ts +2 -4
- package/auth/vault/auth-providers.providers.d.ts.map +1 -1
- package/channel/sources/webhook.source.d.ts +1 -1
- package/channel/sources/webhook.source.d.ts.map +1 -1
- package/common/availability/availability.utils.d.ts +4 -2
- package/common/availability/availability.utils.d.ts.map +1 -1
- package/common/decorators/hook.decorator.d.ts +2 -0
- package/common/decorators/hook.decorator.d.ts.map +1 -1
- package/common/interfaces/agent.interface.d.ts +8 -0
- package/common/interfaces/agent.interface.d.ts.map +1 -1
- package/common/interfaces/channel.interface.d.ts +6 -0
- package/common/interfaces/channel.interface.d.ts.map +1 -1
- package/common/interfaces/execution-context.interface.d.ts +12 -0
- package/common/interfaces/execution-context.interface.d.ts.map +1 -1
- package/common/interfaces/internal/primary-auth-provider.interface.d.ts +5 -1
- package/common/interfaces/internal/primary-auth-provider.interface.d.ts.map +1 -1
- package/common/interfaces/job.interface.d.ts +3 -0
- package/common/interfaces/job.interface.d.ts.map +1 -1
- package/common/interfaces/server.interface.d.ts +2 -0
- package/common/interfaces/server.interface.d.ts.map +1 -1
- package/common/metadata/plugin.metadata.d.ts +9 -0
- package/common/metadata/plugin.metadata.d.ts.map +1 -1
- package/common/tokens/plugin.tokens.d.ts +1 -0
- package/common/tokens/plugin.tokens.d.ts.map +1 -1
- package/common/utils/auth-info.utils.d.ts +26 -1
- package/common/utils/auth-info.utils.d.ts.map +1 -1
- package/common/utils/guard.utils.d.ts +2 -0
- package/common/utils/guard.utils.d.ts.map +1 -1
- package/context/call-surface.d.ts +6 -4
- package/context/call-surface.d.ts.map +1 -1
- package/context/frontmcp-context.d.ts +9 -1
- package/context/frontmcp-context.d.ts.map +1 -1
- package/direct/direct-server.d.ts.map +1 -1
- package/elicitation/helpers/elicit.helper.d.ts.map +1 -1
- package/elicitation/send-elicitation-result.tool.d.ts.map +1 -1
- package/errors/error-handler.d.ts.map +1 -1
- package/errors/index.d.ts +1 -0
- package/errors/index.d.ts.map +1 -1
- package/errors/mcp.error.d.ts.map +1 -1
- package/errors/session-identity-required.error.d.ts +20 -0
- package/errors/session-identity-required.error.d.ts.map +1 -0
- package/esm/index.mjs +873 -223
- package/flows/flow.http-path.d.ts +21 -0
- package/flows/flow.http-path.d.ts.map +1 -0
- package/flows/flow.instance.d.ts.map +1 -1
- package/front-mcp/front-mcp.d.ts +5 -0
- package/front-mcp/front-mcp.d.ts.map +1 -1
- package/front-mcp/static-startup.check.d.ts +12 -0
- package/front-mcp/static-startup.check.d.ts.map +1 -0
- package/hooks/hook-coverage.d.ts +29 -0
- package/hooks/hook-coverage.d.ts.map +1 -0
- package/index.d.ts +9 -5
- package/index.d.ts.map +1 -1
- package/index.js +1366 -715
- package/job/execution/job-execution.manager.d.ts.map +1 -1
- package/notification/notification.service.d.ts.map +1 -1
- package/package.json +10 -10
- package/prompt/flows/get-prompt.flow.d.ts.map +1 -1
- package/scope/flows/http.request.flow.d.ts +25 -1
- package/scope/flows/http.request.flow.d.ts.map +1 -1
- package/scope/scope.instance.d.ts.map +1 -1
- package/skill/sep-2640/sep-2640.per-skill.d.ts.map +1 -1
- package/skill/skill-filter.helper.d.ts +6 -2
- package/skill/skill-filter.helper.d.ts.map +1 -1
- package/skill/skill-scope.helper.d.ts +11 -1
- package/skill/skill-scope.helper.d.ts.map +1 -1
- package/skill/skill.registry.d.ts +27 -0
- package/skill/skill.registry.d.ts.map +1 -1
- package/task/helpers/task-runner.d.ts.map +1 -1
- package/task/runtime/execute-task.d.ts.map +1 -1
- package/task/task.types.d.ts +7 -0
- package/task/task.types.d.ts.map +1 -1
- package/tool/flows/call-tool.flow.d.ts.map +1 -1
- package/transport/flows/handle.mcp-20260728.flow.d.ts.map +1 -1
- package/transport/flows/handle.streamable-http.flow.d.ts.map +1 -1
- package/transport/in-memory-server.d.ts.map +1 -1
- package/transport/index.d.ts +1 -0
- package/transport/index.d.ts.map +1 -1
- package/transport/mcp-handlers/index.d.ts.map +1 -1
- package/transport/persistent-session-owner.d.ts +41 -0
- package/transport/persistent-session-owner.d.ts.map +1 -0
- package/transport/web-fetch-handler.d.ts.map +1 -1
- package/transport/web-standard-mcp.d.ts +4 -0
- package/transport/web-standard-mcp.d.ts.map +1 -1
- package/workflow/engine/workflow-step.executor.d.ts.map +1 -1
package/esm/index.mjs
CHANGED
|
@@ -110,7 +110,7 @@ function extractPublicMessageImpl(error, visited) {
|
|
|
110
110
|
}
|
|
111
111
|
return error == null ? "Unknown error" : String(error);
|
|
112
112
|
}
|
|
113
|
-
var MCP_ERROR_CODES, McpError, PublicMcpError, InternalMcpError, ToolNotFoundError, ToolNotConsentedError, EntryUnavailableError, ResourceNotFoundError, ResourceReadError, InvalidResourceUriError, InvalidInputError, InvalidOutputError, InvalidMethodError, PayloadTooLargeError, ToolExecutionError, RateLimitError, QuotaExceededError, UnauthorizedError, GenericServerError, DependencyNotFoundError, InvalidHookFlowError, InvalidPluginScopeError, RequestContextNotAvailableError, AuthConfigurationError, PromptNotFoundError, PromptExecutionError, SessionMissingError, UnsupportedClientVersionError, GlobalConfigNotFoundError, AuthorityDeniedMcpError, GuardLimitMcpError;
|
|
113
|
+
var MCP_ERROR_CODES, sequentialErrorIds, McpError, PublicMcpError, InternalMcpError, ToolNotFoundError, ToolNotConsentedError, EntryUnavailableError, ResourceNotFoundError, ResourceReadError, InvalidResourceUriError, InvalidInputError, InvalidOutputError, InvalidMethodError, PayloadTooLargeError, ToolExecutionError, RateLimitError, QuotaExceededError, UnauthorizedError, GenericServerError, DependencyNotFoundError, InvalidHookFlowError, InvalidPluginScopeError, RequestContextNotAvailableError, AuthConfigurationError, PromptNotFoundError, PromptExecutionError, SessionMissingError, UnsupportedClientVersionError, GlobalConfigNotFoundError, AuthorityDeniedMcpError, GuardLimitMcpError;
|
|
114
114
|
var init_mcp_error = __esm({
|
|
115
115
|
"libs/sdk/src/errors/mcp.error.ts"() {
|
|
116
116
|
"use strict";
|
|
@@ -132,6 +132,7 @@ var init_mcp_error = __esm({
|
|
|
132
132
|
/** Parse error (-32700) */
|
|
133
133
|
PARSE_ERROR: -32700
|
|
134
134
|
};
|
|
135
|
+
sequentialErrorIds = 0;
|
|
135
136
|
McpError = class extends Error {
|
|
136
137
|
/**
|
|
137
138
|
* Unique error ID for tracking in logs
|
|
@@ -144,7 +145,12 @@ var init_mcp_error = __esm({
|
|
|
144
145
|
Error.captureStackTrace(this, this.constructor);
|
|
145
146
|
}
|
|
146
147
|
generateErrorId() {
|
|
147
|
-
|
|
148
|
+
try {
|
|
149
|
+
return `err_${bytesToHex(randomBytes(8))}`;
|
|
150
|
+
} catch {
|
|
151
|
+
sequentialErrorIds += 1;
|
|
152
|
+
return `err_${sequentialErrorIds.toString(16).padStart(16, "0")}`;
|
|
153
|
+
}
|
|
148
154
|
}
|
|
149
155
|
/**
|
|
150
156
|
* Get the internal error message (for logging)
|
|
@@ -865,7 +871,8 @@ var init_plugin_tokens = __esm({
|
|
|
865
871
|
skills: tokenFactory.meta("skills"),
|
|
866
872
|
scope: tokenFactory.meta("scope"),
|
|
867
873
|
contextExtensions: tokenFactory.meta("contextExtensions"),
|
|
868
|
-
enforcesMetadata: tokenFactory.meta("enforcesMetadata")
|
|
874
|
+
enforcesMetadata: tokenFactory.meta("enforcesMetadata"),
|
|
875
|
+
dynamicSkills: tokenFactory.meta("dynamicSkills")
|
|
869
876
|
};
|
|
870
877
|
}
|
|
871
878
|
});
|
|
@@ -3436,7 +3443,8 @@ var init_plugin_metadata = __esm({
|
|
|
3436
3443
|
skills: z30.array(annotatedFrontMcpSkillsSchema).optional(),
|
|
3437
3444
|
scope: z30.enum(["app", "server"]).optional().default("app"),
|
|
3438
3445
|
contextExtensions: z30.array(contextExtensionSchema).optional(),
|
|
3439
|
-
enforcesMetadata: z30.array(z30.string().min(1)).optional()
|
|
3446
|
+
enforcesMetadata: z30.array(z30.string().min(1)).optional(),
|
|
3447
|
+
dynamicSkills: z30.boolean().optional()
|
|
3440
3448
|
}).passthrough();
|
|
3441
3449
|
}
|
|
3442
3450
|
});
|
|
@@ -4571,6 +4579,25 @@ var init_tool_credentials_required_error = __esm({
|
|
|
4571
4579
|
}
|
|
4572
4580
|
});
|
|
4573
4581
|
|
|
4582
|
+
// libs/sdk/src/errors/session-identity-required.error.ts
|
|
4583
|
+
var SessionIdentityRequiredError;
|
|
4584
|
+
var init_session_identity_required_error = __esm({
|
|
4585
|
+
"libs/sdk/src/errors/session-identity-required.error.ts"() {
|
|
4586
|
+
"use strict";
|
|
4587
|
+
init_mcp_error();
|
|
4588
|
+
SessionIdentityRequiredError = class extends PublicMcpError {
|
|
4589
|
+
mcpErrorCode = MCP_ERROR_CODES.FORBIDDEN;
|
|
4590
|
+
constructor(what = "Session-scoped storage") {
|
|
4591
|
+
super(
|
|
4592
|
+
`${what} needs a verified session or a signed-in caller. Authenticate the request, or use a session transport (an MCP revision before 2026-07-28).`,
|
|
4593
|
+
"SESSION_IDENTITY_REQUIRED",
|
|
4594
|
+
403
|
|
4595
|
+
);
|
|
4596
|
+
}
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
});
|
|
4600
|
+
|
|
4574
4601
|
// libs/sdk/src/errors/error-handler.ts
|
|
4575
4602
|
import { isProduction as isProduction2 } from "@frontmcp/utils";
|
|
4576
4603
|
function createErrorHandler(options = {}) {
|
|
@@ -5932,6 +5959,7 @@ __export(errors_exports, {
|
|
|
5932
5959
|
ServerlessHandlerNotInitializedError: () => ServerlessHandlerNotInitializedError,
|
|
5933
5960
|
SessionClaimConflictError: () => SessionClaimConflictError,
|
|
5934
5961
|
SessionIdEmptyError: () => SessionIdEmptyError,
|
|
5962
|
+
SessionIdentityRequiredError: () => SessionIdentityRequiredError,
|
|
5935
5963
|
SessionMissingError: () => SessionMissingError,
|
|
5936
5964
|
SessionSecretRequiredError: () => SessionSecretRequiredError,
|
|
5937
5965
|
SessionVerificationFailedError: () => SessionVerificationFailedError,
|
|
@@ -5986,6 +6014,7 @@ var init_errors = __esm({
|
|
|
5986
6014
|
init_mcp_error();
|
|
5987
6015
|
init_authorization_required_error();
|
|
5988
6016
|
init_tool_credentials_required_error();
|
|
6017
|
+
init_session_identity_required_error();
|
|
5989
6018
|
init_error_handler();
|
|
5990
6019
|
init_agent_errors();
|
|
5991
6020
|
init_task_error();
|
|
@@ -6468,30 +6497,30 @@ var require_dist = __commonJS({
|
|
|
6468
6497
|
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
|
|
6469
6498
|
});
|
|
6470
6499
|
module.exports = __toCommonJS2(index_exports);
|
|
6471
|
-
var
|
|
6500
|
+
var import_utils113 = __require("@frontmcp/utils");
|
|
6472
6501
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
|
|
6473
6502
|
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
|
|
6474
6503
|
var KEY_LENGTH = 32;
|
|
6475
6504
|
function deriveEncryptionKey(secret) {
|
|
6476
6505
|
const ikm = new TextEncoder().encode(secret);
|
|
6477
|
-
return (0,
|
|
6506
|
+
return (0, import_utils113.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
|
|
6478
6507
|
}
|
|
6479
6508
|
var SEPARATOR = ":";
|
|
6480
6509
|
function encryptValue(key, plaintext) {
|
|
6481
|
-
const iv = (0,
|
|
6510
|
+
const iv = (0, import_utils113.randomBytes)(12);
|
|
6482
6511
|
const plaintextBytes = new TextEncoder().encode(plaintext);
|
|
6483
|
-
const { ciphertext, tag } = (0,
|
|
6484
|
-
return [(0,
|
|
6512
|
+
const { ciphertext, tag } = (0, import_utils113.encryptAesGcm)(key, plaintextBytes, iv);
|
|
6513
|
+
return [(0, import_utils113.base64urlEncode)(iv), (0, import_utils113.base64urlEncode)(tag), (0, import_utils113.base64urlEncode)(ciphertext)].join(SEPARATOR);
|
|
6485
6514
|
}
|
|
6486
6515
|
function decryptValue(key, encrypted) {
|
|
6487
6516
|
const parts = encrypted.split(SEPARATOR);
|
|
6488
6517
|
if (parts.length !== 3) {
|
|
6489
6518
|
throw new Error("Invalid encrypted value format");
|
|
6490
6519
|
}
|
|
6491
|
-
const iv = (0,
|
|
6492
|
-
const tag = (0,
|
|
6493
|
-
const ciphertext = (0,
|
|
6494
|
-
const plaintext = (0,
|
|
6520
|
+
const iv = (0, import_utils113.base64urlDecode)(parts[0]);
|
|
6521
|
+
const tag = (0, import_utils113.base64urlDecode)(parts[1]);
|
|
6522
|
+
const ciphertext = (0, import_utils113.base64urlDecode)(parts[2]);
|
|
6523
|
+
const plaintext = (0, import_utils113.decryptAesGcm)(key, ciphertext, iv, tag);
|
|
6495
6524
|
return new TextDecoder().decode(plaintext);
|
|
6496
6525
|
}
|
|
6497
6526
|
var import_utils210 = __require("@frontmcp/utils");
|
|
@@ -7405,8 +7434,8 @@ function detectStorageType(storage, configuredType) {
|
|
|
7405
7434
|
return "memory";
|
|
7406
7435
|
}
|
|
7407
7436
|
async function createTaskStore(options = {}) {
|
|
7408
|
-
const { storage: storageConfig, redis, keyPrefix = "mcp:task:", logger, isEdgeRuntime:
|
|
7409
|
-
if (
|
|
7437
|
+
const { storage: storageConfig, redis, keyPrefix = "mcp:task:", logger, isEdgeRuntime: isEdgeRuntime3 = false, sqlite } = options;
|
|
7438
|
+
if (isEdgeRuntime3 && sqlite) {
|
|
7410
7439
|
throw new TaskStoreNotSupportedError(
|
|
7411
7440
|
"SQLite task store is not supported on Edge runtime (better-sqlite3 is a native module). Use Redis or Upstash."
|
|
7412
7441
|
);
|
|
@@ -7461,7 +7490,7 @@ async function createTaskStore(options = {}) {
|
|
|
7461
7490
|
"Vercel KV is not supported for task stores (pub/sub required). Use Redis or Upstash."
|
|
7462
7491
|
);
|
|
7463
7492
|
}
|
|
7464
|
-
if (
|
|
7493
|
+
if (isEdgeRuntime3 && (!finalStorageConfig || finalStorageConfig.type === "memory")) {
|
|
7465
7494
|
throw new TaskStoreNotSupportedError(
|
|
7466
7495
|
"Tasks require distributed storage on Edge runtime. Configure Redis or Upstash."
|
|
7467
7496
|
);
|
|
@@ -7469,7 +7498,7 @@ async function createTaskStore(options = {}) {
|
|
|
7469
7498
|
const storage = await createStorage({
|
|
7470
7499
|
...finalStorageConfig,
|
|
7471
7500
|
prefix: keyPrefix,
|
|
7472
|
-
fallback:
|
|
7501
|
+
fallback: isEdgeRuntime3 ? "error" : "memory"
|
|
7473
7502
|
});
|
|
7474
7503
|
const store = new StorageTaskStore(storage, logger);
|
|
7475
7504
|
const type = detectStorageType(storage, finalStorageConfig?.type);
|
|
@@ -7651,8 +7680,9 @@ var init_task_id = __esm({
|
|
|
7651
7680
|
});
|
|
7652
7681
|
|
|
7653
7682
|
// libs/sdk/src/task/helpers/task-runner.ts
|
|
7683
|
+
import { runRequestExclusive } from "@frontmcp/utils";
|
|
7654
7684
|
function runTaskInBackground(params) {
|
|
7655
|
-
return Promise.resolve().then(() => executeTask(params));
|
|
7685
|
+
return Promise.resolve().then(() => runRequestExclusive(() => executeTask(params)));
|
|
7656
7686
|
}
|
|
7657
7687
|
async function executeTask(params) {
|
|
7658
7688
|
const { record, cleanedRequestParams, ctx, scope, store, registry, notifier, logger } = params;
|
|
@@ -8694,6 +8724,7 @@ var init_get_prompt_request_handler = __esm({
|
|
|
8694
8724
|
import {
|
|
8695
8725
|
ListRootsResultSchema
|
|
8696
8726
|
} from "@frontmcp/protocol";
|
|
8727
|
+
import { awaitOutsideRequest } from "@frontmcp/utils";
|
|
8697
8728
|
function hasMcpAppsExtension(capabilities) {
|
|
8698
8729
|
if (!capabilities?.experimental) {
|
|
8699
8730
|
return false;
|
|
@@ -9410,9 +9441,11 @@ var init_notification_service = __esm({
|
|
|
9410
9441
|
}
|
|
9411
9442
|
try {
|
|
9412
9443
|
this.logger.verbose(`Requesting roots from client for session ${sessionId.slice(0, 20)}...`);
|
|
9413
|
-
const result = await
|
|
9414
|
-
|
|
9415
|
-
|
|
9444
|
+
const result = await awaitOutsideRequest(
|
|
9445
|
+
registered.server.request({ method: "roots/list" }, ListRootsResultSchema, {
|
|
9446
|
+
timeout: options?.timeout ?? 3e4
|
|
9447
|
+
})
|
|
9448
|
+
);
|
|
9416
9449
|
registered.cachedRoots = result.roots;
|
|
9417
9450
|
registered.rootsFetchedAt = Date.now();
|
|
9418
9451
|
this.logger.verbose(
|
|
@@ -10221,7 +10254,7 @@ function callerSurface(ctx, surface) {
|
|
|
10221
10254
|
}
|
|
10222
10255
|
async function filterServableSkills(scope, skills, ctx, surface) {
|
|
10223
10256
|
const caller = callerSurface(ctx, surface);
|
|
10224
|
-
const offered = skills.filter((skill) =>
|
|
10257
|
+
const offered = skills.filter((skill) => availabilityForCall(skill.metadata.availableWhen, caller) === "available");
|
|
10225
10258
|
if (offered.length === 0) return [];
|
|
10226
10259
|
const { skills: servable } = await scope.runFlowForOutput("skills:filter", { skills: [...offered], ctx });
|
|
10227
10260
|
const servableSkills = new Set(servable);
|
|
@@ -11889,7 +11922,8 @@ function createMcpHandlers(options) {
|
|
|
11889
11922
|
const hasPromptsOrResources = options.scope?.prompts?.hasAny() || options.scope?.resources?.hasAny();
|
|
11890
11923
|
const completionHandler = hasPromptsOrResources && options.serverOptions?.capabilities?.completions ? [completeRequestHandler(options)] : [];
|
|
11891
11924
|
const loggingHandler = options.serverOptions?.capabilities?.logging ? [LoggingSetLevelRequestHandler(options)] : [];
|
|
11892
|
-
const
|
|
11925
|
+
const skillRegistry = options.scope?.skills;
|
|
11926
|
+
const skillsHandler = skillRegistry?.servesSkills?.() ?? skillRegistry?.hasAny() ? [skillsSearchRequestHandler(options), skillsLoadRequestHandler(options), skillsListRequestHandler(options)] : [];
|
|
11893
11927
|
const tasksHandler = options.serverOptions?.capabilities?.["tasks"] ? [
|
|
11894
11928
|
tasksGetRequestHandler(options),
|
|
11895
11929
|
tasksResultRequestHandler(options),
|
|
@@ -11944,7 +11978,7 @@ var in_memory_server_exports = {};
|
|
|
11944
11978
|
__export(in_memory_server_exports, {
|
|
11945
11979
|
createInMemoryServer: () => createInMemoryServer
|
|
11946
11980
|
});
|
|
11947
|
-
import { randomUUID as randomUUID4 } from "@frontmcp/utils";
|
|
11981
|
+
import { randomUUID as randomUUID4, runRequestExclusive as runRequestExclusive2 } from "@frontmcp/utils";
|
|
11948
11982
|
async function createInMemoryServer(scope, options) {
|
|
11949
11983
|
const { InMemoryTransport, McpServer: McpServer2 } = await import("@frontmcp/protocol");
|
|
11950
11984
|
const { createMcpHandlers: createMcpHandlers2 } = await Promise.resolve().then(() => (init_mcp_handlers(), mcp_handlers_exports));
|
|
@@ -11964,7 +11998,7 @@ async function createInMemoryServer(scope, options) {
|
|
|
11964
11998
|
sessionId
|
|
11965
11999
|
}
|
|
11966
12000
|
};
|
|
11967
|
-
return originalHandler(request, enrichedCtx);
|
|
12001
|
+
return runRequestExclusive2(() => originalHandler(request, enrichedCtx));
|
|
11968
12002
|
};
|
|
11969
12003
|
mcpServer.setRequestHandler(handler.requestSchema, wrappedHandler);
|
|
11970
12004
|
}
|
|
@@ -12612,7 +12646,7 @@ var init_connect = __esm({
|
|
|
12612
12646
|
});
|
|
12613
12647
|
|
|
12614
12648
|
// libs/sdk/src/direct/direct-server.ts
|
|
12615
|
-
import { randomUUID as randomUUID6, sha256Hex } from "@frontmcp/utils";
|
|
12649
|
+
import { randomUUID as randomUUID6, runRequestExclusive as runRequestExclusive3, sha256Hex } from "@frontmcp/utils";
|
|
12616
12650
|
function buildAuthInfo(authContext, defaultSessionId) {
|
|
12617
12651
|
if (!authContext && !defaultSessionId) {
|
|
12618
12652
|
return void 0;
|
|
@@ -12687,7 +12721,7 @@ var init_direct_server = __esm({
|
|
|
12687
12721
|
}
|
|
12688
12722
|
const ctx = this.buildHandlerContext(options);
|
|
12689
12723
|
try {
|
|
12690
|
-
return await this.scope.runFlowForOutput(flowName, { request, ctx });
|
|
12724
|
+
return await runRequestExclusive3(() => this.scope.runFlowForOutput(flowName, { request, ctx }));
|
|
12691
12725
|
} catch (e) {
|
|
12692
12726
|
if (e instanceof FlowControl && e.type === "respond") {
|
|
12693
12727
|
return e.output;
|
|
@@ -14116,11 +14150,19 @@ var init_frontmcp_context = __esm({
|
|
|
14116
14150
|
updateAuthInfo(authInfo) {
|
|
14117
14151
|
this._authInfo = { ...this._authInfo, ...authInfo };
|
|
14118
14152
|
}
|
|
14119
|
-
/**
|
|
14153
|
+
/**
|
|
14154
|
+
* The session the server verified for this request; undefined for a request without one (MCP
|
|
14155
|
+
* 2026-07-28, the stateless transports, an `mcp-session-id` the server did not accept).
|
|
14156
|
+
*
|
|
14157
|
+
* Over HTTP that is the session session verification recorded (`authInfo.extra.sessionId`),
|
|
14158
|
+
* which it records only for a session the request presented: its `mcp-session-id` header, or the
|
|
14159
|
+
* `?sessionId=` a legacy SSE client posts its messages with. Over an in-process transport (stdio,
|
|
14160
|
+
* `connect()`, `createDirect()`) it is the session the transport runs the request in.
|
|
14161
|
+
*/
|
|
14120
14162
|
get verifiedSessionId() {
|
|
14121
|
-
|
|
14122
|
-
const verified = this._authInfo.sessionId
|
|
14123
|
-
return verified ===
|
|
14163
|
+
const recorded = this._authInfo.extra?.["sessionId"];
|
|
14164
|
+
const verified = typeof recorded === "string" && recorded.length > 0 ? recorded : this._authInfo.sessionId === this.sessionId ? this.sessionId : void 0;
|
|
14165
|
+
return verified === STATELESS_SESSION_ID ? void 0 : verified;
|
|
14124
14166
|
}
|
|
14125
14167
|
/**
|
|
14126
14168
|
* The client's self-reported identity, sent with this request (MCP 2026-07-28 `_meta`).
|
|
@@ -15863,6 +15905,9 @@ function registerHookOnClass(ctor, meta) {
|
|
|
15863
15905
|
function resolvePendingTC39HooksForClass(ctor) {
|
|
15864
15906
|
return pendingHookRegistry.resolveForClass(ctor);
|
|
15865
15907
|
}
|
|
15908
|
+
function peekPendingTC39HooksForClass(ctor) {
|
|
15909
|
+
return pendingHookRegistry.resolveForClass(ctor, false);
|
|
15910
|
+
}
|
|
15866
15911
|
function registerPendingTC39Hook(method, meta) {
|
|
15867
15912
|
pendingHookRegistry.store(method, meta);
|
|
15868
15913
|
}
|
|
@@ -16008,7 +16053,7 @@ var init_elicitation_types = __esm({
|
|
|
16008
16053
|
|
|
16009
16054
|
// libs/sdk/src/elicitation/helpers/elicit.helper.ts
|
|
16010
16055
|
import { toJSONSchema, z as z45 } from "@frontmcp/lazy-zod";
|
|
16011
|
-
import { randomUUID as randomUUID10 } from "@frontmcp/utils";
|
|
16056
|
+
import { awaitOutsideRequest as awaitOutsideRequest2, randomUUID as randomUUID10 } from "@frontmcp/utils";
|
|
16012
16057
|
async function performElicit(deps, message, requestedSchema, options) {
|
|
16013
16058
|
const { sessionId, getClientCapabilities, tryGetContext, entryName, entryInput, elicitationEnabled } = deps;
|
|
16014
16059
|
if (elicitationEnabled !== true) {
|
|
@@ -16056,7 +16101,7 @@ async function performElicit(deps, message, requestedSchema, options) {
|
|
|
16056
16101
|
if (!transport) {
|
|
16057
16102
|
throw new ElicitationNotSupportedError("Transport not available for elicitation");
|
|
16058
16103
|
}
|
|
16059
|
-
return transport.elicit(message, requestedSchema, options);
|
|
16104
|
+
return awaitOutsideRequest2(transport.elicit(message, requestedSchema, options));
|
|
16060
16105
|
}
|
|
16061
16106
|
function generateElicitationId() {
|
|
16062
16107
|
return `elicit-${randomUUID10()}`;
|
|
@@ -18224,12 +18269,12 @@ function qualifiedNameOf(lineage, name44) {
|
|
|
18224
18269
|
return `${ownerKeyOf(lineage)}:${name44}`;
|
|
18225
18270
|
}
|
|
18226
18271
|
function hookOwnerIdOf(lineage, owner) {
|
|
18227
|
-
const
|
|
18228
|
-
return appOwnerIdOf(
|
|
18272
|
+
const owners2 = owner ? [...lineage, owner] : lineage;
|
|
18273
|
+
return appOwnerIdOf(owners2) ?? owners2[owners2.length - 1]?.id;
|
|
18229
18274
|
}
|
|
18230
18275
|
function appOwnerIdOf(lineage, owner) {
|
|
18231
|
-
const
|
|
18232
|
-
return
|
|
18276
|
+
const owners2 = owner ? [...lineage, owner] : lineage;
|
|
18277
|
+
return owners2.find((candidate) => candidate.kind === "app")?.id;
|
|
18233
18278
|
}
|
|
18234
18279
|
var EntryLineageIndex;
|
|
18235
18280
|
var init_lineage_utils = __esm({
|
|
@@ -18327,6 +18372,7 @@ var init_get_prompt_flow = __esm({
|
|
|
18327
18372
|
init_remote_capabilities_utils();
|
|
18328
18373
|
init_common();
|
|
18329
18374
|
init_availability();
|
|
18375
|
+
init_call_surface();
|
|
18330
18376
|
init_errors();
|
|
18331
18377
|
init_resolved_entries();
|
|
18332
18378
|
init_hooks_utils();
|
|
@@ -18530,7 +18576,10 @@ var init_get_prompt_flow = __esm({
|
|
|
18530
18576
|
}
|
|
18531
18577
|
promptContext.mark("execute");
|
|
18532
18578
|
try {
|
|
18533
|
-
promptContext.output = await
|
|
18579
|
+
promptContext.output = await runOnSurface(
|
|
18580
|
+
callSurfaceOf(this.input.ctx),
|
|
18581
|
+
async () => promptContext.execute(parsedArgs)
|
|
18582
|
+
);
|
|
18534
18583
|
this.logger.verbose("execute:done");
|
|
18535
18584
|
} catch (error) {
|
|
18536
18585
|
if (error instanceof FlowControl || isClientFacingError(error)) throw error;
|
|
@@ -21615,6 +21664,7 @@ var init_send_elicitation_result_tool = __esm({
|
|
|
21615
21664
|
"libs/sdk/src/elicitation/send-elicitation-result.tool.ts"() {
|
|
21616
21665
|
"use strict";
|
|
21617
21666
|
init_common();
|
|
21667
|
+
init_call_surface();
|
|
21618
21668
|
init_errors();
|
|
21619
21669
|
init_fallback_helper();
|
|
21620
21670
|
inputSchema12 = {
|
|
@@ -21695,8 +21745,15 @@ var init_send_elicitation_result_tool = __esm({
|
|
|
21695
21745
|
arguments: pending.toolInput
|
|
21696
21746
|
}
|
|
21697
21747
|
},
|
|
21698
|
-
//
|
|
21699
|
-
|
|
21748
|
+
// The call context of this call, whose caller is the owner: its auth info as its transport
|
|
21749
|
+
// gave it (the tool reads it as `this.auth`), on the surface it was made from. Not the
|
|
21750
|
+
// request context object, whose auth info is a getter a call context does not carry over.
|
|
21751
|
+
ctx: {
|
|
21752
|
+
authInfo: this.authInfo,
|
|
21753
|
+
requestId: this.tryGetContext()?.requestId ?? this.runId,
|
|
21754
|
+
surface: getCallSurface(),
|
|
21755
|
+
...this.signal ? { signal: this.signal } : {}
|
|
21756
|
+
}
|
|
21700
21757
|
});
|
|
21701
21758
|
this.logger.info("sendElicitationResult: original tool completed", {
|
|
21702
21759
|
elicitId,
|
|
@@ -21874,7 +21931,14 @@ import {
|
|
|
21874
21931
|
MCP_APPS_MIME_TYPE as MCP_APPS_MIME_TYPE2,
|
|
21875
21932
|
resolveServingMode
|
|
21876
21933
|
} from "@frontmcp/uipack/adapters";
|
|
21877
|
-
import {
|
|
21934
|
+
import {
|
|
21935
|
+
awaitOutsideRequest as awaitOutsideRequest3,
|
|
21936
|
+
findNonFiniteNumber,
|
|
21937
|
+
getRuntimeContext as getRuntimeContext7,
|
|
21938
|
+
isDebug,
|
|
21939
|
+
isDevelopment as isDevelopment2,
|
|
21940
|
+
randomUUID as randomUUID11
|
|
21941
|
+
} from "@frontmcp/utils";
|
|
21878
21942
|
function toolNameCandidates(name44) {
|
|
21879
21943
|
if (!/[-_]/.test(name44)) return [name44];
|
|
21880
21944
|
return [name44, name44.includes("_") ? name44.replace(/_/g, "-") : name44.replace(/-/g, "_")];
|
|
@@ -22138,6 +22202,8 @@ var init_call_tool_flow = __esm({
|
|
|
22138
22202
|
request: { method: "tools/call", params: cleanedParams },
|
|
22139
22203
|
scopeId: this.scope.id
|
|
22140
22204
|
};
|
|
22205
|
+
const callSurface = callSurfaceOf(this.input.ctx);
|
|
22206
|
+
if (callSurface !== void 0) record.surface = callSurface;
|
|
22141
22207
|
if (this.state.progressToken !== void 0) {
|
|
22142
22208
|
record.progressToken = this.state.progressToken;
|
|
22143
22209
|
}
|
|
@@ -22543,8 +22609,11 @@ var init_call_tool_flow = __esm({
|
|
|
22543
22609
|
elicitId: error.elicitId,
|
|
22544
22610
|
sessionId,
|
|
22545
22611
|
owner,
|
|
22546
|
-
|
|
22547
|
-
|
|
22612
|
+
// The answer re-runs the call this flow ran: this tool, with these arguments. What asked
|
|
22613
|
+
// may be an entry the tool runs, like the agent behind `invoke_<agent>`, whose own name
|
|
22614
|
+
// no `tools/call` resolves.
|
|
22615
|
+
toolName: tool.fullName,
|
|
22616
|
+
toolInput: this.state.required.input.arguments,
|
|
22548
22617
|
elicitMessage: error.elicitMessage,
|
|
22549
22618
|
elicitSchema: error.schema,
|
|
22550
22619
|
createdAt: Date.now(),
|
|
@@ -22556,7 +22625,7 @@ var init_call_tool_flow = __esm({
|
|
|
22556
22625
|
elicitId: error.elicitId
|
|
22557
22626
|
});
|
|
22558
22627
|
const deps = { scope: this.scope, sessionId, logger: this.logger };
|
|
22559
|
-
const result = await handleWaitingFallback(deps, error);
|
|
22628
|
+
const result = await awaitOutsideRequest3(handleWaitingFallback(deps, error));
|
|
22560
22629
|
toolContext.output = result;
|
|
22561
22630
|
this.logger.verbose("execute:done (elicitation waiting fallback)");
|
|
22562
22631
|
return;
|
|
@@ -24430,6 +24499,39 @@ var init_server_validation = __esm({
|
|
|
24430
24499
|
}
|
|
24431
24500
|
});
|
|
24432
24501
|
|
|
24502
|
+
// libs/sdk/src/flows/flow.http-path.ts
|
|
24503
|
+
function matchMountedPath(pattern, path3) {
|
|
24504
|
+
if (!pattern.includes("/:")) {
|
|
24505
|
+
const prefix = pattern.endsWith("/") ? pattern : `${pattern}/`;
|
|
24506
|
+
return path3 === pattern || path3.startsWith(prefix) ? {} : void 0;
|
|
24507
|
+
}
|
|
24508
|
+
const mount = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
|
|
24509
|
+
const expected = mount.split("/");
|
|
24510
|
+
const actual = path3.split("/");
|
|
24511
|
+
if (actual.length < expected.length) return void 0;
|
|
24512
|
+
const params = {};
|
|
24513
|
+
for (let i = 0; i < expected.length; i++) {
|
|
24514
|
+
const segment = expected[i];
|
|
24515
|
+
const value = actual[i];
|
|
24516
|
+
if (segment.length > 1 && segment.startsWith(":")) {
|
|
24517
|
+
if (value.length === 0) return void 0;
|
|
24518
|
+
try {
|
|
24519
|
+
params[segment.slice(1)] = decodeURIComponent(value);
|
|
24520
|
+
} catch {
|
|
24521
|
+
return void 0;
|
|
24522
|
+
}
|
|
24523
|
+
} else if (segment !== value) {
|
|
24524
|
+
return void 0;
|
|
24525
|
+
}
|
|
24526
|
+
}
|
|
24527
|
+
return params;
|
|
24528
|
+
}
|
|
24529
|
+
var init_flow_http_path = __esm({
|
|
24530
|
+
"libs/sdk/src/flows/flow.http-path.ts"() {
|
|
24531
|
+
"use strict";
|
|
24532
|
+
}
|
|
24533
|
+
});
|
|
24534
|
+
|
|
24433
24535
|
// libs/sdk/src/flows/flow.stages.ts
|
|
24434
24536
|
function sortStageMap(table) {
|
|
24435
24537
|
for (const key of Object.keys(table)) {
|
|
@@ -24537,6 +24639,7 @@ var init_flow_instance = __esm({
|
|
|
24537
24639
|
init_context();
|
|
24538
24640
|
init_errors();
|
|
24539
24641
|
init_server_validation();
|
|
24642
|
+
init_flow_http_path();
|
|
24540
24643
|
init_flow_stages();
|
|
24541
24644
|
FAILED_OUTCOMES = /* @__PURE__ */ new Set(["fail", "abort", "unknown_error"]);
|
|
24542
24645
|
CAP = (s) => s ? s[0].toUpperCase() + s.slice(1) : s;
|
|
@@ -24656,12 +24759,16 @@ var init_flow_instance = __esm({
|
|
|
24656
24759
|
const hasMwCanActivate = (mw.canActivate?.length ?? 0) > 0;
|
|
24657
24760
|
if (!path3 && !hasStaticCanActivate && !hasMwCanActivate) return false;
|
|
24658
24761
|
if (this.method && request.method !== this.method) return false;
|
|
24659
|
-
|
|
24660
|
-
|
|
24661
|
-
|
|
24662
|
-
|
|
24762
|
+
const captured = path3 ? matchMountedPath(path3, request.path) : {};
|
|
24763
|
+
if (!captured) return false;
|
|
24764
|
+
const previous = request.params;
|
|
24765
|
+
if (Object.keys(captured).length > 0) request.params = { ...captured, ...previous };
|
|
24766
|
+
const activated = await this.canActivate(request);
|
|
24767
|
+
if (!activated) {
|
|
24768
|
+
if (previous === void 0) delete request.params;
|
|
24769
|
+
else request.params = previous;
|
|
24663
24770
|
}
|
|
24664
|
-
return
|
|
24771
|
+
return activated;
|
|
24665
24772
|
}
|
|
24666
24773
|
/**
|
|
24667
24774
|
* Get FrontMcpContextStorage from providers (with fallback).
|
|
@@ -25656,6 +25763,11 @@ var init_execution_context_interface = __esm({
|
|
|
25656
25763
|
* Authority/authorization checks still run — an internal call carries the
|
|
25657
25764
|
* same `authInfo` as the surrounding request, so ABAC/RBAC guards apply.
|
|
25658
25765
|
*
|
|
25766
|
+
* `availableWhen.surface`: the call carries {@link callToolSurface}. From a tool, resource or
|
|
25767
|
+
* prompt that is none (in-process dispatch, which no surface restricts); a job's call carries
|
|
25768
|
+
* `'job'`, an agent's `'agent'`, and an HTTP trigger's `'http-trigger'`, so a tool whose `surface`
|
|
25769
|
+
* leaves that caller out answers as an unknown tool.
|
|
25770
|
+
*
|
|
25659
25771
|
* @param name Tool name (or fully-qualified `owner.name`).
|
|
25660
25772
|
* @param args Tool arguments — validated by the tool's input schema.
|
|
25661
25773
|
* @param opts Optional progress token / abort signal forwarded into `_meta`.
|
|
@@ -25673,10 +25785,12 @@ var init_execution_context_interface = __esm({
|
|
|
25673
25785
|
...Object.keys(requestMeta).length > 0 && { _meta: requestMeta }
|
|
25674
25786
|
}
|
|
25675
25787
|
};
|
|
25788
|
+
const surface = this.callToolSurface();
|
|
25676
25789
|
const ctx = {
|
|
25677
25790
|
authInfo: this.getAuthInfo(),
|
|
25678
25791
|
requestId: this.tryGetContext()?.requestId ?? this.runId,
|
|
25679
25792
|
internalCall: true,
|
|
25793
|
+
...surface && { surface },
|
|
25680
25794
|
...opts?.signal && { signal: opts.signal }
|
|
25681
25795
|
};
|
|
25682
25796
|
const result = await scope.runFlow("tools:call-tool", { request, ctx });
|
|
@@ -25685,6 +25799,14 @@ var init_execution_context_interface = __esm({
|
|
|
25685
25799
|
}
|
|
25686
25800
|
return result;
|
|
25687
25801
|
}
|
|
25802
|
+
/**
|
|
25803
|
+
* The surface (`availableWhen.surface`) a {@link callTool} from this context calls on. None by
|
|
25804
|
+
* default: a tool, resource or prompt composing with other tools is in-process dispatch. Contexts
|
|
25805
|
+
* that are callers of their own (jobs, agents, HTTP triggers) name their surface.
|
|
25806
|
+
*/
|
|
25807
|
+
callToolSurface() {
|
|
25808
|
+
return void 0;
|
|
25809
|
+
}
|
|
25688
25810
|
/**
|
|
25689
25811
|
* Fail the execution and trigger error handling.
|
|
25690
25812
|
*/
|
|
@@ -27491,12 +27613,16 @@ var init_skill_registry = __esm({
|
|
|
27491
27613
|
* caller cannot remove a replacement registration.
|
|
27492
27614
|
*/
|
|
27493
27615
|
dynamicGenerations = /* @__PURE__ */ new Map();
|
|
27616
|
+
/** Whether a plugin registers skills at runtime ({@link expectDynamicSkills}). */
|
|
27617
|
+
dynamicSkillsExpected = false;
|
|
27494
27618
|
/** Adopted skills from child registries */
|
|
27495
27619
|
adopted = /* @__PURE__ */ new Map();
|
|
27496
27620
|
/** Children registries */
|
|
27497
27621
|
children = /* @__PURE__ */ new Set();
|
|
27498
27622
|
/** O(1) indexes */
|
|
27499
27623
|
byQualifiedId = /* @__PURE__ */ new Map();
|
|
27624
|
+
/** Owner lineage per skill instance, rebuilt with the index so `lineageOf` is O(1). */
|
|
27625
|
+
lineages = new EntryLineageIndex();
|
|
27500
27626
|
byName = /* @__PURE__ */ new Map();
|
|
27501
27627
|
byOwnerAndName = /* @__PURE__ */ new Map();
|
|
27502
27628
|
/** Version and emitter for change tracking */
|
|
@@ -27911,12 +28037,31 @@ var init_skill_registry = __esm({
|
|
|
27911
28037
|
if (!page.hasMore || page.skills.length === 0) return all;
|
|
27912
28038
|
}
|
|
27913
28039
|
}
|
|
28040
|
+
/**
|
|
28041
|
+
* Owner lineage (root → leaf) of a skill, or undefined when no row holds it. A skill a plugin
|
|
28042
|
+
* contributes is owned by the plugin; its lineage names the app the plugin is installed on.
|
|
28043
|
+
*/
|
|
28044
|
+
lineageOf(entry) {
|
|
28045
|
+
return this.lineages.lineageOf(entry);
|
|
28046
|
+
}
|
|
27914
28047
|
/**
|
|
27915
28048
|
* Check if any skills exist.
|
|
27916
28049
|
*/
|
|
27917
28050
|
hasAny() {
|
|
27918
28051
|
return this.listAllIndexed().length > 0;
|
|
27919
28052
|
}
|
|
28053
|
+
/**
|
|
28054
|
+
* Record that a plugin registers skills at runtime ({@link registerSkillContent}), such as a
|
|
28055
|
+
* skill bundle loaded after the server starts. The scope then serves the skills capability and
|
|
28056
|
+
* methods from startup, answering with the skills registered so far.
|
|
28057
|
+
*/
|
|
28058
|
+
expectDynamicSkills() {
|
|
28059
|
+
this.dynamicSkillsExpected = true;
|
|
28060
|
+
}
|
|
28061
|
+
/** Whether the scope serves skills: it has some, or a plugin registers them at runtime. */
|
|
28062
|
+
servesSkills() {
|
|
28063
|
+
return this.dynamicSkillsExpected || this.hasAny();
|
|
28064
|
+
}
|
|
27920
28065
|
/**
|
|
27921
28066
|
* Get total skill count.
|
|
27922
28067
|
*
|
|
@@ -28060,7 +28205,7 @@ var init_skill_registry = __esm({
|
|
|
28060
28205
|
* of the schema cutover see the declaration.
|
|
28061
28206
|
*/
|
|
28062
28207
|
getCapabilities() {
|
|
28063
|
-
if (!this.
|
|
28208
|
+
if (!this.servesSkills()) return {};
|
|
28064
28209
|
return {
|
|
28065
28210
|
experimental: {
|
|
28066
28211
|
[SEP_2640_EXTENSION_ID]: {}
|
|
@@ -28339,6 +28484,7 @@ var init_skill_registry = __esm({
|
|
|
28339
28484
|
}
|
|
28340
28485
|
reindex() {
|
|
28341
28486
|
const effective = this.listAllIndexed();
|
|
28487
|
+
this.lineages.rebuild(effective);
|
|
28342
28488
|
this.byQualifiedId.clear();
|
|
28343
28489
|
this.byName.clear();
|
|
28344
28490
|
this.byOwnerAndName.clear();
|
|
@@ -29263,13 +29409,15 @@ var init_agent_utils = __esm({
|
|
|
29263
29409
|
// libs/sdk/src/agent/agent.instance.ts
|
|
29264
29410
|
import { AuthorityDeniedError as AuthorityDeniedError6 } from "@frontmcp/auth";
|
|
29265
29411
|
import { toJSONSchema as toJSONSchema2, z as z59 } from "@frontmcp/lazy-zod";
|
|
29266
|
-
var VALID_AGENT_HOOK_FLOWS, AGENT_ONLY_METADATA_KEYS, AgentInstance, FunctionAgentContext;
|
|
29412
|
+
var VALID_AGENT_HOOK_FLOWS, AGENT_SURFACE, AGENT_ONLY_METADATA_KEYS, AgentInstance, FunctionAgentContext;
|
|
29267
29413
|
var init_agent_instance = __esm({
|
|
29268
29414
|
"libs/sdk/src/agent/agent.instance.ts"() {
|
|
29269
29415
|
"use strict";
|
|
29270
29416
|
init_config();
|
|
29271
29417
|
init_common();
|
|
29418
|
+
init_availability();
|
|
29272
29419
|
init_tool_decorator();
|
|
29420
|
+
init_call_surface();
|
|
29273
29421
|
init_running_tool();
|
|
29274
29422
|
init_errors();
|
|
29275
29423
|
init_hooks_utils();
|
|
@@ -29279,6 +29427,7 @@ var init_agent_instance = __esm({
|
|
|
29279
29427
|
init_agent_scope();
|
|
29280
29428
|
init_agent_utils();
|
|
29281
29429
|
VALID_AGENT_HOOK_FLOWS = ["agents:call-agent", "agents:list-agents"];
|
|
29430
|
+
AGENT_SURFACE = "agent";
|
|
29282
29431
|
AGENT_ONLY_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
29283
29432
|
"id",
|
|
29284
29433
|
"name",
|
|
@@ -29655,10 +29804,17 @@ var init_agent_instance = __esm({
|
|
|
29655
29804
|
}
|
|
29656
29805
|
/**
|
|
29657
29806
|
* Get tool definitions available to this agent.
|
|
29658
|
-
* Returns only the agent's own tools (from @Agent({ tools: [...] })).
|
|
29807
|
+
* Returns only the agent's own tools (from @Agent({ tools: [...] })) that are offered to it.
|
|
29659
29808
|
*/
|
|
29660
29809
|
getToolDefinitions() {
|
|
29661
|
-
return buildAgentToolDefinitions(this.
|
|
29810
|
+
return buildAgentToolDefinitions(this.getOfferedTools());
|
|
29811
|
+
}
|
|
29812
|
+
/**
|
|
29813
|
+
* The agent's tools its model may call: those whose `availableWhen.surface` offers them on the
|
|
29814
|
+
* `'agent'` surface. The others are neither shown to the model nor run for it.
|
|
29815
|
+
*/
|
|
29816
|
+
getOfferedTools() {
|
|
29817
|
+
return this.agentTools.filter((tool) => isOfferedOnSurface(tool.metadata.availableWhen, AGENT_SURFACE));
|
|
29662
29818
|
}
|
|
29663
29819
|
/**
|
|
29664
29820
|
* Create a tool executor for agent-scoped tools.
|
|
@@ -29681,12 +29837,13 @@ var init_agent_instance = __esm({
|
|
|
29681
29837
|
createToolExecutor(ctx) {
|
|
29682
29838
|
const useToolFlow = this.record.metadata.execution?.useToolFlow !== false;
|
|
29683
29839
|
return async (toolName, args) => {
|
|
29684
|
-
const
|
|
29840
|
+
const offeredTools = this.getOfferedTools();
|
|
29841
|
+
const tool = offeredTools.find((t) => t.name === toolName || t.fullName === toolName);
|
|
29685
29842
|
if (!tool) {
|
|
29686
29843
|
throw new AgentToolNotFoundError(
|
|
29687
29844
|
this.name,
|
|
29688
29845
|
toolName,
|
|
29689
|
-
|
|
29846
|
+
offeredTools.map((t) => t.name)
|
|
29690
29847
|
);
|
|
29691
29848
|
}
|
|
29692
29849
|
if (useToolFlow && this.agentScope) {
|
|
@@ -29697,16 +29854,19 @@ var init_agent_instance = __esm({
|
|
|
29697
29854
|
},
|
|
29698
29855
|
ctx: {
|
|
29699
29856
|
authInfo: ctx.authInfo,
|
|
29700
|
-
_skipUI: true
|
|
29857
|
+
_skipUI: true,
|
|
29701
29858
|
// Skip UI rendering - agent returns structured data
|
|
29859
|
+
surface: AGENT_SURFACE
|
|
29702
29860
|
}
|
|
29703
29861
|
});
|
|
29704
29862
|
return this.extractToolResult(result);
|
|
29705
29863
|
}
|
|
29706
29864
|
await this.assertToolAuthorized(tool, ctx.authInfo, args);
|
|
29707
29865
|
const runningTool = { name: tool.name, fullName: tool.fullName };
|
|
29708
|
-
|
|
29709
|
-
|
|
29866
|
+
return runOnSurface(AGENT_SURFACE, () => {
|
|
29867
|
+
const toolContext = runAsTool(runningTool, () => tool.create(args, ctx));
|
|
29868
|
+
return runAsTool(runningTool, () => Promise.resolve(toolContext.execute(args)));
|
|
29869
|
+
});
|
|
29710
29870
|
};
|
|
29711
29871
|
}
|
|
29712
29872
|
/**
|
|
@@ -35442,13 +35602,13 @@ var init_esm_module_loader = __esm({
|
|
|
35442
35602
|
URL.revokeObjectURL(url);
|
|
35443
35603
|
}
|
|
35444
35604
|
}
|
|
35445
|
-
const { ensureDir, fileExists: fileExists9, sha256Hex:
|
|
35605
|
+
const { ensureDir, fileExists: fileExists9, sha256Hex: sha256Hex14, writeFile } = await import("@frontmcp/utils");
|
|
35446
35606
|
const nodePath = await import("node:path");
|
|
35447
35607
|
const nodeOs = await import("node:os");
|
|
35448
35608
|
const { pathToFileURL } = await import("node:url");
|
|
35449
35609
|
const dir = nodePath.join(nodeOs.tmpdir(), "frontmcp-esm-modules");
|
|
35450
35610
|
await ensureDir(dir);
|
|
35451
|
-
const tempPath = nodePath.join(dir, `${
|
|
35611
|
+
const tempPath = nodePath.join(dir, `${sha256Hex14(bundleContent)}.mjs`);
|
|
35452
35612
|
if (!await fileExists9(tempPath)) {
|
|
35453
35613
|
await writeFile(tempPath, bundleContent);
|
|
35454
35614
|
}
|
|
@@ -37187,7 +37347,7 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37187
37347
|
});
|
|
37188
37348
|
inputSchema16 = httpInputSchema;
|
|
37189
37349
|
stateSchema15 = z65.object({
|
|
37190
|
-
isDefaultAuthProvider: z65.boolean().describe("
|
|
37350
|
+
isDefaultAuthProvider: z65.boolean().describe("No sign-in to run: no auth options, or public mode (the same configuration, written out)"),
|
|
37191
37351
|
isOrchestrated: z65.boolean().describe("If FrontMcp is orchestrated (local oauth proxy, remote oauth proxy)"),
|
|
37192
37352
|
allowAnonymous: z65.boolean().describe("Allow anonymous access, force orchestrated mode"),
|
|
37193
37353
|
// Validated OAuth request (after validation)
|
|
@@ -37253,9 +37413,13 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37253
37413
|
* Returns `undefined` when the configured auth instance has no issuer, so the
|
|
37254
37414
|
* parameter is simply omitted rather than emitted empty — a client validating
|
|
37255
37415
|
* `iss` treats absence as "not supported" and proceeds.
|
|
37416
|
+
*
|
|
37417
|
+
* With `request`, the issuer the tokens redeemed for this request name
|
|
37418
|
+
* (`LocalPrimaryAuth.issuerFor`); without it, the boot-time issuer.
|
|
37256
37419
|
*/
|
|
37257
|
-
resolveIssuer() {
|
|
37258
|
-
const
|
|
37420
|
+
resolveIssuer(request) {
|
|
37421
|
+
const auth = this.scope.auth;
|
|
37422
|
+
const issuer = request && auth?.issuerFor ? auth.issuerFor(request) : auth?.issuer;
|
|
37259
37423
|
return typeof issuer === "string" && issuer.length > 0 ? issuer : void 0;
|
|
37260
37424
|
}
|
|
37261
37425
|
logger = this.scope.logger.child("OauthAuthorizeFlow");
|
|
@@ -37295,7 +37459,7 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37295
37459
|
}
|
|
37296
37460
|
}
|
|
37297
37461
|
const existingSessionId = request.query["session_id"];
|
|
37298
|
-
const isDefaultAuthProvider = !metadata.auth;
|
|
37462
|
+
const isDefaultAuthProvider = !metadata.auth || isPublicMode(metadata.auth);
|
|
37299
37463
|
let requiresFederatedLogin = false;
|
|
37300
37464
|
if (metadata.auth && isOrchestratedMode(metadata.auth)) {
|
|
37301
37465
|
const configProviders = getConfiguredProviderIds(metadata.auth);
|
|
@@ -37368,11 +37532,26 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37368
37532
|
if (result2.data.state) {
|
|
37369
37533
|
url.searchParams.set("state", result2.data.state);
|
|
37370
37534
|
}
|
|
37371
|
-
const issuer = this.resolveIssuer();
|
|
37535
|
+
const issuer = this.resolveIssuer(this.rawInput.request);
|
|
37372
37536
|
if (issuer) url.searchParams.set("iss", issuer);
|
|
37373
37537
|
this.respond(httpRespond.redirect(url.toString()));
|
|
37374
37538
|
return;
|
|
37375
37539
|
}
|
|
37540
|
+
const authConfig = this.scope.metadata.auth;
|
|
37541
|
+
if (!authConfig || !isOrchestratedMode(authConfig)) {
|
|
37542
|
+
const mode = authConfig?.mode ?? "public";
|
|
37543
|
+
this.logger.warn(`Authorization request refused: auth mode ${mode} does not sign users in`);
|
|
37544
|
+
this.respond(
|
|
37545
|
+
this.htmlPage(
|
|
37546
|
+
this.renderErrorPage(
|
|
37547
|
+
"unsupported_response_type",
|
|
37548
|
+
`This server does not sign users in (auth mode: ${mode}). Present its access token instead.`
|
|
37549
|
+
),
|
|
37550
|
+
400
|
|
37551
|
+
)
|
|
37552
|
+
);
|
|
37553
|
+
return;
|
|
37554
|
+
}
|
|
37376
37555
|
const result = oauthAuthorizeRequestSchema.safeParse({
|
|
37377
37556
|
response_type: request.query["response_type"],
|
|
37378
37557
|
client_id: request.query["client_id"],
|
|
@@ -37408,10 +37587,10 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37408
37587
|
const redirectAllowlisted = !!registry?.hasRedirectAllowlist() && registry.isRedirectUriAllowed(redirect_uri);
|
|
37409
37588
|
let redirectVerified = redirectAllowlisted;
|
|
37410
37589
|
const isCimdClientId2 = !!cimdService?.enabled && cimdService.isCimdClientId(client_id);
|
|
37411
|
-
const
|
|
37412
|
-
if (
|
|
37413
|
-
this.logger.warn(`OAuth authorize: DCR allowlist rejection \u2014 ${
|
|
37414
|
-
this.respondWithError([
|
|
37590
|
+
const redirectError = this.checkDcrRedirectAllowlist(redirect_uri);
|
|
37591
|
+
if (redirectError) {
|
|
37592
|
+
this.logger.warn(`OAuth authorize: DCR allowlist rejection \u2014 ${redirectError}`);
|
|
37593
|
+
this.respondWithError([redirectError], void 0, rawState);
|
|
37415
37594
|
return;
|
|
37416
37595
|
}
|
|
37417
37596
|
if (isCimdClientId2) {
|
|
@@ -37435,24 +37614,34 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37435
37614
|
return;
|
|
37436
37615
|
}
|
|
37437
37616
|
}
|
|
37438
|
-
|
|
37439
|
-
|
|
37440
|
-
if (registered) {
|
|
37441
|
-
|
|
37442
|
-
|
|
37443
|
-
this.respondWithError(["redirect_uri is not registered for this client"], void 0, rawState);
|
|
37444
|
-
return;
|
|
37445
|
-
}
|
|
37446
|
-
redirectVerified = true;
|
|
37447
|
-
} else if (authOptions?.requireRegisteredClients !== false) {
|
|
37448
|
-
this.logger.warn(`OAuth authorize: rejecting unregistered client_id "${client_id}" (requireRegisteredClients)`);
|
|
37449
|
-
this.respondWithError(
|
|
37450
|
-
["Unknown client_id: register the client (DCR / pre-registered) or use a CIMD client-id URL"],
|
|
37451
|
-
void 0,
|
|
37452
|
-
rawState
|
|
37453
|
-
);
|
|
37617
|
+
const registered = isCimdClientId2 ? void 0 : registry?.get?.(client_id);
|
|
37618
|
+
if (registered) {
|
|
37619
|
+
if (!registered.redirect_uris.includes(redirect_uri)) {
|
|
37620
|
+
this.logger.warn(`OAuth authorize: redirect_uri "${redirect_uri}" not registered for client "${client_id}"`);
|
|
37621
|
+
this.respondWithError(["redirect_uri is not registered for this client"], void 0, rawState);
|
|
37454
37622
|
return;
|
|
37455
37623
|
}
|
|
37624
|
+
redirectVerified = true;
|
|
37625
|
+
}
|
|
37626
|
+
const clientIdError = isCimdClientId2 ? void 0 : this.checkDcrClientIdAllowlist(client_id);
|
|
37627
|
+
if (clientIdError) {
|
|
37628
|
+
this.logger.warn(`OAuth authorize: DCR allowlist rejection \u2014 ${clientIdError}`);
|
|
37629
|
+
this.respondWithError(
|
|
37630
|
+
[clientIdError],
|
|
37631
|
+
redirectVerified ? redirect_uri : void 0,
|
|
37632
|
+
rawState,
|
|
37633
|
+
"unauthorized_client"
|
|
37634
|
+
);
|
|
37635
|
+
return;
|
|
37636
|
+
}
|
|
37637
|
+
if (!isCimdClientId2 && !registered && authOptions?.requireRegisteredClients !== false) {
|
|
37638
|
+
this.logger.warn(`OAuth authorize: rejecting unregistered client_id "${client_id}" (requireRegisteredClients)`);
|
|
37639
|
+
this.respondWithError(
|
|
37640
|
+
["Unknown client_id: register the client (DCR / pre-registered) or use a CIMD client-id URL"],
|
|
37641
|
+
void 0,
|
|
37642
|
+
rawState
|
|
37643
|
+
);
|
|
37644
|
+
return;
|
|
37456
37645
|
}
|
|
37457
37646
|
const canonicalResource = computeResource(request, this.scope.entryPath, this.scope.routeBase);
|
|
37458
37647
|
if (!validatedRequest.resource) {
|
|
@@ -37893,26 +38082,28 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37893
38082
|
return httpRespond.html(markup, status, builtInAuthPageHeaders());
|
|
37894
38083
|
}
|
|
37895
38084
|
/**
|
|
37896
|
-
* Enforce the local-AS DCR
|
|
37897
|
-
*
|
|
37898
|
-
*
|
|
37899
|
-
*
|
|
37900
|
-
*
|
|
37901
|
-
* - `allowedRedirectUris`: applies to ALL clients (CIMD or not) as an
|
|
37902
|
-
* open-redirect / lateral-movement guard.
|
|
37903
|
-
* - `allowedClientIds`: applies only to non-CIMD client ids; CIMD URLs are
|
|
37904
|
-
* validated by the CIMD layer instead.
|
|
38085
|
+
* Enforce the local-AS DCR `allowedRedirectUris` (#462) at authorize time,
|
|
38086
|
+
* for ALL clients (CIMD or not) as an open-redirect / lateral-movement guard.
|
|
38087
|
+
* Returns a human-readable rejection reason, or `undefined` when the
|
|
38088
|
+
* redirect_uri is allowed (including when no allowlist is configured, or the
|
|
38089
|
+
* auth instance does not expose a DCR registry — e.g. non-local modes).
|
|
37905
38090
|
*/
|
|
37906
|
-
|
|
37907
|
-
const
|
|
37908
|
-
|
|
37909
|
-
if (!registry) {
|
|
37910
|
-
return void 0;
|
|
37911
|
-
}
|
|
37912
|
-
if (registry.hasRedirectAllowlist() && !registry.isRedirectUriAllowed(redirectUri)) {
|
|
38091
|
+
checkDcrRedirectAllowlist(redirectUri) {
|
|
38092
|
+
const registry = this.scope.auth?.dcrClientRegistry;
|
|
38093
|
+
if (registry?.hasRedirectAllowlist() && !registry.isRedirectUriAllowed(redirectUri)) {
|
|
37913
38094
|
return `redirect_uri "${redirectUri}" is not in the configured allowlist`;
|
|
37914
38095
|
}
|
|
37915
|
-
|
|
38096
|
+
return void 0;
|
|
38097
|
+
}
|
|
38098
|
+
/**
|
|
38099
|
+
* Enforce the local-AS DCR `allowedClientIds` (#462) at authorize time, for
|
|
38100
|
+
* non-CIMD client ids (the caller skips CIMD URLs, which the CIMD layer
|
|
38101
|
+
* validates). Returns a human-readable rejection reason, or `undefined` when
|
|
38102
|
+
* the client is allowed or no allowlist is configured.
|
|
38103
|
+
*/
|
|
38104
|
+
checkDcrClientIdAllowlist(clientId) {
|
|
38105
|
+
const registry = this.scope.auth?.dcrClientRegistry;
|
|
38106
|
+
if (registry?.hasClientIdAllowlist() && !registry.isClientIdAllowed(clientId)) {
|
|
37916
38107
|
return `client_id "${clientId}" is not in the configured allowlist`;
|
|
37917
38108
|
}
|
|
37918
38109
|
return void 0;
|
|
@@ -37927,15 +38118,18 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37927
38118
|
});
|
|
37928
38119
|
}
|
|
37929
38120
|
/**
|
|
37930
|
-
* Respond with OAuth error - redirect if possible, otherwise show error page
|
|
38121
|
+
* Respond with OAuth error - redirect if possible, otherwise show error page.
|
|
38122
|
+
*
|
|
38123
|
+
* @param redirectUri A redirect_uri validated for the client; omit it for an error page.
|
|
38124
|
+
* @param error The RFC 6749 §4.1.2.1 error code. Default `invalid_request`.
|
|
37931
38125
|
*/
|
|
37932
|
-
respondWithError(errors, redirectUri, state) {
|
|
38126
|
+
respondWithError(errors, redirectUri, state, error = "invalid_request") {
|
|
37933
38127
|
const errorDescription = errors.join("; ");
|
|
37934
38128
|
if (redirectUri) {
|
|
37935
38129
|
const safe = safeRedirectUriSchema.safeParse(redirectUri);
|
|
37936
38130
|
if (safe.success) {
|
|
37937
38131
|
const url = new URL(safe.data);
|
|
37938
|
-
url.searchParams.set("error",
|
|
38132
|
+
url.searchParams.set("error", error);
|
|
37939
38133
|
url.searchParams.set("error_description", errorDescription);
|
|
37940
38134
|
if (state) {
|
|
37941
38135
|
url.searchParams.set("state", state);
|
|
@@ -37944,7 +38138,7 @@ var init_oauth_authorize_flow = __esm({
|
|
|
37944
38138
|
return;
|
|
37945
38139
|
}
|
|
37946
38140
|
}
|
|
37947
|
-
this.respond(this.htmlPage(this.renderErrorPage(
|
|
38141
|
+
this.respond(this.htmlPage(this.renderErrorPage(error, errorDescription), 400));
|
|
37948
38142
|
}
|
|
37949
38143
|
/**
|
|
37950
38144
|
* Render the local login page.
|
|
@@ -38223,9 +38417,13 @@ var init_oauth_callback_flow = __esm({
|
|
|
38223
38417
|
* Returns `undefined` when the configured auth instance has no issuer, so the
|
|
38224
38418
|
* parameter is simply omitted rather than emitted empty — a client validating
|
|
38225
38419
|
* `iss` treats absence as "not supported" and proceeds.
|
|
38420
|
+
*
|
|
38421
|
+
* With `request`, the issuer the tokens redeemed for this request name
|
|
38422
|
+
* (`LocalPrimaryAuth.issuerFor`); without it, the boot-time issuer.
|
|
38226
38423
|
*/
|
|
38227
|
-
resolveIssuer() {
|
|
38228
|
-
const
|
|
38424
|
+
resolveIssuer(request) {
|
|
38425
|
+
const auth = this.scope.auth;
|
|
38426
|
+
const issuer = request && auth?.issuerFor ? auth.issuerFor(request) : auth?.issuer;
|
|
38229
38427
|
return typeof issuer === "string" && issuer.length > 0 ? issuer : void 0;
|
|
38230
38428
|
}
|
|
38231
38429
|
async checkIpFilter() {
|
|
@@ -38763,7 +38961,7 @@ var init_oauth_callback_flow = __esm({
|
|
|
38763
38961
|
if (originalState) {
|
|
38764
38962
|
url.searchParams.set("state", originalState);
|
|
38765
38963
|
}
|
|
38766
|
-
const issuer = this.resolveIssuer();
|
|
38964
|
+
const issuer = this.resolveIssuer(this.rawInput.request);
|
|
38767
38965
|
if (issuer) url.searchParams.set("iss", issuer);
|
|
38768
38966
|
if (isIncremental && targetAppId) {
|
|
38769
38967
|
url.searchParams.set("incremental", "true");
|
|
@@ -40665,7 +40863,7 @@ var init_oauth_token_flow = __esm({
|
|
|
40665
40863
|
stateSchema20 = z70.object({
|
|
40666
40864
|
body: tokenRequestSchema.optional(),
|
|
40667
40865
|
grantType: z70.enum(["authorization_code", "refresh_token", "anonymous"]).optional(),
|
|
40668
|
-
isDefaultAuthProvider: z70.boolean().describe("
|
|
40866
|
+
isDefaultAuthProvider: z70.boolean().describe("No sign-in to run: no auth options, or public mode (the same configuration, written out)"),
|
|
40669
40867
|
isOrchestrated: z70.boolean().describe("If auth mode is orchestrated"),
|
|
40670
40868
|
// Token response data
|
|
40671
40869
|
tokenResponse: z70.object({
|
|
@@ -40695,7 +40893,7 @@ var init_oauth_token_flow = __esm({
|
|
|
40695
40893
|
async parseInput() {
|
|
40696
40894
|
const { metadata } = this.scope;
|
|
40697
40895
|
const { request } = this.rawInput;
|
|
40698
|
-
const isDefaultAuthProvider = !metadata.auth;
|
|
40896
|
+
const isDefaultAuthProvider = !metadata.auth || isPublicMode(metadata.auth);
|
|
40699
40897
|
const isOrchestrated = !isDefaultAuthProvider;
|
|
40700
40898
|
const rawBody = coerceTokenRequestBody(request);
|
|
40701
40899
|
const basic = parseBasicAuthorization(request.headers?.["authorization"]);
|
|
@@ -40744,7 +40942,10 @@ var init_oauth_token_flow = __esm({
|
|
|
40744
40942
|
const authOptions = this.scope.auth?.options;
|
|
40745
40943
|
if (isDefaultAuthProvider && body.code === "anonymous" && !!authOptions && allowsPublicAccess(authOptions)) {
|
|
40746
40944
|
const localAuth2 = this.scope.auth;
|
|
40747
|
-
const accessToken = await localAuth2.signAnonymousJwt({
|
|
40945
|
+
const accessToken = await localAuth2.signAnonymousJwt({
|
|
40946
|
+
audience: this.canonicalResource(),
|
|
40947
|
+
issuer: localAuth2.issuerFor(this.rawInput.request)
|
|
40948
|
+
});
|
|
40748
40949
|
this.state.set("tokenResponse", {
|
|
40749
40950
|
access_token: accessToken,
|
|
40750
40951
|
token_type: "Bearer",
|
|
@@ -40760,7 +40961,8 @@ var init_oauth_token_flow = __esm({
|
|
|
40760
40961
|
body.redirect_uri,
|
|
40761
40962
|
body.code_verifier,
|
|
40762
40963
|
body.client_secret,
|
|
40763
|
-
this.canonicalResource()
|
|
40964
|
+
this.canonicalResource(),
|
|
40965
|
+
localAuth.issuerFor(this.rawInput.request)
|
|
40764
40966
|
);
|
|
40765
40967
|
if ("error" in result) {
|
|
40766
40968
|
this.logger.warn(`Code exchange failed: ${result.error}`);
|
|
@@ -40798,7 +41000,10 @@ var init_oauth_token_flow = __esm({
|
|
|
40798
41000
|
return;
|
|
40799
41001
|
}
|
|
40800
41002
|
const localAuth2 = this.scope.auth;
|
|
40801
|
-
const accessToken = await localAuth2.signAnonymousJwt({
|
|
41003
|
+
const accessToken = await localAuth2.signAnonymousJwt({
|
|
41004
|
+
audience: this.canonicalResource(),
|
|
41005
|
+
issuer: localAuth2.issuerFor(this.rawInput.request)
|
|
41006
|
+
});
|
|
40802
41007
|
this.state.set("tokenResponse", {
|
|
40803
41008
|
access_token: accessToken,
|
|
40804
41009
|
token_type: "Bearer",
|
|
@@ -40812,7 +41017,8 @@ var init_oauth_token_flow = __esm({
|
|
|
40812
41017
|
body.refresh_token,
|
|
40813
41018
|
body.client_id,
|
|
40814
41019
|
body.client_secret,
|
|
40815
|
-
this.canonicalResource()
|
|
41020
|
+
this.canonicalResource(),
|
|
41021
|
+
localAuth.issuerFor(this.rawInput.request)
|
|
40816
41022
|
);
|
|
40817
41023
|
if ("error" in result) {
|
|
40818
41024
|
this.logger.warn(`Refresh token failed: ${result.error}`);
|
|
@@ -40868,7 +41074,10 @@ var init_oauth_token_flow = __esm({
|
|
|
40868
41074
|
}
|
|
40869
41075
|
const localAuth = this.scope.auth;
|
|
40870
41076
|
const audience = body?.grant_type === "anonymous" && body.resource ? body.resource : canonicalResource;
|
|
40871
|
-
const accessToken = await localAuth.signAnonymousJwt({
|
|
41077
|
+
const accessToken = await localAuth.signAnonymousJwt({
|
|
41078
|
+
audience,
|
|
41079
|
+
issuer: localAuth.issuerFor(this.rawInput.request)
|
|
41080
|
+
});
|
|
40872
41081
|
this.state.set("tokenResponse", {
|
|
40873
41082
|
access_token: accessToken,
|
|
40874
41083
|
token_type: "Bearer",
|
|
@@ -40996,7 +41205,8 @@ var init_oauth_userinfo_flow = __esm({
|
|
|
40996
41205
|
return;
|
|
40997
41206
|
}
|
|
40998
41207
|
const localAuth = this.scope.auth;
|
|
40999
|
-
const
|
|
41208
|
+
const issuer = localAuth.issuerFor?.(this.rawInput.request);
|
|
41209
|
+
const result = await localAuth.verifyGatewayToken(token, baseUrl, void 0, issuer);
|
|
41000
41210
|
if (!result.ok) {
|
|
41001
41211
|
this.logger.warn("userinfo: token verification failed", { error: result.error });
|
|
41002
41212
|
this.respond(
|
|
@@ -41374,7 +41584,8 @@ var init_session_verify_flow = __esm({
|
|
|
41374
41584
|
} else {
|
|
41375
41585
|
const configuredAudience = isOrchestratedMode(authOptions) ? authOptions.expectedAudience : void 0;
|
|
41376
41586
|
const resource = computeResource(this.rawInput.request, this.scope.entryPath, this.scope.routeBase);
|
|
41377
|
-
|
|
41587
|
+
const issuer = auth.issuerFor?.(this.rawInput.request);
|
|
41588
|
+
verify = auth.verifyGatewayToken(token, this.state.required.baseUrl, configuredAudience ?? resource, issuer);
|
|
41378
41589
|
}
|
|
41379
41590
|
const result = await verify;
|
|
41380
41591
|
if (result.ok) {
|
|
@@ -41621,6 +41832,7 @@ var init_well_known_jwks_flow = __esm({
|
|
|
41621
41832
|
|
|
41622
41833
|
// libs/sdk/src/auth/flows/well-known.oauth-authorization-server.flow.ts
|
|
41623
41834
|
import "reflect-metadata";
|
|
41835
|
+
import { advertisedScopes } from "@frontmcp/auth";
|
|
41624
41836
|
import { z as z74 } from "@frontmcp/lazy-zod";
|
|
41625
41837
|
import { isProduction as isProduction4 } from "@frontmcp/utils";
|
|
41626
41838
|
function resolveDcrEnabled(auth) {
|
|
@@ -41705,7 +41917,9 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41705
41917
|
wellKnownAsStateSchema.parse({
|
|
41706
41918
|
baseUrl,
|
|
41707
41919
|
oauthBaseUrl,
|
|
41708
|
-
|
|
41920
|
+
// The scopes this server grants (`allowedScopes`, #262): the literal
|
|
41921
|
+
// entries; a `*` glob names no scope a client could ask for.
|
|
41922
|
+
scopesSupported: metadata.auth && isOrchestratedMode(metadata.auth) ? advertisedScopes(metadata.auth.allowedScopes) : [],
|
|
41709
41923
|
tokenEndpointAuthMethods: [],
|
|
41710
41924
|
// #462 — advertise registration_endpoint only when local-AS DCR is
|
|
41711
41925
|
// actually active (explicit auth.dcr.enabled, else dev/prod default).
|
|
@@ -41789,6 +42003,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41789
42003
|
|
|
41790
42004
|
// libs/sdk/src/auth/flows/well-known.prm.flow.ts
|
|
41791
42005
|
import "reflect-metadata";
|
|
42006
|
+
import { advertisedScopes as advertisedScopes2 } from "@frontmcp/auth";
|
|
41792
42007
|
import { z as z75 } from "@frontmcp/lazy-zod";
|
|
41793
42008
|
var inputSchema26, stateSchema24, outputSchema24, plan24, name25, Stage25, WellKnownPrmFlow;
|
|
41794
42009
|
var init_well_known_prm_flow = __esm({
|
|
@@ -41831,11 +42046,12 @@ var init_well_known_prm_flow = __esm({
|
|
|
41831
42046
|
if (!request) throw new FlowInputMissingError("request", "well-known:prm");
|
|
41832
42047
|
const resource = computeResource(request, scope.entryPath, scope.routeBase);
|
|
41833
42048
|
const baseUrl = getRequestBaseUrl(request, scope.entryPath);
|
|
42049
|
+
const auth = scope.metadata.auth;
|
|
41834
42050
|
this.state.set(
|
|
41835
42051
|
stateSchema24.parse({
|
|
41836
42052
|
resource,
|
|
41837
42053
|
baseUrl,
|
|
41838
|
-
scopesSupported: scope.getAllSupportedScopes(),
|
|
42054
|
+
scopesSupported: auth && isOrchestratedMode(auth) ? advertisedScopes2(auth.allowedScopes) : scope.getAllSupportedScopes(),
|
|
41839
42055
|
isOrchestrated: false
|
|
41840
42056
|
//scope.orchestrated,// TODO: fix
|
|
41841
42057
|
})
|
|
@@ -41899,6 +42115,24 @@ var init_well_known_prm_flow = __esm({
|
|
|
41899
42115
|
}
|
|
41900
42116
|
});
|
|
41901
42117
|
|
|
42118
|
+
// libs/sdk/src/auth/session-scope-identity.ts
|
|
42119
|
+
import { isAnonymousSubject as isAnonymousSubject3 } from "@frontmcp/auth";
|
|
42120
|
+
function sessionScopeIdentity(ctx) {
|
|
42121
|
+
const verified = ctx.verifiedSessionId;
|
|
42122
|
+
if (verified) return verified;
|
|
42123
|
+
const authInfo = ctx.authInfo ?? {};
|
|
42124
|
+
const extraUser = authInfo.extra?.["user"];
|
|
42125
|
+
const principal = [authInfo.user?.sub, extraUser?.sub, authInfo.clientId].find(
|
|
42126
|
+
(candidate) => !isAnonymousSubject3(candidate)
|
|
42127
|
+
);
|
|
42128
|
+
return principal ? `principal:${principal}` : void 0;
|
|
42129
|
+
}
|
|
42130
|
+
var init_session_scope_identity = __esm({
|
|
42131
|
+
"libs/sdk/src/auth/session-scope-identity.ts"() {
|
|
42132
|
+
"use strict";
|
|
42133
|
+
}
|
|
42134
|
+
});
|
|
42135
|
+
|
|
41902
42136
|
// libs/sdk/src/auth/secure-store/secure-store.providers.ts
|
|
41903
42137
|
import {
|
|
41904
42138
|
SECURE_STORE_ACCESSOR,
|
|
@@ -41906,8 +42140,9 @@ import {
|
|
|
41906
42140
|
} from "@frontmcp/auth";
|
|
41907
42141
|
import { ProviderScope as ProviderScope6 } from "@frontmcp/di";
|
|
41908
42142
|
function resolveRequestSessionId(ctx) {
|
|
41909
|
-
const
|
|
41910
|
-
|
|
42143
|
+
const identity = sessionScopeIdentity(ctx);
|
|
42144
|
+
if (identity) return identity;
|
|
42145
|
+
throw new SessionIdentityRequiredError("The secure store's session scope");
|
|
41911
42146
|
}
|
|
41912
42147
|
function createSecureStoreProviders(opts) {
|
|
41913
42148
|
const { backend, scope, ttlMs } = opts;
|
|
@@ -41939,7 +42174,9 @@ var init_secure_store_providers = __esm({
|
|
|
41939
42174
|
"use strict";
|
|
41940
42175
|
init_common();
|
|
41941
42176
|
init_frontmcp_context_provider();
|
|
42177
|
+
init_session_identity_required_error();
|
|
41942
42178
|
init_credentials_providers();
|
|
42179
|
+
init_session_scope_identity();
|
|
41943
42180
|
SECURE_STORE_BACKEND = /* @__PURE__ */ Symbol.for("frontmcp:SECURE_STORE_BACKEND");
|
|
41944
42181
|
}
|
|
41945
42182
|
});
|
|
@@ -42079,6 +42316,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42079
42316
|
this.port = this.providers.getActiveScope().metadata.http?.port ?? 3001;
|
|
42080
42317
|
this.host = getEnv4("FRONTMCP_PUBLIC_HOST")?.trim() || "localhost";
|
|
42081
42318
|
this.issuer = this.deriveIssuer(options);
|
|
42319
|
+
this.issuerIsConfigured = this.configuredIssuer(options) !== void 0 || !!getEnv4("FRONTMCP_PUBLIC_HOST")?.trim();
|
|
42082
42320
|
const jwtSecret = getEnv4("JWT_SECRET");
|
|
42083
42321
|
const hasJwtSecret = !!jwtSecret?.trim();
|
|
42084
42322
|
if (hasJwtSecret && jwtSecret) {
|
|
@@ -42117,7 +42355,10 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42117
42355
|
providers;
|
|
42118
42356
|
host;
|
|
42119
42357
|
port;
|
|
42358
|
+
/** The boot-time issuer: the configured one, else this server's listener address (see {@link issuerFor}). */
|
|
42120
42359
|
issuer;
|
|
42360
|
+
/** Whether the operator named the issuer (`issuer` / `local.issuer`, or its host with `FRONTMCP_PUBLIC_HOST`). */
|
|
42361
|
+
issuerIsConfigured;
|
|
42121
42362
|
keys = [];
|
|
42122
42363
|
secret;
|
|
42123
42364
|
logger;
|
|
@@ -42266,18 +42507,36 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42266
42507
|
* supported knob for aligning the issuer with discovery.
|
|
42267
42508
|
*/
|
|
42268
42509
|
deriveIssuer(options) {
|
|
42269
|
-
|
|
42270
|
-
|
|
42271
|
-
|
|
42272
|
-
|
|
42273
|
-
if (
|
|
42274
|
-
|
|
42275
|
-
|
|
42276
|
-
|
|
42277
|
-
|
|
42278
|
-
|
|
42279
|
-
|
|
42280
|
-
|
|
42510
|
+
return this.configuredIssuer(options) ?? `http://${this.host}:${this.port}${this.scope.fullPath}`;
|
|
42511
|
+
}
|
|
42512
|
+
/** The issuer the options name: `issuer` in public mode, `local.issuer` in local and remote mode. */
|
|
42513
|
+
configuredIssuer(options) {
|
|
42514
|
+
if (isPublicMode(options)) return options.issuer;
|
|
42515
|
+
if (isOrchestratedMode(options)) return options.local?.issuer;
|
|
42516
|
+
return void 0;
|
|
42517
|
+
}
|
|
42518
|
+
/**
|
|
42519
|
+
* The issuer (`iss`) this server names on the tokens it issues in answer to
|
|
42520
|
+
* `request`, and requires on the tokens it accepts there (#269). Issuing and
|
|
42521
|
+
* verifying both ask this, so they can't disagree:
|
|
42522
|
+
*
|
|
42523
|
+
* - a configured issuer (`issuer` / `local.issuer`) is the issuer;
|
|
42524
|
+
* - with `FRONTMCP_PUBLIC_URL` pinned, the issuer is that URL plus this
|
|
42525
|
+
* scope's path, the address discovery advertises;
|
|
42526
|
+
* - with `FRONTMCP_PUBLIC_HOST` or `expectedAudience` set, the boot-time
|
|
42527
|
+
* issuer, so a token serves at every address the server answers to;
|
|
42528
|
+
* - a request that came through a Web fetch handler (a Worker, Deno, Bun, a
|
|
42529
|
+
* Durable Object) has no listener the boot-time issuer could describe, so
|
|
42530
|
+
* the issuer is the request's own origin plus this scope's path;
|
|
42531
|
+
* - otherwise (the Node server) the boot-time issuer.
|
|
42532
|
+
*/
|
|
42533
|
+
issuerFor(request) {
|
|
42534
|
+
if (!request || this.issuerIsConfigured) return this.issuer;
|
|
42535
|
+
if (isPublicUrlPinned()) return computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
|
|
42536
|
+
const options = this.options;
|
|
42537
|
+
if (isOrchestratedMode(options) && options.expectedAudience !== void 0) return this.issuer;
|
|
42538
|
+
const webRequest = request[ServerRequestTokens.webRequest];
|
|
42539
|
+
return webRequest === void 0 ? this.issuer : computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
|
|
42281
42540
|
}
|
|
42282
42541
|
/**
|
|
42283
42542
|
* Read the `tokenStorage` selection off the auth options. Only
|
|
@@ -42508,9 +42767,10 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42508
42767
|
* user, and its scopes are the configured `anonymousScopes`, never a role.
|
|
42509
42768
|
*
|
|
42510
42769
|
* @param options.audience The protected resource the token is for (`aud`, #269).
|
|
42770
|
+
* @param options.issuer The issuer to name (`iss`): {@link issuerFor} the request. Default: the boot-time issuer.
|
|
42511
42771
|
*/
|
|
42512
42772
|
async signAnonymousJwt(options = {}) {
|
|
42513
|
-
const jwt = new SignJWT({ sub: `anon:${randomUUID21()}`, anonymous: true, scope: this.anonymousScopes().join(" ") }).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setIssuer(this.issuer).setExpirationTime("1d").setJti(randomUUID21());
|
|
42773
|
+
const jwt = new SignJWT({ sub: `anon:${randomUUID21()}`, anonymous: true, scope: this.anonymousScopes().join(" ") }).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setIssuer(options.issuer ?? this.issuer).setExpirationTime("1d").setJti(randomUUID21());
|
|
42514
42774
|
if (options.audience) jwt.setAudience(options.audience);
|
|
42515
42775
|
return jwt.sign(this.secret);
|
|
42516
42776
|
}
|
|
@@ -42527,8 +42787,10 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42527
42787
|
*
|
|
42528
42788
|
* The secret alone doesn't make a token this server's: every server started
|
|
42529
42789
|
* with the same JWT_SECRET holds it. So a token must also (#269):
|
|
42530
|
-
* - name THIS instance as its issuer (`iss`,
|
|
42531
|
-
*
|
|
42790
|
+
* - name THIS instance as its issuer (`iss`): `expectedIssuer`, which callers
|
|
42791
|
+
* take from {@link issuerFor} the request, as the token endpoint does when
|
|
42792
|
+
* it signs; the boot-time issuer when not given. Never the token's own
|
|
42793
|
+
* claim; and
|
|
42532
42794
|
* - when `expectedAudience` is given, be issued for that protected resource,
|
|
42533
42795
|
* or one of those resources (`aud`, compared as RFC 8707 resource URIs).
|
|
42534
42796
|
* `session:verify` passes the request's resource URL, or the configured
|
|
@@ -42537,11 +42799,11 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42537
42799
|
* Lifetime: `exp` is required (#272) and checked with `nbf` by `jose`. The
|
|
42538
42800
|
* algorithm is pinned to HS256 to block `alg` confusion.
|
|
42539
42801
|
*/
|
|
42540
|
-
async verifyGatewayToken(token, requestBaseUrl, expectedAudience) {
|
|
42802
|
+
async verifyGatewayToken(token, requestBaseUrl, expectedAudience, expectedIssuer) {
|
|
42541
42803
|
try {
|
|
42542
42804
|
const { payload, protectedHeader } = await jwtVerify(token, this.secret, {
|
|
42543
42805
|
algorithms: ["HS256"],
|
|
42544
|
-
issuer: this.issuer,
|
|
42806
|
+
issuer: expectedIssuer ?? this.issuer,
|
|
42545
42807
|
requiredClaims: ["exp"]
|
|
42546
42808
|
});
|
|
42547
42809
|
if (expectedAudience !== void 0 && !audienceMatchesResource(payload.aud, expectedAudience)) {
|
|
@@ -42562,7 +42824,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42562
42824
|
/**
|
|
42563
42825
|
* Sign an access token for an authenticated user
|
|
42564
42826
|
*/
|
|
42565
|
-
async signAccessToken(user, scopes, audience, consentMetadata) {
|
|
42827
|
+
async signAccessToken(user, scopes, audience, consentMetadata, issuer) {
|
|
42566
42828
|
const claims = {
|
|
42567
42829
|
sub: user.sub,
|
|
42568
42830
|
scope: scopes.join(" ")
|
|
@@ -42598,7 +42860,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42598
42860
|
}
|
|
42599
42861
|
}
|
|
42600
42862
|
}
|
|
42601
|
-
const jwt = new SignJWT(claims).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setIssuer(this.issuer).setExpirationTime(`${this.accessTokenTtlSeconds}s`).setJti(randomUUID21());
|
|
42863
|
+
const jwt = new SignJWT(claims).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setIssuer(issuer ?? this.issuer).setExpirationTime(`${this.accessTokenTtlSeconds}s`).setJti(randomUUID21());
|
|
42602
42864
|
if (audience) {
|
|
42603
42865
|
jwt.setAudience(audience);
|
|
42604
42866
|
}
|
|
@@ -42607,7 +42869,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42607
42869
|
/**
|
|
42608
42870
|
* Exchange an authorization code for tokens
|
|
42609
42871
|
*/
|
|
42610
|
-
async exchangeCode(code, clientId, redirectUri, codeVerifier, clientSecret, defaultAudience) {
|
|
42872
|
+
async exchangeCode(code, clientId, redirectUri, codeVerifier, clientSecret, defaultAudience, issuer) {
|
|
42611
42873
|
if (this.dcrClientRegistryImpl.verifyClientSecret(clientId, clientSecret) === "invalid") {
|
|
42612
42874
|
this.logger.warn("Client authentication failed at token endpoint (authorization_code)");
|
|
42613
42875
|
return { error: "invalid_client", error_description: "Client authentication failed" };
|
|
@@ -42674,7 +42936,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42674
42936
|
customClaims: codeRecord.customClaims
|
|
42675
42937
|
} : void 0;
|
|
42676
42938
|
const resource = codeRecord.resource ?? defaultAudience;
|
|
42677
|
-
const accessToken = await this.signAccessToken(user, codeRecord.scopes, resource, consentMetadata);
|
|
42939
|
+
const accessToken = await this.signAccessToken(user, codeRecord.scopes, resource, consentMetadata, issuer);
|
|
42678
42940
|
if (codeRecord.pendingAuthId && codeRecord.federatedLoginUsed) {
|
|
42679
42941
|
try {
|
|
42680
42942
|
const pendingAuthId = `pending:${codeRecord.pendingAuthId}`;
|
|
@@ -42716,7 +42978,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42716
42978
|
/**
|
|
42717
42979
|
* Refresh an access token using a refresh token
|
|
42718
42980
|
*/
|
|
42719
|
-
async refreshAccessToken(refreshToken, clientId, clientSecret, defaultAudience) {
|
|
42981
|
+
async refreshAccessToken(refreshToken, clientId, clientSecret, defaultAudience, issuer) {
|
|
42720
42982
|
if (this.dcrClientRegistryImpl.verifyClientSecret(clientId, clientSecret) === "invalid") {
|
|
42721
42983
|
this.logger.warn("Client authentication failed at token endpoint (refresh_token)");
|
|
42722
42984
|
return { error: "invalid_client", error_description: "Client authentication failed" };
|
|
@@ -42753,7 +43015,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42753
43015
|
customClaims: tokenRecord.customClaims
|
|
42754
43016
|
} : void 0;
|
|
42755
43017
|
const resource = tokenRecord.resource ?? defaultAudience;
|
|
42756
|
-
const accessToken = await this.signAccessToken(user, tokenRecord.scopes, resource, consentMetadata);
|
|
43018
|
+
const accessToken = await this.signAccessToken(user, tokenRecord.scopes, resource, consentMetadata, issuer);
|
|
42757
43019
|
const newRefreshRecord = this.authorizationStore.createRefreshTokenRecord({
|
|
42758
43020
|
clientId,
|
|
42759
43021
|
userSub: tokenRecord.userSub,
|
|
@@ -45876,12 +46138,12 @@ async function createElicitationStore(options = {}) {
|
|
|
45876
46138
|
sqlite,
|
|
45877
46139
|
keyPrefix = "mcp:elicit:",
|
|
45878
46140
|
logger,
|
|
45879
|
-
isEdgeRuntime:
|
|
46141
|
+
isEdgeRuntime: isEdgeRuntime3 = false,
|
|
45880
46142
|
requiresPubSub = true,
|
|
45881
46143
|
encryption
|
|
45882
46144
|
} = options;
|
|
45883
46145
|
if (sqlite) {
|
|
45884
|
-
if (
|
|
46146
|
+
if (isEdgeRuntime3) {
|
|
45885
46147
|
throw new ElicitationNotSupportedError(
|
|
45886
46148
|
"SQLite elicitation store is not supported on Edge runtime (better-sqlite3 is a native module). Use Redis or Upstash instead."
|
|
45887
46149
|
);
|
|
@@ -45955,7 +46217,7 @@ async function createElicitationStore(options = {}) {
|
|
|
45955
46217
|
"Vercel KV is not supported for elicitation stores. Elicitation requires pub/sub for cross-node result routing, which Vercel KV does not support. Use Redis or Upstash instead."
|
|
45956
46218
|
);
|
|
45957
46219
|
}
|
|
45958
|
-
if (
|
|
46220
|
+
if (isEdgeRuntime3 && (!finalStorageConfig || finalStorageConfig.type === "memory")) {
|
|
45959
46221
|
throw new ElicitationNotSupportedError(
|
|
45960
46222
|
"Elicitation requires distributed storage when running on Edge runtime. Edge functions are stateless and cannot use in-memory elicitation. Configure Redis or Upstash storage."
|
|
45961
46223
|
);
|
|
@@ -45963,7 +46225,7 @@ async function createElicitationStore(options = {}) {
|
|
|
45963
46225
|
const storage = await createStorage2({
|
|
45964
46226
|
...finalStorageConfig,
|
|
45965
46227
|
prefix: keyPrefix,
|
|
45966
|
-
fallback:
|
|
46228
|
+
fallback: isEdgeRuntime3 ? "error" : "memory"
|
|
45967
46229
|
});
|
|
45968
46230
|
if (requiresPubSub && !storage.supportsPubSub()) {
|
|
45969
46231
|
throw new ElicitationNotSupportedError(
|
|
@@ -47629,6 +47891,7 @@ var WorkflowStepExecutor;
|
|
|
47629
47891
|
var init_workflow_step_executor = __esm({
|
|
47630
47892
|
"libs/sdk/src/workflow/engine/workflow-step.executor.ts"() {
|
|
47631
47893
|
"use strict";
|
|
47894
|
+
init_call_surface();
|
|
47632
47895
|
init_errors();
|
|
47633
47896
|
init_job_errors();
|
|
47634
47897
|
init_workflow_errors();
|
|
@@ -47691,7 +47954,7 @@ var init_workflow_step_executor = __esm({
|
|
|
47691
47954
|
const timer = setTimeout(() => {
|
|
47692
47955
|
reject(new WorkflowJobTimeoutError(job.name, timeout));
|
|
47693
47956
|
}, timeout);
|
|
47694
|
-
|
|
47957
|
+
runOnSurface("job", async () => ctx.execute(parsedInput)).then((result) => {
|
|
47695
47958
|
clearTimeout(timer);
|
|
47696
47959
|
resolve(result);
|
|
47697
47960
|
}).catch((err) => {
|
|
@@ -47881,12 +48144,13 @@ var init_workflow_engine = __esm({
|
|
|
47881
48144
|
});
|
|
47882
48145
|
|
|
47883
48146
|
// libs/sdk/src/job/execution/job-execution.manager.ts
|
|
47884
|
-
import { randomUUID as randomUUID23 } from "@frontmcp/utils";
|
|
48147
|
+
import { randomUUID as randomUUID23, runRequestExclusive as runRequestExclusive4 } from "@frontmcp/utils";
|
|
47885
48148
|
var JobExecutionManager;
|
|
47886
48149
|
var init_job_execution_manager = __esm({
|
|
47887
48150
|
"libs/sdk/src/job/execution/job-execution.manager.ts"() {
|
|
47888
48151
|
"use strict";
|
|
47889
48152
|
init_principal_utils();
|
|
48153
|
+
init_call_surface();
|
|
47890
48154
|
init_errors();
|
|
47891
48155
|
init_workflow_engine();
|
|
47892
48156
|
init_job_permission_guard();
|
|
@@ -47923,7 +48187,7 @@ var init_job_execution_manager = __esm({
|
|
|
47923
48187
|
};
|
|
47924
48188
|
await this.stateStore.createRun(runRecord);
|
|
47925
48189
|
if (opts.background) {
|
|
47926
|
-
this.executeJobBackground(job, input, runId, opts).catch(async (err) => {
|
|
48190
|
+
runRequestExclusive4(() => this.executeJobBackground(job, input, runId, opts)).catch(async (err) => {
|
|
47927
48191
|
this.logger.error(`Background job execution failed: ${err}`);
|
|
47928
48192
|
try {
|
|
47929
48193
|
await this.updateState(runId, {
|
|
@@ -47963,18 +48227,20 @@ var init_job_execution_manager = __esm({
|
|
|
47963
48227
|
};
|
|
47964
48228
|
await this.stateStore.createRun(runRecord);
|
|
47965
48229
|
if (opts.background) {
|
|
47966
|
-
this.executeWorkflowBackground(workflow, jobRegistry, runId, opts).catch(
|
|
47967
|
-
|
|
47968
|
-
|
|
47969
|
-
|
|
47970
|
-
|
|
47971
|
-
|
|
47972
|
-
|
|
47973
|
-
|
|
47974
|
-
|
|
47975
|
-
|
|
48230
|
+
runRequestExclusive4(() => this.executeWorkflowBackground(workflow, jobRegistry, runId, opts)).catch(
|
|
48231
|
+
async (err) => {
|
|
48232
|
+
this.logger.error(`Background workflow execution failed: ${err}`);
|
|
48233
|
+
try {
|
|
48234
|
+
await this.updateState(runId, {
|
|
48235
|
+
state: "failed",
|
|
48236
|
+
error: { message: err?.message ?? String(err), name: err?.name ?? "Error" },
|
|
48237
|
+
completedAt: Date.now()
|
|
48238
|
+
});
|
|
48239
|
+
} catch (updateErr) {
|
|
48240
|
+
this.logger.error(`Failed to update run state after error: ${updateErr}`);
|
|
48241
|
+
}
|
|
47976
48242
|
}
|
|
47977
|
-
|
|
48243
|
+
);
|
|
47978
48244
|
return { runId, state: "pending" };
|
|
47979
48245
|
}
|
|
47980
48246
|
return this.executeWorkflowInline(workflow, jobRegistry, runId, opts);
|
|
@@ -48027,7 +48293,7 @@ var init_job_execution_manager = __esm({
|
|
|
48027
48293
|
authInfo: opts.authInfo ?? {},
|
|
48028
48294
|
contextProviders: opts.contextProviders
|
|
48029
48295
|
});
|
|
48030
|
-
const result = await ctx.execute(parsedInput);
|
|
48296
|
+
const result = await runOnSurface("job", async () => ctx.execute(parsedInput));
|
|
48031
48297
|
const logs = ctx.getLogs();
|
|
48032
48298
|
await this.updateState(runId, {
|
|
48033
48299
|
state: "completed",
|
|
@@ -48262,6 +48528,10 @@ var init_job_interface = __esm({
|
|
|
48262
48528
|
getLogs() {
|
|
48263
48529
|
return this._logs;
|
|
48264
48530
|
}
|
|
48531
|
+
/** A job (or a workflow step running it) calls tools on the `'job'` surface. */
|
|
48532
|
+
callToolSurface() {
|
|
48533
|
+
return "job";
|
|
48534
|
+
}
|
|
48265
48535
|
};
|
|
48266
48536
|
}
|
|
48267
48537
|
});
|
|
@@ -52401,6 +52671,8 @@ var init_server_interface = __esm({
|
|
|
52401
52671
|
ServerRequest = class extends IncomingMessage {
|
|
52402
52672
|
path;
|
|
52403
52673
|
query;
|
|
52674
|
+
/** Path parameters of the matched route (`:providerId` in `/oauth/provider/:providerId/callback`). */
|
|
52675
|
+
params;
|
|
52404
52676
|
body;
|
|
52405
52677
|
authSession;
|
|
52406
52678
|
};
|
|
@@ -52923,9 +53195,10 @@ var init_agent_execution_loop = __esm({
|
|
|
52923
53195
|
let iterations = 0;
|
|
52924
53196
|
let totalPromptTokens = 0;
|
|
52925
53197
|
let totalCompletionTokens = 0;
|
|
53198
|
+
let timeout;
|
|
52926
53199
|
try {
|
|
52927
53200
|
const timeoutPromise = new Promise((_, reject) => {
|
|
52928
|
-
setTimeout(
|
|
53201
|
+
timeout = setTimeout(
|
|
52929
53202
|
() => reject(new Error(`Agent execution timed out after ${this.config.timeout}ms`)),
|
|
52930
53203
|
this.config.timeout
|
|
52931
53204
|
);
|
|
@@ -52969,6 +53242,8 @@ var init_agent_execution_loop = __esm({
|
|
|
52969
53242
|
error,
|
|
52970
53243
|
durationMs: Date.now() - startTime
|
|
52971
53244
|
};
|
|
53245
|
+
} finally {
|
|
53246
|
+
clearTimeout(timeout);
|
|
52972
53247
|
}
|
|
52973
53248
|
}
|
|
52974
53249
|
/**
|
|
@@ -53288,6 +53563,7 @@ var init_agent_interface = __esm({
|
|
|
53288
53563
|
"libs/sdk/src/common/interfaces/agent.interface.ts"() {
|
|
53289
53564
|
"use strict";
|
|
53290
53565
|
init_agent_execution_loop();
|
|
53566
|
+
init_frontmcp_context_storage();
|
|
53291
53567
|
init_elicitation2();
|
|
53292
53568
|
init_errors();
|
|
53293
53569
|
init_execution_context_interface();
|
|
@@ -53555,6 +53831,10 @@ var init_agent_interface = __esm({
|
|
|
53555
53831
|
async invokeAgent(agentId, input) {
|
|
53556
53832
|
throw new AgentMethodNotAvailableError("invokeAgent", agentId);
|
|
53557
53833
|
}
|
|
53834
|
+
/** An agent calls tools on the `'agent'` surface, as its model's tool calls do. */
|
|
53835
|
+
callToolSurface() {
|
|
53836
|
+
return "agent";
|
|
53837
|
+
}
|
|
53558
53838
|
// ============================================================================
|
|
53559
53839
|
// Notification Methods
|
|
53560
53840
|
// ============================================================================
|
|
@@ -53671,7 +53951,9 @@ var init_agent_interface = __esm({
|
|
|
53671
53951
|
{
|
|
53672
53952
|
sessionId: this.authInfo.sessionId,
|
|
53673
53953
|
getClientCapabilities: (sid) => this.scope.notifications.getClientCapabilities(sid),
|
|
53674
|
-
|
|
53954
|
+
// The request holds what answers this question: a fallback answer the owner already sent
|
|
53955
|
+
// (the re-run of its call), or the 2026-07-28 exchange.
|
|
53956
|
+
tryGetContext: () => this.tryGetContext() ?? this.runningRequestContext(),
|
|
53675
53957
|
entryName: this._agentNameInternal ?? this.agentName,
|
|
53676
53958
|
entryInput: this._agentInputInternal ?? this.input,
|
|
53677
53959
|
elicitationEnabled: this.scope.metadata.elicitation?.enabled === true
|
|
@@ -53681,6 +53963,17 @@ var init_agent_interface = __esm({
|
|
|
53681
53963
|
options
|
|
53682
53964
|
);
|
|
53683
53965
|
}
|
|
53966
|
+
/**
|
|
53967
|
+
* The context of the request this agent runs in. An agent is built with its own provider registry,
|
|
53968
|
+
* which holds no request-scoped instances, so the context is read from the running request.
|
|
53969
|
+
*/
|
|
53970
|
+
runningRequestContext() {
|
|
53971
|
+
try {
|
|
53972
|
+
return this.scope.providers.get(FrontMcpContextStorage).getStore();
|
|
53973
|
+
} catch {
|
|
53974
|
+
return void 0;
|
|
53975
|
+
}
|
|
53976
|
+
}
|
|
53684
53977
|
/**
|
|
53685
53978
|
* Respond with the final output and end execution.
|
|
53686
53979
|
*
|
|
@@ -53836,6 +54129,7 @@ var ChannelContext;
|
|
|
53836
54129
|
var init_channel_interface = __esm({
|
|
53837
54130
|
"libs/sdk/src/common/interfaces/channel.interface.ts"() {
|
|
53838
54131
|
"use strict";
|
|
54132
|
+
init_call_surface();
|
|
53839
54133
|
init_mcp_error();
|
|
53840
54134
|
init_execution_context_interface();
|
|
53841
54135
|
ChannelContext = class extends ExecutionContextBase {
|
|
@@ -53913,13 +54207,27 @@ var init_channel_interface = __esm({
|
|
|
53913
54207
|
this.logger.warn(`Channel "${this.channelName}": pushIncoming called but no handler is wired`);
|
|
53914
54208
|
}
|
|
53915
54209
|
}
|
|
54210
|
+
/**
|
|
54211
|
+
* While it handles an HTTP trigger (a webhook source's request), a channel calls tools on the
|
|
54212
|
+
* `'http-trigger'` surface. Handling any other event, it dispatches in-process.
|
|
54213
|
+
*/
|
|
54214
|
+
callToolSurface() {
|
|
54215
|
+
return getCallSurface() === "http-trigger" ? "http-trigger" : void 0;
|
|
54216
|
+
}
|
|
53916
54217
|
};
|
|
53917
54218
|
}
|
|
53918
54219
|
});
|
|
53919
54220
|
|
|
53920
54221
|
// libs/sdk/src/common/utils/auth-info.utils.ts
|
|
53921
|
-
function
|
|
54222
|
+
function sessionIdPresentedBy(request) {
|
|
54223
|
+
const header = request.headers?.["mcp-session-id"];
|
|
54224
|
+
if (typeof header === "string" && header.length > 0) return header;
|
|
54225
|
+
const query = request.query?.["sessionId"];
|
|
54226
|
+
return typeof query === "string" && query.length > 0 ? query : void 0;
|
|
54227
|
+
}
|
|
54228
|
+
function authInfoFromAuthorization(authorization, presentedSessionId) {
|
|
53922
54229
|
const { token, user, session } = authorization;
|
|
54230
|
+
const verifiedSession = session !== void 0 && presentedSessionId !== void 0 && session.id === presentedSessionId ? session : void 0;
|
|
53923
54231
|
return {
|
|
53924
54232
|
token,
|
|
53925
54233
|
clientId: user?.sub,
|
|
@@ -53929,8 +54237,8 @@ function authInfoFromAuthorization(authorization) {
|
|
|
53929
54237
|
user,
|
|
53930
54238
|
extra: {
|
|
53931
54239
|
user,
|
|
53932
|
-
sessionId:
|
|
53933
|
-
sessionPayload:
|
|
54240
|
+
sessionId: verifiedSession?.id,
|
|
54241
|
+
sessionPayload: verifiedSession?.payload
|
|
53934
54242
|
}
|
|
53935
54243
|
};
|
|
53936
54244
|
}
|
|
@@ -54308,7 +54616,7 @@ var init_decide_request_intent_utils = __esm({
|
|
|
54308
54616
|
});
|
|
54309
54617
|
|
|
54310
54618
|
// libs/sdk/src/common/utils/guard.utils.ts
|
|
54311
|
-
import { isAnonymousSubject as
|
|
54619
|
+
import { isAnonymousSubject as isAnonymousSubject4 } from "@frontmcp/auth";
|
|
54312
54620
|
import {
|
|
54313
54621
|
ConcurrencyLimitError
|
|
54314
54622
|
} from "@frontmcp/guard";
|
|
@@ -54321,15 +54629,19 @@ function enforceIpFilter(scope, clientIp, buildRejection = () => httpRespond.for
|
|
|
54321
54629
|
function buildPartitionContext(source) {
|
|
54322
54630
|
if (!source) return void 0;
|
|
54323
54631
|
const clientId = source.authInfo?.clientId;
|
|
54324
|
-
const userId =
|
|
54325
|
-
const verifiedSessionId = source.authInfo?.sessionId ?? source.authInfo?.extra?.sessionId;
|
|
54632
|
+
const userId = isAnonymousSubject4(clientId) ? void 0 : String(clientId);
|
|
54326
54633
|
const callerKey = userId ? `user:${userId}` : "anonymous";
|
|
54327
54634
|
return {
|
|
54328
|
-
sessionId:
|
|
54635
|
+
sessionId: verifiedSessionOf(source) ?? callerKey,
|
|
54329
54636
|
clientIp: source.metadata?.clientIp,
|
|
54330
54637
|
userId
|
|
54331
54638
|
};
|
|
54332
54639
|
}
|
|
54640
|
+
function verifiedSessionOf(source) {
|
|
54641
|
+
if ("verifiedSessionId" in source) return source.verifiedSessionId;
|
|
54642
|
+
const confirmed = source.authInfo?.sessionId ?? source.authInfo?.extra?.sessionId;
|
|
54643
|
+
return confirmed === source.sessionId ? source.sessionId : void 0;
|
|
54644
|
+
}
|
|
54333
54645
|
function partitionsByIdentity(partitionBy) {
|
|
54334
54646
|
return partitionBy === "session" || partitionBy === "userId" || typeof partitionBy === "function";
|
|
54335
54647
|
}
|
|
@@ -54512,8 +54824,12 @@ var init_primary_auth_provider_interface = __esm({
|
|
|
54512
54824
|
* @param _expectedAudience The protected resource(s) the token must be for
|
|
54513
54825
|
* (`aud` names at least one), when the caller is a
|
|
54514
54826
|
* resource endpoint.
|
|
54827
|
+
* @param _expectedIssuer The issuer the token must name: the one this
|
|
54828
|
+
* instance issues tokens under for the request
|
|
54829
|
+
* (`LocalPrimaryAuth.issuerFor`). Default: its
|
|
54830
|
+
* boot-time issuer.
|
|
54515
54831
|
*/
|
|
54516
|
-
verifyGatewayToken(_token, _requestBaseUrl, _expectedAudience) {
|
|
54832
|
+
verifyGatewayToken(_token, _requestBaseUrl, _expectedAudience, _expectedIssuer) {
|
|
54517
54833
|
return Promise.resolve({
|
|
54518
54834
|
ok: false,
|
|
54519
54835
|
error: "gateway token verification is not supported by this auth mode"
|
|
@@ -55045,7 +55361,10 @@ function buildPerSkillResourceRecord(scope, skill, opts = {}) {
|
|
|
55045
55361
|
description: skill.metadata.description,
|
|
55046
55362
|
mimeType: SKILL_MD_MIME_TYPE,
|
|
55047
55363
|
annotations,
|
|
55048
|
-
_meta: meta
|
|
55364
|
+
_meta: meta,
|
|
55365
|
+
// The skill's own `availableWhen`, so `resources/list` and `resources/read` offer its SKILL.md
|
|
55366
|
+
// on the surfaces that offer the skill, and answer others as for an unknown resource.
|
|
55367
|
+
...skill.metadata.availableWhen !== void 0 && { availableWhen: skill.metadata.availableWhen }
|
|
55049
55368
|
};
|
|
55050
55369
|
const handler = async () => {
|
|
55051
55370
|
const { loadResult } = await findAndLoadSkillByPath(scope, skillPath);
|
|
@@ -55119,6 +55438,8 @@ function refreshFromSkill(resource, skill) {
|
|
|
55119
55438
|
}
|
|
55120
55439
|
Object.assign(resource.metadata, extensions);
|
|
55121
55440
|
resource.metadata["description"] = skill.metadata.description;
|
|
55441
|
+
if (skill.metadata.availableWhen === void 0) Reflect.deleteProperty(resource.metadata, "availableWhen");
|
|
55442
|
+
else resource.metadata["availableWhen"] = skill.metadata.availableWhen;
|
|
55122
55443
|
resource.extensionKeys = Object.keys(extensions);
|
|
55123
55444
|
}
|
|
55124
55445
|
var init_sep_2640_per_skill = __esm({
|
|
@@ -55218,11 +55539,23 @@ var init_skill_audit_helper = __esm({
|
|
|
55218
55539
|
});
|
|
55219
55540
|
|
|
55220
55541
|
// libs/sdk/src/skill/skill-scope.helper.ts
|
|
55542
|
+
function declaresDynamicSkills(metadata) {
|
|
55543
|
+
return metadata.dynamicSkills === true || (metadata.plugins ?? []).some((plugin) => declaresDynamicSkills(normalizePlugin(plugin).metadata));
|
|
55544
|
+
}
|
|
55545
|
+
function pluginsRegisterDynamicSkills(plugins) {
|
|
55546
|
+
for (const plugin of plugins) {
|
|
55547
|
+
if (declaresDynamicSkills(normalizePlugin(plugin).metadata)) return true;
|
|
55548
|
+
}
|
|
55549
|
+
return false;
|
|
55550
|
+
}
|
|
55221
55551
|
async function registerSkillCapabilities(options) {
|
|
55222
|
-
const { skillRegistry, flowRegistry, resourceRegistry, providers, skillsConfig, logger } = options;
|
|
55552
|
+
const { skillRegistry, flowRegistry, resourceRegistry, providers, skillsConfig, logger, dynamicSkills } = options;
|
|
55223
55553
|
registerSkillAuditWriter({ providers, audit: skillsConfig?.audit, logger });
|
|
55224
55554
|
await flowRegistry.registryFlows([FilterSkillsFlow]);
|
|
55225
|
-
if (
|
|
55555
|
+
if (dynamicSkills) {
|
|
55556
|
+
skillRegistry.expectDynamicSkills();
|
|
55557
|
+
}
|
|
55558
|
+
if (!skillRegistry.servesSkills()) {
|
|
55226
55559
|
return;
|
|
55227
55560
|
}
|
|
55228
55561
|
await flowRegistry.registryFlows([SearchSkillsFlow, LoadSkillFlow]);
|
|
@@ -55264,6 +55597,7 @@ async function registerSep2640Resources(options) {
|
|
|
55264
55597
|
var init_skill_scope_helper = __esm({
|
|
55265
55598
|
"libs/sdk/src/skill/skill-scope.helper.ts"() {
|
|
55266
55599
|
"use strict";
|
|
55600
|
+
init_plugin_utils();
|
|
55267
55601
|
init_flows2();
|
|
55268
55602
|
init_resources();
|
|
55269
55603
|
init_sep_2640_last_modified();
|
|
@@ -55849,8 +56183,10 @@ var init_session_store_factory = __esm({
|
|
|
55849
56183
|
});
|
|
55850
56184
|
|
|
55851
56185
|
// libs/sdk/src/transport/flows/handle.mcp-20260728.flow.ts
|
|
56186
|
+
import { isAnonymousSubject as isAnonymousSubject5 } from "@frontmcp/auth";
|
|
55852
56187
|
import { z as z102 } from "@frontmcp/lazy-zod";
|
|
55853
56188
|
import { MCP_20260728_META as MCP_20260728_META5 } from "@frontmcp/protocol";
|
|
56189
|
+
import { runRequestExclusive as runRequestExclusive5 } from "@frontmcp/utils";
|
|
55854
56190
|
function frame2(message) {
|
|
55855
56191
|
return encoder3.encode(`event: message
|
|
55856
56192
|
data: ${JSON.stringify(message)}
|
|
@@ -55900,6 +56236,11 @@ async function* streamMessageResponse(options, sink, requestId, runInContext) {
|
|
|
55900
56236
|
}
|
|
55901
56237
|
yield frame2({ jsonrpc: "2.0", id, result: outcome?.result });
|
|
55902
56238
|
}
|
|
56239
|
+
function isAnonymousCaller(auth) {
|
|
56240
|
+
if (!auth) return true;
|
|
56241
|
+
if (!isAnonymousSubject5(auth.user?.sub)) return false;
|
|
56242
|
+
return !auth.token || auth.session?.payload?.isPublic === true;
|
|
56243
|
+
}
|
|
55903
56244
|
function errorResponse(status, error, id) {
|
|
55904
56245
|
return httpRespond.json(
|
|
55905
56246
|
{
|
|
@@ -55956,11 +56297,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
55956
56297
|
async parseInput() {
|
|
55957
56298
|
const { request } = this.rawInput;
|
|
55958
56299
|
const auth = request[ServerRequestTokens.auth];
|
|
55959
|
-
this.state.set(
|
|
55960
|
-
stateSchema37.parse({
|
|
55961
|
-
isAnonymous: !auth?.token || auth.token.length === 0 || auth.session?.payload?.isPublic === true
|
|
55962
|
-
})
|
|
55963
|
-
);
|
|
56300
|
+
this.state.set(stateSchema37.parse({ isAnonymous: isAnonymousCaller(auth) }));
|
|
55964
56301
|
}
|
|
55965
56302
|
async validate() {
|
|
55966
56303
|
const { request } = this.rawInput;
|
|
@@ -56017,7 +56354,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56017
56354
|
if (!context) return (fn) => fn();
|
|
56018
56355
|
const storage = this.scope.providers.get(FrontMcpContextStorage);
|
|
56019
56356
|
return async (fn) => {
|
|
56020
|
-
await storage.runWithContext(context, fn);
|
|
56357
|
+
await runRequestExclusive5(() => storage.runWithContext(context, fn));
|
|
56021
56358
|
};
|
|
56022
56359
|
}
|
|
56023
56360
|
findToolSchema(scope, toolName) {
|
|
@@ -56103,10 +56440,12 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56103
56440
|
clientCapabilities,
|
|
56104
56441
|
frontmcpContext: this.tryGetContext(),
|
|
56105
56442
|
authInfo: auth ? {
|
|
56443
|
+
// No session is presented: this revision has none, so the request has no verified
|
|
56444
|
+
// session (`extra.sessionId`), and anything kept across requests falls back to the
|
|
56445
|
+
// authenticated caller.
|
|
56106
56446
|
...authInfoFromAuthorization(auth),
|
|
56107
|
-
//
|
|
56108
|
-
//
|
|
56109
|
-
// Derive a request-scoped one so nothing leaks between calls.
|
|
56447
|
+
// The shared handlers still key per-request state (provider views, notifications)
|
|
56448
|
+
// off an id. This one names the request only; the request context does not vouch for it.
|
|
56110
56449
|
sessionId: auth.session?.id
|
|
56111
56450
|
} : void 0,
|
|
56112
56451
|
isAnonymous: this.state.required.isAnonymous,
|
|
@@ -56991,6 +57330,7 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
56991
57330
|
init_ext_apps2();
|
|
56992
57331
|
init_ha_constants();
|
|
56993
57332
|
init_skill_mode_utils();
|
|
57333
|
+
init_mcp_surface();
|
|
56994
57334
|
plan40 = {
|
|
56995
57335
|
pre: ["parseInput", "router"],
|
|
56996
57336
|
execute: ["onInitialize", "onMessage", "onElicitResult", "onSseListener", "onExtApps"],
|
|
@@ -57373,7 +57713,8 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57373
57713
|
sessionId: session.id,
|
|
57374
57714
|
sessionIdPayload: session.payload,
|
|
57375
57715
|
token
|
|
57376
|
-
}
|
|
57716
|
+
},
|
|
57717
|
+
surface: mcpRequestSurface(this.scope)
|
|
57377
57718
|
}
|
|
57378
57719
|
});
|
|
57379
57720
|
const parsed = CallToolResultSchema4.safeParse(result);
|
|
@@ -59695,6 +60036,41 @@ var init_request_log_redaction = __esm({
|
|
|
59695
60036
|
}
|
|
59696
60037
|
});
|
|
59697
60038
|
|
|
60039
|
+
// libs/sdk/src/transport/persistent-session-owner.ts
|
|
60040
|
+
var persistent_session_owner_exports = {};
|
|
60041
|
+
__export(persistent_session_owner_exports, {
|
|
60042
|
+
claimPersistentSession: () => claimPersistentSession,
|
|
60043
|
+
persistentSessionCallerKey: () => persistentSessionCallerKey
|
|
60044
|
+
});
|
|
60045
|
+
import { isAnonymousSubject as isAnonymousSubject6 } from "@frontmcp/auth";
|
|
60046
|
+
import { sha256Hex as sha256Hex13 } from "@frontmcp/utils";
|
|
60047
|
+
function persistentSessionCallerKey(authorization) {
|
|
60048
|
+
const sub = authorization?.user?.sub;
|
|
60049
|
+
if (!isAnonymousSubject6(sub)) {
|
|
60050
|
+
const iss = authorization?.user?.iss;
|
|
60051
|
+
return `sub:${sha256Hex13(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
|
|
60052
|
+
}
|
|
60053
|
+
if (authorization?.token) return `token:${sha256Hex13(authorization.token)}`;
|
|
60054
|
+
return null;
|
|
60055
|
+
}
|
|
60056
|
+
function claimPersistentSession(session, callerKey, store) {
|
|
60057
|
+
if (!owners.has(session) && store && store.initial !== void 0) {
|
|
60058
|
+
owners.set(session, store.initial);
|
|
60059
|
+
}
|
|
60060
|
+
if (!owners.has(session)) {
|
|
60061
|
+
owners.set(session, callerKey);
|
|
60062
|
+
return "claimed";
|
|
60063
|
+
}
|
|
60064
|
+
return owners.get(session) === callerKey ? "owner" : "refused";
|
|
60065
|
+
}
|
|
60066
|
+
var owners;
|
|
60067
|
+
var init_persistent_session_owner = __esm({
|
|
60068
|
+
"libs/sdk/src/transport/persistent-session-owner.ts"() {
|
|
60069
|
+
"use strict";
|
|
60070
|
+
owners = /* @__PURE__ */ new WeakMap();
|
|
60071
|
+
}
|
|
60072
|
+
});
|
|
60073
|
+
|
|
59698
60074
|
// libs/sdk/src/transport/web-standard-mcp.ts
|
|
59699
60075
|
var web_standard_mcp_exports = {};
|
|
59700
60076
|
__export(web_standard_mcp_exports, {
|
|
@@ -59721,7 +60097,8 @@ async function wireServer(scope, serverOptions, sessionId, sessionIdGenerator, e
|
|
|
59721
60097
|
}
|
|
59722
60098
|
async function buildPersistentWebStandardMcp(scope, options) {
|
|
59723
60099
|
const serverOptions = buildScopedServerOptions(scope);
|
|
59724
|
-
|
|
60100
|
+
const pair = await wireServer(scope, serverOptions, options.sessionId, () => options.sessionId, false);
|
|
60101
|
+
return options.owner ? { ...pair, owner: options.owner } : pair;
|
|
59725
60102
|
}
|
|
59726
60103
|
async function runWebStandardMcp(scope, request, options) {
|
|
59727
60104
|
if (options.persistent) {
|
|
@@ -59730,13 +60107,7 @@ async function runWebStandardMcp(scope, request, options) {
|
|
|
59730
60107
|
const sessionId = `web:${randomUUID26()}`;
|
|
59731
60108
|
const accept = request.headers.get("accept") ?? "";
|
|
59732
60109
|
const wantsStream = request.method === "GET" || options.sse && accept.includes("text/event-stream");
|
|
59733
|
-
const { mcpServer, transport } = await wireServer(
|
|
59734
|
-
scope,
|
|
59735
|
-
options.serverOptions,
|
|
59736
|
-
sessionId,
|
|
59737
|
-
void 0,
|
|
59738
|
-
!wantsStream
|
|
59739
|
-
);
|
|
60110
|
+
const { mcpServer, transport } = await wireServer(scope, options.serverOptions, sessionId, void 0, !wantsStream);
|
|
59740
60111
|
const response = await transport.handleRequest(request, {
|
|
59741
60112
|
authInfo: options.authInfo
|
|
59742
60113
|
});
|
|
@@ -59798,6 +60169,10 @@ var init_http_request_flow = __esm({
|
|
|
59798
60169
|
"router"
|
|
59799
60170
|
],
|
|
59800
60171
|
execute: [
|
|
60172
|
+
// A Durable Object's persistent session serves only the caller that opened
|
|
60173
|
+
// it: anyone else presenting its id is answered as an unknown session. First,
|
|
60174
|
+
// before every protocol handler (2026-07-28 included), so none can serve it.
|
|
60175
|
+
"checkPersistentSessionOwner",
|
|
59801
60176
|
// Protocol 2026-07-28. Runs before EVERY session-era handler — including
|
|
59802
60177
|
// `handleWebFetch`, which answers any request carrying a Web `Request`
|
|
59803
60178
|
// regardless of intent and would otherwise swallow 2026 traffic on a
|
|
@@ -59934,7 +60309,8 @@ var init_http_request_flow = __esm({
|
|
|
59934
60309
|
const ctx = this.tryGetContext();
|
|
59935
60310
|
if (ctx) {
|
|
59936
60311
|
const { token, user } = result.authorization;
|
|
59937
|
-
|
|
60312
|
+
const presentedSessionId = this.servesProtocol20260728() ? void 0 : sessionIdPresentedBy(request);
|
|
60313
|
+
ctx.updateAuthInfo(authInfoFromAuthorization(result.authorization, presentedSessionId));
|
|
59938
60314
|
await this.bindOrchestrationAccessor(ctx, token, user);
|
|
59939
60315
|
}
|
|
59940
60316
|
}
|
|
@@ -59986,6 +60362,24 @@ var init_http_request_flow = __esm({
|
|
|
59986
60362
|
});
|
|
59987
60363
|
ctx.setContextToken(ORCHESTRATED_AUTH_ACCESSOR2, new OrchestratedAuthAccessorAdapter(authorization));
|
|
59988
60364
|
}
|
|
60365
|
+
/**
|
|
60366
|
+
* Whether this request is served under MCP protocol 2026-07-28.
|
|
60367
|
+
*
|
|
60368
|
+
* A V8-isolate deployment (Cloudflare Worker) defaults to the stateless revision: it needs no
|
|
60369
|
+
* session storage, so an unversioned call is served directly instead of minting a session in a
|
|
60370
|
+
* Durable Object. Node keeps defaulting to the session pipeline. Either way an explicit declaration
|
|
60371
|
+
* from the client always wins.
|
|
60372
|
+
*/
|
|
60373
|
+
servesProtocol20260728() {
|
|
60374
|
+
const { request } = this.rawInput;
|
|
60375
|
+
const isWebMode = !!request[ServerRequestTokens.webRequest];
|
|
60376
|
+
const defaultProtocolVersion = this.scope.metadata.transport?.defaultProtocolVersion ?? (isWebMode ? "2026-07-28" : "legacy");
|
|
60377
|
+
return isProtocol20260728Request({
|
|
60378
|
+
headers: request.headers,
|
|
60379
|
+
body: request.body,
|
|
60380
|
+
defaultVersion: defaultProtocolVersion
|
|
60381
|
+
});
|
|
60382
|
+
}
|
|
59989
60383
|
async router() {
|
|
59990
60384
|
try {
|
|
59991
60385
|
const { request } = this.rawInput;
|
|
@@ -60019,12 +60413,7 @@ var init_http_request_flow = __esm({
|
|
|
60019
60413
|
reasons: decision.reasons,
|
|
60020
60414
|
debug: decision.debug
|
|
60021
60415
|
});
|
|
60022
|
-
|
|
60023
|
-
if (isProtocol20260728Request({
|
|
60024
|
-
headers: request.headers,
|
|
60025
|
-
body: request.body,
|
|
60026
|
-
defaultVersion: defaultProtocolVersion
|
|
60027
|
-
})) {
|
|
60416
|
+
if (this.servesProtocol20260728()) {
|
|
60028
60417
|
const verify = this.state.required.verifyResult;
|
|
60029
60418
|
if (verify.kind === "authorized") {
|
|
60030
60419
|
request[ServerRequestTokens.auth] = verify.authorization;
|
|
@@ -60070,6 +60459,11 @@ var init_http_request_flow = __esm({
|
|
|
60070
60459
|
const deleteVerify = this.state.required.verifyResult;
|
|
60071
60460
|
const requestedSessionId = request[ServerRequestTokens.sessionId] ?? request.headers["mcp-session-id"] ?? request.query?.["sessionId"];
|
|
60072
60461
|
const deleteAuthz = authorizeSessionTermination(deleteVerify, requestedSessionId);
|
|
60462
|
+
if (request[ServerRequestTokens.webTransport] && deleteAuthz.kind !== "unauthorized") {
|
|
60463
|
+
request[ServerRequestTokens.auth] = deleteVerify.authorization;
|
|
60464
|
+
this.state.set("intent", decision.intent);
|
|
60465
|
+
return;
|
|
60466
|
+
}
|
|
60073
60467
|
if (deleteAuthz.kind === "unauthorized") {
|
|
60074
60468
|
this.logger.warn(`[${this.requestId}] DELETE session denied: request is not authenticated`);
|
|
60075
60469
|
this.respond(
|
|
@@ -60183,6 +60577,28 @@ var init_http_request_flow = __esm({
|
|
|
60183
60577
|
throw error;
|
|
60184
60578
|
}
|
|
60185
60579
|
}
|
|
60580
|
+
async checkPersistentSessionOwner() {
|
|
60581
|
+
const req = this.rawInput.request;
|
|
60582
|
+
const persistent = req[ServerRequestTokens.webTransport];
|
|
60583
|
+
if (!req[ServerRequestTokens.webRequest] || !persistent || typeof persistent !== "object") return;
|
|
60584
|
+
const { claimPersistentSession: claimPersistentSession2, persistentSessionCallerKey: persistentSessionCallerKey2 } = await Promise.resolve().then(() => (init_persistent_session_owner(), persistent_session_owner_exports));
|
|
60585
|
+
const authorization = req[ServerRequestTokens.auth];
|
|
60586
|
+
const callerKey = persistentSessionCallerKey2(authorization);
|
|
60587
|
+
const store = persistent.owner;
|
|
60588
|
+
const claim = claimPersistentSession2(persistent, callerKey, store);
|
|
60589
|
+
if (claim === "claimed") {
|
|
60590
|
+
await store?.save(callerKey);
|
|
60591
|
+
return;
|
|
60592
|
+
}
|
|
60593
|
+
if (claim === "owner") return;
|
|
60594
|
+
this.logger.warn(`[${this.requestId}] Persistent session refused: it was opened by another caller`);
|
|
60595
|
+
this.respond(
|
|
60596
|
+
httpRespond.json(
|
|
60597
|
+
{ jsonrpc: "2.0", error: { code: -32001, message: "Session not found" }, id: null },
|
|
60598
|
+
{ status: 404 }
|
|
60599
|
+
)
|
|
60600
|
+
);
|
|
60601
|
+
}
|
|
60186
60602
|
async handleWebFetch() {
|
|
60187
60603
|
const { request } = this.rawInput;
|
|
60188
60604
|
const req = request;
|
|
@@ -60197,7 +60613,7 @@ var init_http_request_flow = __esm({
|
|
|
60197
60613
|
const proto = expandProtocolConfig2(scope.metadata.transport?.protocol);
|
|
60198
60614
|
const sse = proto.streamable && !proto.json;
|
|
60199
60615
|
const authorization = req[ServerRequestTokens.auth];
|
|
60200
|
-
const authInfo = authorization ? authInfoFromAuthorization(authorization) : void 0;
|
|
60616
|
+
const authInfo = authorization ? authInfoFromAuthorization(authorization, sessionIdPresentedBy(request)) : void 0;
|
|
60201
60617
|
const persistent = req[ServerRequestTokens.webTransport];
|
|
60202
60618
|
const response = await runWebStandardMcp2(scope, webRequest, {
|
|
60203
60619
|
serverOptions: buildScopedServerOptions2(scope),
|
|
@@ -60403,6 +60819,9 @@ var init_http_request_flow = __esm({
|
|
|
60403
60819
|
__decorateClass([
|
|
60404
60820
|
Stage43("router")
|
|
60405
60821
|
], HttpRequestFlow.prototype, "router", 1);
|
|
60822
|
+
__decorateClass([
|
|
60823
|
+
Stage43("checkPersistentSessionOwner")
|
|
60824
|
+
], HttpRequestFlow.prototype, "checkPersistentSessionOwner", 1);
|
|
60406
60825
|
__decorateClass([
|
|
60407
60826
|
Stage43("handleWebFetch")
|
|
60408
60827
|
], HttpRequestFlow.prototype, "handleWebFetch", 1);
|
|
@@ -60990,7 +61409,11 @@ var init_scope_instance = __esm({
|
|
|
60990
61409
|
if (!this.cliMode) {
|
|
60991
61410
|
await this.buildAuthUiRegistry();
|
|
60992
61411
|
}
|
|
60993
|
-
|
|
61412
|
+
const dynamicSkills = pluginsRegisterDynamicSkills([
|
|
61413
|
+
...serverPlugins,
|
|
61414
|
+
...this.scopeApps.getApps().flatMap((app) => "plugins" in app.metadata ? app.metadata.plugins ?? [] : [])
|
|
61415
|
+
]);
|
|
61416
|
+
if (this.scopeSkills.hasAny() || dynamicSkills) {
|
|
60994
61417
|
const store = createSkillSessionStore({ type: "memory" });
|
|
60995
61418
|
this._skillSession = new SkillSessionManager(
|
|
60996
61419
|
{
|
|
@@ -61041,7 +61464,8 @@ var init_scope_instance = __esm({
|
|
|
61041
61464
|
resourceRegistry: this.scopeResources,
|
|
61042
61465
|
providers: this.scopeProviders,
|
|
61043
61466
|
skillsConfig: this.metadata.skillsConfig,
|
|
61044
|
-
logger: this.logger
|
|
61467
|
+
logger: this.logger,
|
|
61468
|
+
dynamicSkills
|
|
61045
61469
|
});
|
|
61046
61470
|
const jobsConfig = this.metadata.jobs;
|
|
61047
61471
|
const metaRecord = this.metadata;
|
|
@@ -61844,7 +62268,7 @@ import {
|
|
|
61844
62268
|
ProviderScope as ProviderScope8,
|
|
61845
62269
|
tokenName as tokenName18
|
|
61846
62270
|
} from "@frontmcp/di";
|
|
61847
|
-
function
|
|
62271
|
+
function verifiedSessionOf2(requestContext) {
|
|
61848
62272
|
const { authInfo } = requestContext;
|
|
61849
62273
|
const verified = authInfo.sessionId ?? authInfo.extra?.["sessionId"];
|
|
61850
62274
|
return typeof verified === "string" && verified.length > 0 && verified !== STATELESS_SESSION_ID ? verified : void 0;
|
|
@@ -62605,7 +63029,7 @@ var init_provider_registry = __esm({
|
|
|
62605
63029
|
if (!(requestContext instanceof FrontMcpContext)) {
|
|
62606
63030
|
return newProviderStore();
|
|
62607
63031
|
}
|
|
62608
|
-
if (!this.sessionCacheEnabled ||
|
|
63032
|
+
if (!this.sessionCacheEnabled || verifiedSessionOf2(requestContext) !== sessionKey) {
|
|
62609
63033
|
let stores = requestContext.get(REQUEST_PROVIDER_STORES);
|
|
62610
63034
|
if (!stores) {
|
|
62611
63035
|
stores = /* @__PURE__ */ new WeakMap();
|
|
@@ -63027,6 +63451,7 @@ var init_web_response_renderer = __esm({
|
|
|
63027
63451
|
});
|
|
63028
63452
|
|
|
63029
63453
|
// libs/sdk/src/transport/web-fetch-handler.ts
|
|
63454
|
+
import { runRequestExclusive as runRequestExclusive6 } from "@frontmcp/utils";
|
|
63030
63455
|
function isCloudflareWorkersRuntime(request) {
|
|
63031
63456
|
if (typeof navigator !== "undefined" && navigator.userAgent === CLOUDFLARE_WORKERS_USER_AGENT) return true;
|
|
63032
63457
|
const cloudflareProperties = request.cf;
|
|
@@ -63140,10 +63565,12 @@ async function runHttpRequestFlowWeb(scope, request, opts = {}) {
|
|
|
63140
63565
|
const serverRequest = await toServerRequest(request, url, opts.ctx, opts.persistent, opts.env);
|
|
63141
63566
|
let output;
|
|
63142
63567
|
try {
|
|
63143
|
-
output = await
|
|
63144
|
-
request
|
|
63145
|
-
|
|
63146
|
-
|
|
63568
|
+
output = await runRequestExclusive6(
|
|
63569
|
+
() => scope.runFlow("http:request", {
|
|
63570
|
+
request: serverRequest,
|
|
63571
|
+
response: {}
|
|
63572
|
+
})
|
|
63573
|
+
);
|
|
63147
63574
|
} catch (error) {
|
|
63148
63575
|
output = flowErrorToHttpOutput(error);
|
|
63149
63576
|
}
|
|
@@ -63156,10 +63583,12 @@ async function runMatchingHttpFlowWeb(scope, request, opts = {}) {
|
|
|
63156
63583
|
if (!flowName) return void 0;
|
|
63157
63584
|
let output;
|
|
63158
63585
|
try {
|
|
63159
|
-
output = await
|
|
63160
|
-
|
|
63161
|
-
|
|
63162
|
-
|
|
63586
|
+
output = await runRequestExclusive6(
|
|
63587
|
+
() => scope.runFlow(flowName, {
|
|
63588
|
+
request: serverRequest,
|
|
63589
|
+
response: {}
|
|
63590
|
+
})
|
|
63591
|
+
);
|
|
63163
63592
|
} catch (error) {
|
|
63164
63593
|
output = flowErrorToHttpOutput(error);
|
|
63165
63594
|
}
|
|
@@ -63170,6 +63599,7 @@ async function toServerRequest(request, url, ctx, persistent, env) {
|
|
|
63170
63599
|
request.headers.forEach((v, k) => {
|
|
63171
63600
|
headers[k] = v;
|
|
63172
63601
|
});
|
|
63602
|
+
headers["host"] = url.host;
|
|
63173
63603
|
const query = {};
|
|
63174
63604
|
url.searchParams.forEach((v, k) => {
|
|
63175
63605
|
const existing = query[k];
|
|
@@ -63200,6 +63630,8 @@ async function toServerRequest(request, url, ctx, persistent, env) {
|
|
|
63200
63630
|
const webRequest = new Request(url.toString(), init);
|
|
63201
63631
|
const serverRequest = {
|
|
63202
63632
|
method,
|
|
63633
|
+
// The scheme the client used, as Express reports it (`req.protocol`).
|
|
63634
|
+
protocol: url.protocol.slice(0, -1),
|
|
63203
63635
|
path: url.pathname,
|
|
63204
63636
|
url: url.pathname + url.search,
|
|
63205
63637
|
headers,
|
|
@@ -63280,7 +63712,10 @@ var init_web_fetch_handler = __esm({
|
|
|
63280
63712
|
MISCONFIGURATION_REMEDIES = {
|
|
63281
63713
|
SESSION_SECRET_REQUIRED: "Set MCP_SESSION_SECRET in the deployment environment (e.g. `wrangler secret put MCP_SESSION_SECRET`). Session IDs are encrypted with it, and production refuses the development machine-id fallback.",
|
|
63282
63714
|
JWT_SECRET_INVALID: "JWT_SECRET is present but too weak: HS256 requires at least 32 bytes (RFC 7518). Replace it with `openssl rand -hex 32`.",
|
|
63283
|
-
JWT_SECRET_REQUIRED: "Set JWT_SECRET in the deployment environment (e.g. `wrangler secret put JWT_SECRET`). Tokens are signed with it; production refuses the random per-process fallback because tokens would not survive a restart or verify across instances."
|
|
63715
|
+
JWT_SECRET_REQUIRED: "Set JWT_SECRET in the deployment environment (e.g. `wrangler secret put JWT_SECRET`). Tokens are signed with it; production refuses the random per-process fallback because tokens would not survive a restart or verify across instances.",
|
|
63716
|
+
// The startup checks: a server whose entries ask for protection nothing gives them does not start.
|
|
63717
|
+
UNENFORCED_METADATA: "An entry declares a field only a plugin enforces (approval, featureFlag, ...) and no installed plugin that enforces it reaches the entry, so the server refuses to start. Install the plugin or remove the field; the server log names the entries.",
|
|
63718
|
+
AUTH_CONFIGURATION_ERROR: "The auth or authorities configuration is invalid (for example, entries declare authorities and the server has no authorities option, or a rule checks nothing), so the server refuses to start. The server log names the entries."
|
|
63284
63719
|
};
|
|
63285
63720
|
}
|
|
63286
63721
|
});
|
|
@@ -63930,12 +64365,172 @@ var init_front_mcp_providers = __esm({
|
|
|
63930
64365
|
}
|
|
63931
64366
|
});
|
|
63932
64367
|
|
|
64368
|
+
// libs/sdk/src/front-mcp/static-startup.check.ts
|
|
64369
|
+
function tryNormalize(normalize) {
|
|
64370
|
+
try {
|
|
64371
|
+
return normalize();
|
|
64372
|
+
} catch {
|
|
64373
|
+
return void 0;
|
|
64374
|
+
}
|
|
64375
|
+
}
|
|
64376
|
+
function asMetadata(value) {
|
|
64377
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
64378
|
+
}
|
|
64379
|
+
function isServedEverywhere(metadata) {
|
|
64380
|
+
const availability = asMetadata(metadata["availableWhen"]);
|
|
64381
|
+
if (!availability) return true;
|
|
64382
|
+
return Object.entries(availability).every(
|
|
64383
|
+
([axis, value]) => value === void 0 || axis === "surface" && Array.isArray(value) && value.length > 0
|
|
64384
|
+
);
|
|
64385
|
+
}
|
|
64386
|
+
function pluginClassOf(record) {
|
|
64387
|
+
switch (record.kind) {
|
|
64388
|
+
case "CLASS_TOKEN" /* CLASS_TOKEN */:
|
|
64389
|
+
return record.provide;
|
|
64390
|
+
case "CLASS" /* CLASS */:
|
|
64391
|
+
return record.useClass;
|
|
64392
|
+
case "VALUE" /* VALUE */:
|
|
64393
|
+
return record.useValue?.constructor;
|
|
64394
|
+
default:
|
|
64395
|
+
return void 0;
|
|
64396
|
+
}
|
|
64397
|
+
}
|
|
64398
|
+
function pluginHooksOf(record) {
|
|
64399
|
+
const pluginClass = pluginClassOf(record);
|
|
64400
|
+
if (!pluginClass) return void 0;
|
|
64401
|
+
return [...collectHook(pluginClass), ...peekPendingTC39HooksForClass(pluginClass)];
|
|
64402
|
+
}
|
|
64403
|
+
function keysEnforcedByPlugins2(plugins, into = /* @__PURE__ */ new Set()) {
|
|
64404
|
+
for (const plugin of plugins ?? []) {
|
|
64405
|
+
const record = tryNormalize(() => normalizePlugin(plugin));
|
|
64406
|
+
if (!record) continue;
|
|
64407
|
+
for (const key of record.metadata.enforcesMetadata ?? []) into.add(key);
|
|
64408
|
+
keysEnforcedByPlugins2(record.metadata.plugins, into);
|
|
64409
|
+
}
|
|
64410
|
+
return into;
|
|
64411
|
+
}
|
|
64412
|
+
function collectStaticEntries(config) {
|
|
64413
|
+
const found = [];
|
|
64414
|
+
const everyAppKeys = /* @__PURE__ */ new Set();
|
|
64415
|
+
const ownAppKeys = /* @__PURE__ */ new Map();
|
|
64416
|
+
const add = (label, metadata, reach) => {
|
|
64417
|
+
const fields = asMetadata(metadata);
|
|
64418
|
+
if (!fields || !isServedEverywhere(fields)) return false;
|
|
64419
|
+
found.push({ label, metadata: fields, reach });
|
|
64420
|
+
return true;
|
|
64421
|
+
};
|
|
64422
|
+
const keysOfApp = (app) => {
|
|
64423
|
+
const keys = ownAppKeys.get(app) ?? /* @__PURE__ */ new Set();
|
|
64424
|
+
ownAppKeys.set(app, keys);
|
|
64425
|
+
return keys;
|
|
64426
|
+
};
|
|
64427
|
+
const visitPlugins = (plugins, app) => {
|
|
64428
|
+
for (const plugin of plugins ?? []) {
|
|
64429
|
+
const record = tryNormalize(() => normalizePlugin(plugin));
|
|
64430
|
+
if (!record) continue;
|
|
64431
|
+
const hooks = pluginHooksOf(record);
|
|
64432
|
+
if (hooks === void 0 || hooks.length > 0) {
|
|
64433
|
+
const gatesOnlyItsApp = hooks !== void 0 && !hooks.some((hook) => hook.appliesTo === "uncovered-apps");
|
|
64434
|
+
const keys = app && gatesOnlyItsApp ? keysOfApp(app) : everyAppKeys;
|
|
64435
|
+
for (const key of record.metadata.enforcesMetadata ?? []) keys.add(key);
|
|
64436
|
+
}
|
|
64437
|
+
visitEntries(record.metadata, app);
|
|
64438
|
+
}
|
|
64439
|
+
};
|
|
64440
|
+
const visitEntries = (owner, app) => {
|
|
64441
|
+
const reach = app ? { app } : "every-plugin";
|
|
64442
|
+
for (const item of owner.tools ?? []) {
|
|
64443
|
+
const record = tryNormalize(() => normalizeTool(item));
|
|
64444
|
+
if (record) add(`Tool "${record.metadata.id || record.metadata.name}"`, record.metadata, reach);
|
|
64445
|
+
}
|
|
64446
|
+
for (const item of owner.resources ?? []) {
|
|
64447
|
+
if (tryNormalize(() => isResourceTemplate(item))) {
|
|
64448
|
+
const record = tryNormalize(() => normalizeResourceTemplate(item));
|
|
64449
|
+
if (record) add(`Resource template "${record.metadata.name}"`, record.metadata, reach);
|
|
64450
|
+
} else {
|
|
64451
|
+
const record = tryNormalize(() => normalizeResource(item));
|
|
64452
|
+
if (record) add(`Resource "${record.metadata.name}"`, record.metadata, reach);
|
|
64453
|
+
}
|
|
64454
|
+
}
|
|
64455
|
+
for (const item of owner.prompts ?? []) {
|
|
64456
|
+
const record = tryNormalize(() => normalizePrompt(item));
|
|
64457
|
+
if (record) add(`Prompt "${record.metadata.name}"`, record.metadata, reach);
|
|
64458
|
+
}
|
|
64459
|
+
for (const item of owner.agents ?? []) {
|
|
64460
|
+
const record = tryNormalize(() => normalizeAgent(item));
|
|
64461
|
+
if (!record) continue;
|
|
64462
|
+
const agentName = record.metadata.id ?? record.metadata.name;
|
|
64463
|
+
if (!add(`Agent "${agentName}"`, record.metadata, reach)) continue;
|
|
64464
|
+
const agentKeys = record.metadata.execution?.useToolFlow === false ? /* @__PURE__ */ new Set() : keysEnforcedByPlugins2(record.metadata.plugins);
|
|
64465
|
+
for (const toolItem of record.metadata.tools ?? []) {
|
|
64466
|
+
const toolRecord = tryNormalize(() => normalizeTool(toolItem));
|
|
64467
|
+
if (toolRecord) add(`Tool "${agentName}:${toolRecord.metadata.name}"`, toolRecord.metadata, { agentKeys });
|
|
64468
|
+
}
|
|
64469
|
+
}
|
|
64470
|
+
for (const item of owner.skills ?? []) {
|
|
64471
|
+
const record = tryNormalize(() => normalizeSkill(item));
|
|
64472
|
+
if (record) add(`Skill "${record.metadata.id ?? record.metadata.name}"`, record.metadata, "every-plugin");
|
|
64473
|
+
}
|
|
64474
|
+
visitPlugins(owner.plugins, app);
|
|
64475
|
+
};
|
|
64476
|
+
const server = config;
|
|
64477
|
+
visitEntries({ skills: server.skills, plugins: server.plugins }, void 0);
|
|
64478
|
+
for (const app of server.apps ?? []) {
|
|
64479
|
+
const record = tryNormalize(() => normalizeApp(app));
|
|
64480
|
+
if (record?.kind === "LOCAL_CLASS" /* LOCAL_CLASS */) visitEntries(record.metadata, record);
|
|
64481
|
+
}
|
|
64482
|
+
const everyPluginKeys = /* @__PURE__ */ new Set([...everyAppKeys, ...[...ownAppKeys.values()].flatMap((keys) => [...keys])]);
|
|
64483
|
+
const keysReaching = (reach) => {
|
|
64484
|
+
if (reach === "every-plugin") return everyPluginKeys;
|
|
64485
|
+
if ("agentKeys" in reach) return reach.agentKeys;
|
|
64486
|
+
return /* @__PURE__ */ new Set([...everyAppKeys, ...ownAppKeys.get(reach.app) ?? []]);
|
|
64487
|
+
};
|
|
64488
|
+
return found.map(({ label, metadata, reach }) => ({ label, metadata, enforcedBy: keysReaching(reach) }));
|
|
64489
|
+
}
|
|
64490
|
+
function assertStaticStartupConfig(config) {
|
|
64491
|
+
const entries = collectStaticEntries(config);
|
|
64492
|
+
if (!config.authorities) {
|
|
64493
|
+
const labels = entries.filter(({ metadata }) => isEnforcementRequested(metadata["authorities"])).map(({ label }) => label);
|
|
64494
|
+
if (labels.length > 0) {
|
|
64495
|
+
const names = labels.slice(0, 5).join(", ");
|
|
64496
|
+
const suffix = labels.length > 5 ? ` and ${labels.length - 5} more` : "";
|
|
64497
|
+
throw new AuthConfigurationError(
|
|
64498
|
+
`Authorities configuration required: ${names}${suffix} declare 'authorities' metadata but authorities enforcement is not fully configured (engine/context builder missing). Add 'authorities: { claimsMapping: {...}, profiles: {...} }' to your @FrontMcp() decorator to enable enforcement, or remove 'authorities' from entry metadata.`,
|
|
64499
|
+
{ suggestion: "Add authorities config to @FrontMcp() or remove authorities from entry metadata" }
|
|
64500
|
+
);
|
|
64501
|
+
}
|
|
64502
|
+
}
|
|
64503
|
+
const problems = entries.flatMap(
|
|
64504
|
+
({ label, metadata, enforcedBy }) => getEnforcedMetadataKeys().filter((key) => isEnforcementRequested(metadata[key]) && !enforcedBy.has(key)).map((key) => {
|
|
64505
|
+
const enforcer = describeMetadataEnforcer(key);
|
|
64506
|
+
return `${label} declares '${key}'${enforcer ? ` (enforced by ${enforcer})` : ""}`;
|
|
64507
|
+
})
|
|
64508
|
+
);
|
|
64509
|
+
if (problems.length > 0) throw new UnenforcedMetadataError(problems);
|
|
64510
|
+
}
|
|
64511
|
+
var init_static_startup_check = __esm({
|
|
64512
|
+
"libs/sdk/src/front-mcp/static-startup.check.ts"() {
|
|
64513
|
+
"use strict";
|
|
64514
|
+
init_agent_utils();
|
|
64515
|
+
init_app_utils();
|
|
64516
|
+
init_hook_decorator();
|
|
64517
|
+
init_enforced_metadata_utils();
|
|
64518
|
+
init_errors();
|
|
64519
|
+
init_hooks_utils();
|
|
64520
|
+
init_plugin_utils();
|
|
64521
|
+
init_prompt_utils();
|
|
64522
|
+
init_resource_utils();
|
|
64523
|
+
init_skill_utils();
|
|
64524
|
+
init_tool_utils();
|
|
64525
|
+
}
|
|
64526
|
+
});
|
|
64527
|
+
|
|
63933
64528
|
// libs/sdk/src/front-mcp/front-mcp.ts
|
|
63934
64529
|
var front_mcp_exports = {};
|
|
63935
64530
|
__export(front_mcp_exports, {
|
|
63936
64531
|
FrontMcpInstance: () => FrontMcpInstance
|
|
63937
64532
|
});
|
|
63938
|
-
import { fileExists as fileExists7, randomUUID as randomUUID28, unlink as unlink2 } from "@frontmcp/utils";
|
|
64533
|
+
import { fileExists as fileExists7, isEdgeRuntime as isEdgeRuntime2, randomUUID as randomUUID28, unlink as unlink2 } from "@frontmcp/utils";
|
|
63939
64534
|
function resolveConfigInput(optionsOrClass) {
|
|
63940
64535
|
if (typeof optionsOrClass === "function") {
|
|
63941
64536
|
const stored = getDecoratorConfig(optionsOrClass);
|
|
@@ -63963,6 +64558,7 @@ var init_front_mcp = __esm({
|
|
|
63963
64558
|
init_task();
|
|
63964
64559
|
init_web_fetch_handler();
|
|
63965
64560
|
init_front_mcp_providers();
|
|
64561
|
+
init_static_startup_check();
|
|
63966
64562
|
stdioServing = false;
|
|
63967
64563
|
FrontMcpInstance = class _FrontMcpInstance2 {
|
|
63968
64564
|
config;
|
|
@@ -64121,17 +64717,25 @@ var init_front_mcp = __esm({
|
|
|
64121
64717
|
* Express / Node `req`/`res` and runs on V8-isolate targets (Cloudflare
|
|
64122
64718
|
* Workers, Deno Deploy, Bun).
|
|
64123
64719
|
*
|
|
64720
|
+
* A server the startup checks refuse (an entry whose `approval`, `featureFlag`
|
|
64721
|
+
* or `authorities` nothing enforces, a missing secret, ...) rejects here, as
|
|
64722
|
+
* with {@link createDirect}. On an edge isolate the server is built on its
|
|
64723
|
+
* first request instead (see below).
|
|
64724
|
+
*
|
|
64124
64725
|
* @example
|
|
64125
64726
|
* // worker entry
|
|
64126
64727
|
* const handler = await FrontMcpInstance.createFetchHandler(config);
|
|
64127
64728
|
* export default { fetch: (request) => handler(request) };
|
|
64128
64729
|
*/
|
|
64129
64730
|
static async createFetchHandler(options) {
|
|
64130
|
-
|
|
64131
|
-
let building;
|
|
64132
|
-
const build = async () => {
|
|
64731
|
+
const build = async (deferred) => {
|
|
64133
64732
|
const frontMcp = new _FrontMcpInstance2(frontMcpMetadataSchema.parse(options));
|
|
64134
|
-
|
|
64733
|
+
try {
|
|
64734
|
+
await frontMcp.ready;
|
|
64735
|
+
} catch (err) {
|
|
64736
|
+
if (deferred) frontMcp.log?.error("FrontMCP failed to start; requests are refused until it does", err);
|
|
64737
|
+
throw err;
|
|
64738
|
+
}
|
|
64135
64739
|
const scope = frontMcp.getPrimaryScope();
|
|
64136
64740
|
if (!scope) {
|
|
64137
64741
|
throw new ServerNotFoundError();
|
|
@@ -64139,10 +64743,14 @@ var init_front_mcp = __esm({
|
|
|
64139
64743
|
frontMcp.log?.info("FrontMCP fetch handler created (web-standard transport)");
|
|
64140
64744
|
return createWebFetchHandler(scope);
|
|
64141
64745
|
};
|
|
64746
|
+
if (!isEdgeRuntime2()) return build(false);
|
|
64747
|
+
assertStaticStartupConfig(options);
|
|
64748
|
+
let inner;
|
|
64749
|
+
let building;
|
|
64142
64750
|
return async (request, ctx, env) => {
|
|
64143
64751
|
if (!inner) {
|
|
64144
64752
|
if (!building) {
|
|
64145
|
-
building = build().catch((err) => {
|
|
64753
|
+
building = build(true).catch((err) => {
|
|
64146
64754
|
building = void 0;
|
|
64147
64755
|
throw err;
|
|
64148
64756
|
});
|
|
@@ -64649,7 +65257,8 @@ async function executeTaskWorker(options, taskId) {
|
|
|
64649
65257
|
await runTaskInBackground({
|
|
64650
65258
|
record,
|
|
64651
65259
|
cleanedRequestParams: record.request.params,
|
|
64652
|
-
|
|
65260
|
+
// On the surface of the call that created the task, as the in-process runner runs it.
|
|
65261
|
+
ctx: { authInfo: { sessionId }, ...record.surface !== void 0 && { surface: record.surface } },
|
|
64653
65262
|
scope,
|
|
64654
65263
|
store: taskStore,
|
|
64655
65264
|
registry,
|
|
@@ -66519,6 +67128,7 @@ var init_common = __esm({
|
|
|
66519
67128
|
// libs/sdk/src/index.ts
|
|
66520
67129
|
init_common();
|
|
66521
67130
|
init_front_mcp2();
|
|
67131
|
+
init_static_startup_check();
|
|
66522
67132
|
init_serverless_handler();
|
|
66523
67133
|
init_common();
|
|
66524
67134
|
init_errors();
|
|
@@ -66856,6 +67466,39 @@ init_agent_decorator();
|
|
|
66856
67466
|
|
|
66857
67467
|
// libs/sdk/src/index.ts
|
|
66858
67468
|
init_adapters();
|
|
67469
|
+
|
|
67470
|
+
// libs/sdk/src/hooks/hook-coverage.ts
|
|
67471
|
+
init_lineage_utils();
|
|
67472
|
+
function gateOf(scope, entry) {
|
|
67473
|
+
if ("tool" in entry) {
|
|
67474
|
+
return {
|
|
67475
|
+
flow: "tools:call-tool",
|
|
67476
|
+
ownerId: hookOwnerIdOf(scope.tools.lineageOf(entry.tool) ?? [], entry.tool.owner)
|
|
67477
|
+
};
|
|
67478
|
+
}
|
|
67479
|
+
if ("resource" in entry) {
|
|
67480
|
+
return {
|
|
67481
|
+
flow: "resources:read-resource",
|
|
67482
|
+
ownerId: appOwnerIdOf(scope.resources.lineageOf(entry.resource) ?? [], entry.resource.owner)
|
|
67483
|
+
};
|
|
67484
|
+
}
|
|
67485
|
+
if ("prompt" in entry) {
|
|
67486
|
+
return {
|
|
67487
|
+
flow: "prompts:get-prompt",
|
|
67488
|
+
ownerId: appOwnerIdOf(scope.prompts.lineageOf(entry.prompt) ?? [], entry.prompt.owner)
|
|
67489
|
+
};
|
|
67490
|
+
}
|
|
67491
|
+
return {
|
|
67492
|
+
flow: "skills:filter",
|
|
67493
|
+
ownerId: appOwnerIdOf(scope.skills.lineageOf?.(entry.skill) ?? [], entry.skill.owner)
|
|
67494
|
+
};
|
|
67495
|
+
}
|
|
67496
|
+
function isEntryGatedBy(scope, entry, target) {
|
|
67497
|
+
const { flow, ownerId } = gateOf(scope, entry);
|
|
67498
|
+
return scope.hooks.getFlowHooksForOwner(flow, ownerId).some((hook) => hook.metadata.target === target);
|
|
67499
|
+
}
|
|
67500
|
+
|
|
67501
|
+
// libs/sdk/src/index.ts
|
|
66859
67502
|
init_config();
|
|
66860
67503
|
init_config();
|
|
66861
67504
|
init_direct();
|
|
@@ -66876,6 +67519,7 @@ init_mcp_stateless_client_adapter();
|
|
|
66876
67519
|
init_ext_apps2();
|
|
66877
67520
|
import { PROTOCOL_2026_07_28 as PROTOCOL_2026_07_284 } from "@frontmcp/protocol";
|
|
66878
67521
|
import { InMemoryOrchestratedTokenStore as InMemoryOrchestratedTokenStore2 } from "@frontmcp/auth";
|
|
67522
|
+
import { isAnonymousSubject as isAnonymousSubject7 } from "@frontmcp/auth";
|
|
66879
67523
|
(function suppressZodExpressWarning() {
|
|
66880
67524
|
if (typeof process === "undefined" || typeof process.emitWarning !== "function") return;
|
|
66881
67525
|
const originalEmitWarning = process.emitWarning.bind(process);
|
|
@@ -67270,6 +67914,7 @@ export {
|
|
|
67270
67914
|
SessionClaimConflictError,
|
|
67271
67915
|
SessionHookStage,
|
|
67272
67916
|
SessionIdEmptyError,
|
|
67917
|
+
SessionIdentityRequiredError,
|
|
67273
67918
|
SessionMissingError,
|
|
67274
67919
|
SessionSecretRequiredError,
|
|
67275
67920
|
SessionVerificationFailedError,
|
|
@@ -67397,6 +68042,7 @@ export {
|
|
|
67397
68042
|
appAuthOptionsSchema,
|
|
67398
68043
|
appFilterConfigSchema,
|
|
67399
68044
|
applyPrimitiveFilter,
|
|
68045
|
+
assertStaticStartupConfig,
|
|
67400
68046
|
authInfoFromAuthorization,
|
|
67401
68047
|
authOptionsSchema,
|
|
67402
68048
|
authenticateFnSchema,
|
|
@@ -67568,6 +68214,7 @@ export {
|
|
|
67568
68214
|
incrementalAuthConfigSchema,
|
|
67569
68215
|
intentSchema,
|
|
67570
68216
|
isAlive,
|
|
68217
|
+
isAnonymousSubject7 as isAnonymousSubject,
|
|
67571
68218
|
isAuthError,
|
|
67572
68219
|
isChannelClass,
|
|
67573
68220
|
isChannelFunction,
|
|
@@ -67577,6 +68224,7 @@ export {
|
|
|
67577
68224
|
isDynamicPluginClass,
|
|
67578
68225
|
isElicitationEncryptionAvailable,
|
|
67579
68226
|
isEncryptedBlob,
|
|
68227
|
+
isEntryGatedBy,
|
|
67580
68228
|
isFileInstructions,
|
|
67581
68229
|
isInlineInstructions,
|
|
67582
68230
|
isLazy,
|
|
@@ -67657,6 +68305,7 @@ export {
|
|
|
67657
68305
|
parseTraceContext,
|
|
67658
68306
|
partitionsByIdentity,
|
|
67659
68307
|
pathToEnvKey,
|
|
68308
|
+
peekPendingTC39HooksForClass,
|
|
67660
68309
|
performElicit,
|
|
67661
68310
|
performListRoots,
|
|
67662
68311
|
performSample,
|
|
@@ -67702,6 +68351,7 @@ export {
|
|
|
67702
68351
|
serializeHttpCookie,
|
|
67703
68352
|
serverInfoOptionsSchema,
|
|
67704
68353
|
sessionIdPayloadSchema2 as sessionIdPayloadSchema,
|
|
68354
|
+
sessionIdPresentedBy,
|
|
67705
68355
|
sessionIdSchema,
|
|
67706
68356
|
sessionOptionsSchema,
|
|
67707
68357
|
setNestedValue,
|