veryfront 0.1.159 → 0.1.162
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/esm/deno.js +1 -1
- package/esm/src/agent/index.d.ts +1 -1
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.d.ts +2 -12
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +62 -25
- package/esm/src/agent/types.d.ts +37 -0
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/mcp/http-transport.d.ts +33 -0
- package/esm/src/mcp/http-transport.d.ts.map +1 -0
- package/esm/src/mcp/http-transport.js +97 -0
- package/esm/src/mcp/server.d.ts.map +1 -1
- package/esm/src/mcp/server.js +14 -107
- package/esm/src/platform/adapters/fs/veryfront/base-operations.d.ts +1 -1
- package/esm/src/platform/adapters/fs/veryfront/base-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/directory-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/directory-operations.js +9 -52
- package/esm/src/platform/adapters/fs/veryfront/file-list-access.d.ts +33 -0
- package/esm/src/platform/adapters/fs/veryfront/file-list-access.d.ts.map +1 -0
- package/esm/src/platform/adapters/fs/veryfront/file-list-access.js +49 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts +1 -20
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/stat-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/stat-operations.js +21 -94
- package/esm/src/tool/factory.d.ts +3 -5
- package/esm/src/tool/factory.d.ts.map +1 -1
- package/esm/src/tool/factory.js +2 -1
- package/esm/src/tool/index.d.ts +2 -0
- package/esm/src/tool/index.d.ts.map +1 -1
- package/esm/src/tool/index.js +1 -0
- package/esm/src/tool/remote-source-tools.d.ts +8 -0
- package/esm/src/tool/remote-source-tools.d.ts.map +1 -0
- package/esm/src/tool/remote-source-tools.js +33 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/index.ts +6 -0
- package/src/src/agent/runtime/index.ts +118 -11
- package/src/src/agent/types.ts +47 -0
- package/src/src/mcp/http-transport.ts +163 -0
- package/src/src/mcp/server.ts +15 -123
- package/src/src/platform/adapters/fs/veryfront/base-operations.ts +1 -1
- package/src/src/platform/adapters/fs/veryfront/directory-operations.ts +10 -75
- package/src/src/platform/adapters/fs/veryfront/file-list-access.ts +109 -0
- package/src/src/platform/adapters/fs/veryfront/read-operations.ts +1 -22
- package/src/src/platform/adapters/fs/veryfront/stat-operations.ts +27 -120
- package/src/src/tool/factory.ts +4 -6
- package/src/src/tool/index.ts +5 -0
- package/src/src/tool/remote-source-tools.ts +54 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { dynamicTool } from "./factory.js";
|
|
3
|
+
function toToolInputRecord(input) {
|
|
4
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
return Object.fromEntries(Object.entries(input));
|
|
8
|
+
}
|
|
9
|
+
export function createToolsFromRemoteDefinitions(source, definitions, options = {}) {
|
|
10
|
+
return Object.fromEntries(definitions.map((definition) => {
|
|
11
|
+
const toolName = options.toolNameAliases?.[definition.name] ?? definition.name;
|
|
12
|
+
return [
|
|
13
|
+
toolName,
|
|
14
|
+
dynamicTool({
|
|
15
|
+
id: toolName,
|
|
16
|
+
description: definition.description,
|
|
17
|
+
inputSchema: z.object({}).passthrough(),
|
|
18
|
+
inputSchemaJson: definition.parameters,
|
|
19
|
+
mcp: {
|
|
20
|
+
title: definition.title,
|
|
21
|
+
annotations: definition.annotations,
|
|
22
|
+
},
|
|
23
|
+
execute: async (input, context) => await source.executeTool(definition.name, toToolInputRecord(input), context),
|
|
24
|
+
}),
|
|
25
|
+
];
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
export async function loadRemoteToolsFromSource(source, options = {}) {
|
|
29
|
+
const definitions = await source.listTools(options.context);
|
|
30
|
+
return createToolsFromRemoteDefinitions(source, definitions, {
|
|
31
|
+
toolNameAliases: options.toolNameAliases,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.162";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
package/src/src/agent/index.ts
CHANGED
|
@@ -97,7 +97,13 @@ export type {
|
|
|
97
97
|
MessagePart,
|
|
98
98
|
ModelProvider,
|
|
99
99
|
ModelString,
|
|
100
|
+
ModelTransportRequest,
|
|
101
|
+
ModelTransportResolver,
|
|
100
102
|
ResolvedAgentConfig,
|
|
103
|
+
ResolvedModelTransport,
|
|
104
|
+
ResolvedRuntimeState,
|
|
105
|
+
RuntimeStateRequest,
|
|
106
|
+
RuntimeStateResolver,
|
|
101
107
|
StreamToolCall,
|
|
102
108
|
ToolCall,
|
|
103
109
|
ToolCallPart,
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @module ai/agent/runtime
|
|
12
12
|
*/
|
|
13
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
import {
|
|
15
17
|
type AgentConfig,
|
|
@@ -19,6 +21,7 @@ import {
|
|
|
19
21
|
getTextFromParts,
|
|
20
22
|
type Message,
|
|
21
23
|
type MessagePart,
|
|
24
|
+
type ResolvedRuntimeState,
|
|
22
25
|
type ToolCall,
|
|
23
26
|
type ToolResultPart,
|
|
24
27
|
} from "../types.js";
|
|
@@ -264,6 +267,19 @@ function getRuntimeAllowedRemoteTools(config: AgentConfig): string[] | undefined
|
|
|
264
267
|
return raw.every((toolName) => typeof toolName === "string") ? raw : [];
|
|
265
268
|
}
|
|
266
269
|
|
|
270
|
+
type ResolvedModelTransport = {
|
|
271
|
+
requestedModel: string;
|
|
272
|
+
resolvedModelString: string;
|
|
273
|
+
languageModel: ModelRuntime;
|
|
274
|
+
headers?: dntShim.HeadersInit;
|
|
275
|
+
providerOptions?: Record<string, unknown>;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
type RuntimeStepState = {
|
|
279
|
+
systemPrompt: string;
|
|
280
|
+
context?: Record<string, unknown>;
|
|
281
|
+
};
|
|
282
|
+
|
|
267
283
|
export class AgentRuntime {
|
|
268
284
|
private id: string;
|
|
269
285
|
private config: AgentConfig;
|
|
@@ -279,6 +295,52 @@ export class AgentRuntime {
|
|
|
279
295
|
this.memory = createMemory<Message>(memoryConfig);
|
|
280
296
|
}
|
|
281
297
|
|
|
298
|
+
private async resolveModelTransport(
|
|
299
|
+
context: Record<string, unknown> | undefined,
|
|
300
|
+
modelOverride: string | undefined,
|
|
301
|
+
mode: "generate" | "stream",
|
|
302
|
+
): Promise<ResolvedModelTransport> {
|
|
303
|
+
const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);
|
|
304
|
+
const resolvedModelString = resolveRuntimeModel(modelOverride || this.config.model);
|
|
305
|
+
const transport = await this.config.resolveModelTransport?.({
|
|
306
|
+
agentId: this.id,
|
|
307
|
+
requestedModel,
|
|
308
|
+
resolvedModel: resolvedModelString,
|
|
309
|
+
context,
|
|
310
|
+
mode,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
requestedModel,
|
|
315
|
+
resolvedModelString,
|
|
316
|
+
languageModel: transport?.model ?? resolveModel(resolvedModelString),
|
|
317
|
+
headers: transport?.headers,
|
|
318
|
+
providerOptions: transport?.providerOptions,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async resolveRuntimeState(
|
|
323
|
+
messages: Message[],
|
|
324
|
+
context: Record<string, unknown> | undefined,
|
|
325
|
+
mode: "generate" | "stream",
|
|
326
|
+
step: number,
|
|
327
|
+
systemPrompt: string,
|
|
328
|
+
): Promise<RuntimeStepState> {
|
|
329
|
+
const refreshed: ResolvedRuntimeState | undefined = await this.config.resolveRuntimeState?.({
|
|
330
|
+
agentId: this.id,
|
|
331
|
+
mode,
|
|
332
|
+
step,
|
|
333
|
+
system: systemPrompt,
|
|
334
|
+
messages: [...messages],
|
|
335
|
+
context,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
systemPrompt: refreshed?.system ?? systemPrompt,
|
|
340
|
+
context: refreshed?.context ?? context,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
282
344
|
/**
|
|
283
345
|
* Generate a response (non-streaming)
|
|
284
346
|
*/
|
|
@@ -288,8 +350,9 @@ export class AgentRuntime {
|
|
|
288
350
|
modelOverride?: string,
|
|
289
351
|
maxOutputTokensOverride?: number,
|
|
290
352
|
): Promise<AgentResponse> {
|
|
291
|
-
const
|
|
292
|
-
const
|
|
353
|
+
const transport = await this.resolveModelTransport(context, modelOverride, "generate");
|
|
354
|
+
const requestedModel = transport.requestedModel;
|
|
355
|
+
const resolvedModelString = transport.resolvedModelString;
|
|
293
356
|
if (resolvedModelString !== requestedModel) {
|
|
294
357
|
logger.info(
|
|
295
358
|
`⚡ Using runtime model "${resolvedModelString}" instead of "${requestedModel}".`,
|
|
@@ -326,9 +389,12 @@ export class AgentRuntime {
|
|
|
326
389
|
{
|
|
327
390
|
agentId: this.id,
|
|
328
391
|
projectId: tryGetCacheKeyContext()?.projectId,
|
|
329
|
-
...context,
|
|
330
392
|
},
|
|
393
|
+
context,
|
|
331
394
|
resolvedModelString,
|
|
395
|
+
transport.languageModel,
|
|
396
|
+
transport.headers,
|
|
397
|
+
transport.providerOptions,
|
|
332
398
|
maxOutputTokensOverride,
|
|
333
399
|
),
|
|
334
400
|
);
|
|
@@ -351,8 +417,9 @@ export class AgentRuntime {
|
|
|
351
417
|
maxOutputTokensOverride?: number,
|
|
352
418
|
abortSignal?: AbortSignal,
|
|
353
419
|
): Promise<ReadableStream<Uint8Array>> {
|
|
354
|
-
const
|
|
355
|
-
const
|
|
420
|
+
const transport = await this.resolveModelTransport(context, modelOverride, "stream");
|
|
421
|
+
const requestedModel = transport.requestedModel;
|
|
422
|
+
const resolvedModelString = transport.resolvedModelString;
|
|
356
423
|
if (resolvedModelString !== requestedModel) {
|
|
357
424
|
logger.info(
|
|
358
425
|
`⚡ Using runtime model "${resolvedModelString}" instead of "${requestedModel}".`,
|
|
@@ -389,7 +456,7 @@ export class AgentRuntime {
|
|
|
389
456
|
// Resolve model BEFORE creating the ReadableStream — if this throws
|
|
390
457
|
// (e.g., no_ai_available), the error propagates to the caller who can
|
|
391
458
|
// return a proper error response (503) instead of a 200 with an error event.
|
|
392
|
-
const languageModel =
|
|
459
|
+
const languageModel = transport.languageModel;
|
|
393
460
|
|
|
394
461
|
// Determine inference mode from the resolved model object (not the string),
|
|
395
462
|
// because resolveModel may internally fall back from cloud to local.
|
|
@@ -433,8 +500,11 @@ export class AgentRuntime {
|
|
|
433
500
|
callbacks,
|
|
434
501
|
textPartId,
|
|
435
502
|
toolContext,
|
|
503
|
+
context,
|
|
436
504
|
resolvedModelString,
|
|
437
505
|
languageModel,
|
|
506
|
+
transport.headers,
|
|
507
|
+
transport.providerOptions,
|
|
438
508
|
maxOutputTokensOverride,
|
|
439
509
|
streamAbortSignal,
|
|
440
510
|
);
|
|
@@ -474,15 +544,19 @@ export class AgentRuntime {
|
|
|
474
544
|
private async executeAgentLoop(
|
|
475
545
|
systemPrompt: string,
|
|
476
546
|
messages: Message[],
|
|
477
|
-
|
|
547
|
+
toolContextBase?: ToolExecutionContext,
|
|
548
|
+
runtimeContext?: Record<string, unknown>,
|
|
478
549
|
modelString?: string,
|
|
550
|
+
resolvedModel?: ModelRuntime,
|
|
551
|
+
headers?: dntShim.HeadersInit,
|
|
552
|
+
providerOptions?: Record<string, unknown>,
|
|
479
553
|
maxOutputTokensOverride?: number,
|
|
480
554
|
): Promise<AgentResponse> {
|
|
481
555
|
return withSpan("agent.execution_loop", async (loopSpan) => {
|
|
482
556
|
const { maxAgentSteps } = getPlatformCapabilities();
|
|
483
557
|
const maxSteps = this.computeMaxSteps(maxAgentSteps);
|
|
484
558
|
const effectiveModel = resolveRuntimeModel(modelString || this.config.model);
|
|
485
|
-
const languageModel = resolveModel(effectiveModel);
|
|
559
|
+
const languageModel = resolvedModel ?? resolveModel(effectiveModel);
|
|
486
560
|
|
|
487
561
|
const toolCalls: ToolCall[] = [];
|
|
488
562
|
const currentMessages = [...messages];
|
|
@@ -502,11 +576,24 @@ export class AgentRuntime {
|
|
|
502
576
|
// Request-scoped skill policy (not class-level mutable state)
|
|
503
577
|
let activeSkillPolicy: string[] | undefined;
|
|
504
578
|
const allowedRemoteToolNames = getRuntimeAllowedRemoteTools(this.config);
|
|
579
|
+
let currentSystemPrompt = systemPrompt;
|
|
580
|
+
let currentRuntimeContext = runtimeContext;
|
|
505
581
|
|
|
506
582
|
for (let step = 0; step < maxSteps; step++) {
|
|
507
583
|
this.status = "thinking";
|
|
508
584
|
addSpanEvent(loopSpan, "step_start", { step });
|
|
509
585
|
|
|
586
|
+
const runtimeState = await this.resolveRuntimeState(
|
|
587
|
+
currentMessages,
|
|
588
|
+
currentRuntimeContext,
|
|
589
|
+
"generate",
|
|
590
|
+
step,
|
|
591
|
+
currentSystemPrompt,
|
|
592
|
+
);
|
|
593
|
+
currentSystemPrompt = runtimeState.systemPrompt;
|
|
594
|
+
currentRuntimeContext = runtimeState.context;
|
|
595
|
+
const toolContext = { ...toolContextBase, ...currentRuntimeContext };
|
|
596
|
+
|
|
510
597
|
let tools = isLocal ? [] : await getAvailableTools(this.config.tools, {
|
|
511
598
|
includeSkillTools: Boolean(this.config.skills),
|
|
512
599
|
allowedRemoteToolNames,
|
|
@@ -526,7 +613,7 @@ export class AgentRuntime {
|
|
|
526
613
|
});
|
|
527
614
|
return generateText({
|
|
528
615
|
model: languageModel,
|
|
529
|
-
system:
|
|
616
|
+
system: currentSystemPrompt,
|
|
530
617
|
messages: convertToModelMessages(currentMessages),
|
|
531
618
|
tools: convertToolsToRuntimeTools(tools, {
|
|
532
619
|
model: effectiveModel,
|
|
@@ -535,6 +622,8 @@ export class AgentRuntime {
|
|
|
535
622
|
experimental_repairToolCall: repairToolCall,
|
|
536
623
|
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
|
|
537
624
|
temperature: DEFAULT_TEMPERATURE,
|
|
625
|
+
...(headers ? { headers } : {}),
|
|
626
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
538
627
|
});
|
|
539
628
|
});
|
|
540
629
|
|
|
@@ -764,9 +853,12 @@ export class AgentRuntime {
|
|
|
764
853
|
onFinish?: (response: AgentResponse) => void;
|
|
765
854
|
},
|
|
766
855
|
textPartId?: string,
|
|
767
|
-
|
|
856
|
+
toolContextBase?: Record<string, unknown>,
|
|
857
|
+
runtimeContext?: Record<string, unknown>,
|
|
768
858
|
modelString?: string,
|
|
769
859
|
resolvedModel?: ModelRuntime,
|
|
860
|
+
headers?: dntShim.HeadersInit,
|
|
861
|
+
providerOptions?: Record<string, unknown>,
|
|
770
862
|
maxOutputTokensOverride?: number,
|
|
771
863
|
abortSignal?: AbortSignal,
|
|
772
864
|
): Promise<AgentResponse> {
|
|
@@ -795,12 +887,25 @@ export class AgentRuntime {
|
|
|
795
887
|
let finalFinishReason: string | undefined;
|
|
796
888
|
let latestAssistantText = "";
|
|
797
889
|
const allowedRemoteToolNames = getRuntimeAllowedRemoteTools(this.config);
|
|
890
|
+
let currentSystemPrompt = systemPrompt;
|
|
891
|
+
let currentRuntimeContext = runtimeContext;
|
|
798
892
|
|
|
799
893
|
for (let step = 0; step < maxSteps; step++) {
|
|
800
894
|
throwIfAborted(abortSignal);
|
|
801
895
|
sendSSE(controller, encoder, { type: "step-start" });
|
|
802
896
|
const currentStepToolResults = new Map<string, ToolResultPart>();
|
|
803
897
|
|
|
898
|
+
const runtimeState = await this.resolveRuntimeState(
|
|
899
|
+
currentMessages,
|
|
900
|
+
currentRuntimeContext,
|
|
901
|
+
"stream",
|
|
902
|
+
step,
|
|
903
|
+
currentSystemPrompt,
|
|
904
|
+
);
|
|
905
|
+
currentSystemPrompt = runtimeState.systemPrompt;
|
|
906
|
+
currentRuntimeContext = runtimeState.context;
|
|
907
|
+
const toolContext = { ...toolContextBase, ...currentRuntimeContext };
|
|
908
|
+
|
|
804
909
|
let tools = isLocalStreaming ? [] : await getAvailableTools(this.config.tools, {
|
|
805
910
|
includeSkillTools: Boolean(this.config.skills),
|
|
806
911
|
allowedRemoteToolNames,
|
|
@@ -815,7 +920,7 @@ export class AgentRuntime {
|
|
|
815
920
|
|
|
816
921
|
const result = streamText({
|
|
817
922
|
model: languageModel,
|
|
818
|
-
system:
|
|
923
|
+
system: currentSystemPrompt,
|
|
819
924
|
messages: convertToModelMessages(currentMessages),
|
|
820
925
|
tools: convertToolsToRuntimeTools(tools, {
|
|
821
926
|
model: effectiveModel,
|
|
@@ -824,6 +929,8 @@ export class AgentRuntime {
|
|
|
824
929
|
experimental_repairToolCall: repairToolCall,
|
|
825
930
|
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
|
|
826
931
|
temperature: DEFAULT_TEMPERATURE,
|
|
932
|
+
...(headers ? { headers } : {}),
|
|
933
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
827
934
|
abortSignal,
|
|
828
935
|
});
|
|
829
936
|
|
package/src/src/agent/types.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import * as dntShim from "../../_dnt.shims.js";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
import type { ModelRuntime } from "../provider/types.js";
|
|
7
8
|
import type { RemoteToolSource, Tool } from "../tool/index.js";
|
|
8
9
|
import { INVALID_ARGUMENT } from "../errors/error-registry.js";
|
|
9
10
|
import type { Memory } from "./memory/memory-interface.js";
|
|
@@ -91,6 +92,16 @@ export interface AgentConfig {
|
|
|
91
92
|
};
|
|
92
93
|
/** Restrict runtime model overrides to these "provider/model" strings. */
|
|
93
94
|
allowedModels?: ModelString[];
|
|
95
|
+
/**
|
|
96
|
+
* Optional request-aware hook for overriding the resolved model runtime and
|
|
97
|
+
* provider transport options on a per-call basis.
|
|
98
|
+
*/
|
|
99
|
+
resolveModelTransport?: ModelTransportResolver;
|
|
100
|
+
/**
|
|
101
|
+
* Optional step-boundary hook for refreshing the runtime system prompt and
|
|
102
|
+
* host-owned context during a long-lived run.
|
|
103
|
+
*/
|
|
104
|
+
resolveRuntimeState?: RuntimeStateResolver;
|
|
94
105
|
/**
|
|
95
106
|
* Enable skills for this agent.
|
|
96
107
|
* - true: include all discovered skills from skills/ directory
|
|
@@ -108,6 +119,42 @@ export interface AgentConfig {
|
|
|
108
119
|
|
|
109
120
|
export type ResolvedAgentConfig = AgentConfig & { model: ModelString };
|
|
110
121
|
|
|
122
|
+
export interface ModelTransportRequest {
|
|
123
|
+
agentId: string;
|
|
124
|
+
requestedModel: ModelString;
|
|
125
|
+
resolvedModel: ModelString;
|
|
126
|
+
context?: Record<string, unknown>;
|
|
127
|
+
mode: "generate" | "stream";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface ResolvedModelTransport {
|
|
131
|
+
model?: ModelRuntime;
|
|
132
|
+
headers?: dntShim.HeadersInit;
|
|
133
|
+
providerOptions?: Record<string, unknown>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export type ModelTransportResolver = (
|
|
137
|
+
request: ModelTransportRequest,
|
|
138
|
+
) => ResolvedModelTransport | Promise<ResolvedModelTransport>;
|
|
139
|
+
|
|
140
|
+
export interface RuntimeStateRequest {
|
|
141
|
+
agentId: string;
|
|
142
|
+
mode: "generate" | "stream";
|
|
143
|
+
step: number;
|
|
144
|
+
system: string;
|
|
145
|
+
messages: Message[];
|
|
146
|
+
context?: Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ResolvedRuntimeState {
|
|
150
|
+
system?: string;
|
|
151
|
+
context?: Record<string, unknown>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export type RuntimeStateResolver = (
|
|
155
|
+
request: RuntimeStateRequest,
|
|
156
|
+
) => ResolvedRuntimeState | undefined | Promise<ResolvedRuntimeState | undefined>;
|
|
157
|
+
|
|
111
158
|
// Import for use in AgentMiddleware
|
|
112
159
|
import type { AgentContext, AgentResponse } from "./schemas/index.js";
|
|
113
160
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import type { ToolExecutionContext } from "../tool/index.js";
|
|
3
|
+
import { VeryfrontError } from "../security/input-validation/errors.js";
|
|
4
|
+
import { validateContentType } from "../security/input-validation/limits.js";
|
|
5
|
+
import { SessionManager } from "./session.js";
|
|
6
|
+
|
|
7
|
+
const MAX_REQUEST_BODY_SIZE = 1_048_576; // 1 MB
|
|
8
|
+
const JSON_CONTENT_TYPE = "application/json";
|
|
9
|
+
|
|
10
|
+
type JSONRPCParams = Record<string, unknown> | unknown[];
|
|
11
|
+
|
|
12
|
+
interface JSONRPCRequest {
|
|
13
|
+
jsonrpc: "2.0";
|
|
14
|
+
id?: string | number;
|
|
15
|
+
method: string;
|
|
16
|
+
params?: JSONRPCParams;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface JSONRPCResponse {
|
|
20
|
+
jsonrpc: "2.0";
|
|
21
|
+
id?: string | number;
|
|
22
|
+
result?: unknown;
|
|
23
|
+
error?: {
|
|
24
|
+
code: number;
|
|
25
|
+
message: string;
|
|
26
|
+
data?: unknown;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface MCPHTTPTransportDependencies {
|
|
31
|
+
authEnabled: boolean;
|
|
32
|
+
getCORSHeaders: (requestOrigin?: string | null) => Record<string, string>;
|
|
33
|
+
validateAuth: (request: dntShim.Request) => Promise<boolean>;
|
|
34
|
+
handleRequest: (
|
|
35
|
+
request: JSONRPCRequest,
|
|
36
|
+
context?: ToolExecutionContext,
|
|
37
|
+
) => Promise<JSONRPCResponse>;
|
|
38
|
+
extractRequestContext: (request: dntShim.Request) => ToolExecutionContext | undefined;
|
|
39
|
+
isOriginAllowed: (requestOrigin?: string | null) => boolean;
|
|
40
|
+
sessionCapabilities: Map<string, Record<string, unknown>>;
|
|
41
|
+
sessionManager: SessionManager;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createJSONResponse(body: unknown, init?: dntShim.ResponseInit): dntShim.Response {
|
|
45
|
+
const headers = new dntShim.Headers(init?.headers);
|
|
46
|
+
headers.set("Content-Type", JSON_CONTENT_TYPE);
|
|
47
|
+
return new dntShim.Response(JSON.stringify(body), { ...init, headers });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createJSONRPCErrorResponse(status: number, code: number, message: string): dntShim.Response {
|
|
51
|
+
return createJSONResponse(
|
|
52
|
+
{
|
|
53
|
+
jsonrpc: "2.0",
|
|
54
|
+
id: null,
|
|
55
|
+
error: { code, message },
|
|
56
|
+
},
|
|
57
|
+
{ status },
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createMCPHTTPHandler(
|
|
62
|
+
dependencies: MCPHTTPTransportDependencies,
|
|
63
|
+
): (request: dntShim.Request) => Promise<dntShim.Response> {
|
|
64
|
+
const {
|
|
65
|
+
authEnabled,
|
|
66
|
+
getCORSHeaders,
|
|
67
|
+
validateAuth,
|
|
68
|
+
handleRequest,
|
|
69
|
+
extractRequestContext,
|
|
70
|
+
isOriginAllowed,
|
|
71
|
+
sessionCapabilities,
|
|
72
|
+
sessionManager,
|
|
73
|
+
} = dependencies;
|
|
74
|
+
|
|
75
|
+
return async (request: dntShim.Request) => {
|
|
76
|
+
const requestOrigin = request.headers.get("Origin");
|
|
77
|
+
|
|
78
|
+
if (request.method === "OPTIONS") {
|
|
79
|
+
return new dntShim.Response(null, { status: 204, headers: getCORSHeaders(requestOrigin) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!isOriginAllowed(requestOrigin)) {
|
|
83
|
+
return createJSONRPCErrorResponse(403, -32600, "Forbidden: Origin not allowed");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (authEnabled) {
|
|
87
|
+
const authorized = await validateAuth(request);
|
|
88
|
+
if (!authorized) return new dntShim.Response("Unauthorized", { status: 401 });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (request.method === "DELETE") {
|
|
92
|
+
const sessionId = request.headers.get("MCP-Session-Id");
|
|
93
|
+
if (sessionId) {
|
|
94
|
+
sessionManager.terminate(sessionId);
|
|
95
|
+
sessionCapabilities.delete(sessionId);
|
|
96
|
+
}
|
|
97
|
+
return new dntShim.Response(null, { status: 200, headers: getCORSHeaders(requestOrigin) });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (request.method !== "POST") {
|
|
101
|
+
return new dntShim.Response("Method Not Allowed", { status: 405 });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const contentLength = request.headers.get("content-length");
|
|
105
|
+
if (contentLength && Number(contentLength) > MAX_REQUEST_BODY_SIZE) {
|
|
106
|
+
return createJSONRPCErrorResponse(413, -32600, "Request body too large");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
validateContentType(request, JSON_CONTENT_TYPE);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const message = error instanceof VeryfrontError ? error.message : "Invalid Content-Type";
|
|
113
|
+
return createJSONRPCErrorResponse(400, -32700, message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let rpcRequest: JSONRPCRequest;
|
|
117
|
+
try {
|
|
118
|
+
const bodyText = await request.text();
|
|
119
|
+
if (bodyText.length > MAX_REQUEST_BODY_SIZE) {
|
|
120
|
+
return createJSONRPCErrorResponse(413, -32600, "Request body too large");
|
|
121
|
+
}
|
|
122
|
+
rpcRequest = JSON.parse(bodyText) as JSONRPCRequest;
|
|
123
|
+
} catch (_) {
|
|
124
|
+
return createJSONRPCErrorResponse(400, -32700, "Parse error");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const responseHeaders: Record<string, string> = {
|
|
128
|
+
...getCORSHeaders(requestOrigin),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (rpcRequest.method === "initialize") {
|
|
132
|
+
const context = extractRequestContext(request);
|
|
133
|
+
const rpcResponse = await handleRequest(rpcRequest, context);
|
|
134
|
+
const clientCaps =
|
|
135
|
+
((rpcRequest.params as Record<string, unknown> | undefined)?.capabilities ??
|
|
136
|
+
{}) as Record<string, unknown>;
|
|
137
|
+
const sessionId = sessionManager.create();
|
|
138
|
+
sessionCapabilities.set(sessionId, clientCaps);
|
|
139
|
+
responseHeaders["MCP-Session-Id"] = sessionId;
|
|
140
|
+
return createJSONResponse(rpcResponse, { headers: responseHeaders });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (sessionManager.size > 0) {
|
|
144
|
+
const sessionId = request.headers.get("MCP-Session-Id");
|
|
145
|
+
if (!sessionId) {
|
|
146
|
+
return createJSONRPCErrorResponse(400, -32600, "Missing MCP-Session-Id header");
|
|
147
|
+
}
|
|
148
|
+
if (!sessionManager.isValid(sessionId)) {
|
|
149
|
+
return createJSONRPCErrorResponse(404, -32600, "Session not found or expired");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (rpcRequest.id === undefined) {
|
|
154
|
+
const context = extractRequestContext(request);
|
|
155
|
+
await handleRequest(rpcRequest, context);
|
|
156
|
+
return new dntShim.Response(null, { status: 202, headers: responseHeaders });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const context = extractRequestContext(request);
|
|
160
|
+
const rpcResponse = await handleRequest(rpcRequest, context);
|
|
161
|
+
return createJSONResponse(rpcResponse, { headers: responseHeaders });
|
|
162
|
+
};
|
|
163
|
+
}
|