@yanlinglabs/winter-agent-runtime 0.0.35 → 0.0.38

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.
@@ -0,0 +1,17 @@
1
+ import type { ToolResultSiteIcon } from "../registry.js";
2
+ export declare const SITE_ICONS_MAX = 10;
3
+ export declare const SITE_ICON_URL_MAX_LENGTH = 2048;
4
+ /** A url a host may be handed: https, parseable, no credentials, re-serialised, within the cap. */
5
+ export declare function siteIconHttpsUrl(value: unknown): string | undefined;
6
+ /** A clean list: both urls of each entry checked, one entry per page url (first wins), capped. */
7
+ export declare function collectSiteIcons(entries: Iterable<{
8
+ url: unknown;
9
+ iconUrl: unknown;
10
+ }>): ToolResultSiteIcon[] | undefined;
11
+ /**
12
+ * The icon `html` declares for the page at `pageUrl`, resolved against it: `apple-touch-icon` (large
13
+ * and opaque) over `icon` / `shortcut icon`, never a `mask-icon` (a monochrome stencil), an SVG (most
14
+ * hosts' image decoders cannot draw one) or a `data:` url. With none declared, the page origin's own
15
+ * `/favicon.ico`. Undefined when the page itself is not https (nothing is handed out for it).
16
+ */
17
+ export declare function pageIconUrl(html: string, pageUrl: string): string | undefined;
@@ -9,6 +9,8 @@ export interface WebFetchCacheEntry {
9
9
  contentType: string;
10
10
  /** The URL actually fetched (after the https upgrade and any auto-followed same-host redirect) -- may differ from the cache key. */
11
11
  finalUrl: string;
12
+ /** The page's icon url (`_site-icons.ts`'s `pageIconUrl`), kept so a cache HIT still reports it to the host. */
13
+ iconUrl?: string;
12
14
  }
13
15
  export declare const WEB_FETCH_CACHE_TTL_MS: number;
14
16
  export declare const WEB_FETCH_CACHE_MAX_BYTES: number;
@@ -34,4 +34,5 @@ import "./skill.ts";
34
34
  import "./workflow.ts";
35
35
  import "./web-fetch.ts";
36
36
  import "./web-search.ts";
37
+ import "./search.ts";
37
38
  export declare const EXECUTORS_WIRED = true;
@@ -34,6 +34,8 @@ declare function buildMonitorRunCommandOptions(ctx: ToolExecutionContext): {
34
34
  home: string;
35
35
  /** Phase 5 fix wave, I1: the resolved winter root, distinct from the OS home above. */
36
36
  winterHome?: string;
37
+ /** WS-21 fix round 1, item 4: the shared store home -- see `ToolExecutionContext.storeHome`. */
38
+ storeHome?: string;
37
39
  /** P7a (D19): the session's brand -- the dot-dir names the seatbelt fences. */
38
40
  brand?: SandboxBrand;
39
41
  /** Fix round 16, item 2: `sandbox.filesystem.allowGitConfig` -- see `SandboxFilesystemSettings.allowGitConfig`'s own header. */
@@ -1,44 +1,3 @@
1
1
  import "../descriptors/index.ts";
2
- export type ReadBlock = {
3
- type: "text";
4
- text: string;
5
- } | {
6
- type: "image";
7
- media_type: string;
8
- bytes: number;
9
- width?: number;
10
- height?: number;
11
- data?: string;
12
- note?: string;
13
- } | {
14
- type: "pdf";
15
- media_type: "application/pdf";
16
- bytes: number;
17
- totalPages?: number;
18
- requestedPages?: {
19
- start: number;
20
- end: number;
21
- };
22
- data?: string;
23
- note: string;
24
- };
25
- export interface ReadBlocksEnvelope {
26
- winterReadBlocks: ReadBlock[];
27
- }
28
- export declare function parsePngDimensions(buf: Uint8Array): {
29
- width: number;
30
- height: number;
31
- } | undefined;
32
- export declare function parseGifDimensions(buf: Uint8Array): {
33
- width: number;
34
- height: number;
35
- } | undefined;
36
- export declare function parseBmpDimensions(buf: Uint8Array): {
37
- width: number;
38
- height: number;
39
- } | undefined;
40
- export declare function parseJpegDimensions(buf: Uint8Array): {
41
- width: number;
42
- height: number;
43
- } | undefined;
2
+ export { IMAGE_MAX_LONG_EDGE, MODEL_DOES_NOT_SUPPORT_IMAGES, READ_IMAGE_MAX_BYTES, READ_IMAGE_MAX_DIMENSION, parseBmpDimensions, parseGifDimensions, parseJpegDimensions, parsePngDimensions, parseWebpDimensions, sniffImageType, } from "../image-prep.js";
44
3
  export declare function countPdfPages(bytes: Buffer): number | undefined;
@@ -0,0 +1,25 @@
1
+ import "../descriptors/search.ts";
2
+ import { type ToolExecutor } from "../registry.js";
3
+ /** `/answer` SYNTHESIZES -- it runs a search and then writes a grounded answer over the results -- so it
4
+ * is materially slower than a `/search` round trip; the daemon's 45 s, kept. */
5
+ export declare const SEARCH_REQUEST_TIMEOUT_MS = 45000;
6
+ /** The synthesized answer itself. */
7
+ export declare const SEARCH_ANSWER_CHARS = 24000;
8
+ /** Rendered as a sources list; the backend decides how many it used. */
9
+ export declare const SEARCH_MAX_CITATIONS = 20;
10
+ /** The whole response: the model's ENTIRE view of the search (a chat has no page-reading tool). */
11
+ export declare const SEARCH_TOTAL_OUTPUT_CHARS = 30000;
12
+ export declare const EXA_ANSWER_URL = "https://api.exa.ai/answer";
13
+ /** One sentence per documented failure, each naming what clears it -- never the response body, which can
14
+ * echo request headers (and so the key) and is third-party text besides. */
15
+ export declare function searchStatusMessage(status: number): string;
16
+ export declare const SEARCH_NO_KEY_MESSAGE = "Search needs an Exa API key (from exa.ai), and none is configured for this session";
17
+ export interface SearchExecutorDeps {
18
+ /** Test seam: defaults to the global `fetch`. Tests point it at a loopback fixture. */
19
+ fetchFn?: typeof fetch;
20
+ /** Test seam: defaults to `EXA_ANSWER_URL`. */
21
+ endpoint?: string;
22
+ /** Test seam: defaults to `SEARCH_REQUEST_TIMEOUT_MS`. */
23
+ timeoutMs?: number;
24
+ }
25
+ export declare function createSearchExecutor(deps?: SearchExecutorDeps): ToolExecutor;
@@ -40,7 +40,11 @@ export interface AvailabilityPredicate {
40
40
  requiresToolSearchEnabled?: true;
41
41
  hiddenWhenFamilyTaskNative?: true;
42
42
  insideSubagent?: false;
43
+ optIn?: true;
44
+ requiresAnyCapability?: readonly string[];
43
45
  }
46
+ /** Derived by the engine when the host's `deferTools` names anything: a built-in may then be deferred. */
47
+ export declare const DEFERRED_BUILTINS_CAPABILITY = "winter.deferred-builtins";
44
48
  export type ToolDisposition = "implement-now" | "implement-later" | "correctly-absent" | "winter-backed-equivalent" | "winter-backed-later";
45
49
  export interface ToolDescriptor {
46
50
  canonicalName: string;
@@ -61,11 +65,51 @@ export interface ToolDescriptor {
61
65
  alwaysLoad?: boolean;
62
66
  _meta?: Record<string, unknown>;
63
67
  interaction?: "required";
68
+ mcpName?: string;
64
69
  }
65
70
  export interface ToolResultPayload {
66
71
  output: string;
72
+ /**
73
+ * Code-mode images: what the MODEL is shown for this call, when it is more than text. Present only on
74
+ * a result that carries an image (the Read tool on an image file or a notebook with plot output);
75
+ * the engine then writes these blocks as the `tool_result`'s content array -- claude's own shape --
76
+ * in place of `output`. `output` stays a short TEXT rendering of the same result, and it is what every
77
+ * text-only consumer reads: the PostToolUse hook's `tool_response`, a log line, a flattening. Kept
78
+ * separate so no text channel ever carries base64 (a hook payload rides the same capped stdout line
79
+ * a frame does).
80
+ */
81
+ blocks?: ToolResultBlock[];
67
82
  isError?: boolean;
83
+ /**
84
+ * HOST-ONLY metadata, never model-visible: the sites this result names and the icon the tool knows
85
+ * for each (`WebFetch`: the page's own declared icon; `WebSearch`: Exa's `favicon`). The engine
86
+ * writes it ONLY onto the host-facing frame's `tool_result` block, as `winter_site_icons:
87
+ * [{url, icon_url}]` -- never into the model-facing content, the history, the transcript or a
88
+ * provider request. Built through `tools/impl/_site-icons.ts`'s `collectSiteIcons` (bounded, https).
89
+ */
90
+ siteIcons?: ToolResultSiteIcon[];
91
+ }
92
+ /** One `ToolResultPayload.siteIcons` entry: a page url the result names and its icon url. */
93
+ export interface ToolResultSiteIcon {
94
+ url: string;
95
+ iconUrl: string;
68
96
  }
97
+ /**
98
+ * One block of a multimodal tool result (see `ToolResultPayload.blocks`). Structurally the engine's own
99
+ * `text` and `image` ContentBlock variants, so the engine carries them into history, the transcript and
100
+ * every provider adapter unchanged.
101
+ */
102
+ export type ToolResultBlock = {
103
+ type: "text";
104
+ text: string;
105
+ } | {
106
+ type: "image";
107
+ source: {
108
+ type: "base64";
109
+ media_type: string;
110
+ data: string;
111
+ };
112
+ };
69
113
  /** See `ToolExecutionContext.permission`. */
70
114
  export interface ToolCallPermission {
71
115
  explicitApproval: "prompt" | "rule";
@@ -132,6 +176,13 @@ export interface ToolExecutionContext {
132
176
  * ABSENT READS AS `WINTER_BRAND`, which is byte-identical to the behaviour before this field.
133
177
  */
134
178
  brand?: BrandProfile;
179
+ /**
180
+ * Code-mode images: whether the model the session is on RIGHT NOW accepts image input (its catalog
181
+ * row's `inputModalities` lists "image"). The Read tool answers an image file with a short text
182
+ * refusal instead of image blocks when this is `false`. Absent reads as "yes" -- a scripted double or
183
+ * a row with no modality facts, the same default `ModelDescription.readsImages` has.
184
+ */
185
+ modelReadsImages?: boolean;
135
186
  sessionId: string;
136
187
  readState: SessionReadState;
137
188
  emitFrame: (frame: BackgroundTaskMessage) => void;
@@ -282,6 +333,20 @@ export interface McpToolDefinition {
282
333
  _meta?: Record<string, unknown>;
283
334
  }
284
335
  export declare function onRegistryChange(cb: () => void): () => void;
336
+ /**
337
+ * A HOST'S PLAIN NAMES for some of one in-process server's tools (`McpSdkServerConfig.toolNames`):
338
+ * `{ <tool as the server lists it>: <plain name> }`. See that field's doc for the contract.
339
+ */
340
+ export type McpServerToolNames = Readonly<Record<string, string>>;
341
+ /** The search tool's name (descriptors/tool-search.ts): never deferred, and its absence from `tools` switches deferral off. */
342
+ export declare const TOOL_SEARCH_TOOL_NAME = "ToolSearch";
343
+ /** The `mcp__<server>__<tool>` spelling a plain-named MCP tool keeps as its equivalent identity, or `undefined`. */
344
+ export declare function mcpSpellingOf(name: string): string | undefined;
345
+ /**
346
+ * The registry name of `mcp__<server>__<tool>`: its plain name when the host renamed it, else the name itself.
347
+ * The inverse of `mcpSpellingOf`, for a reader handed the MCP spelling (a permission rule, a hook matcher).
348
+ */
349
+ export declare function registeredNameForMcpSpelling(name: string): string;
285
350
  export declare function rebrandStandingServerTools(renames: ReadonlyArray<{
286
351
  from: string;
287
352
  to: string;
@@ -289,6 +354,7 @@ export declare function rebrandStandingServerTools(renames: ReadonlyArray<{
289
354
  export declare function registerMcpServerTools(server: string, incomingTools: readonly McpToolDefinition[], opts: {
290
355
  alwaysLoad?: boolean;
291
356
  deferredDefault: boolean | readonly PermissionMode[];
357
+ toolNames?: McpServerToolNames;
292
358
  }): void;
293
359
  /**
294
360
  * Fix round 20: the live MCP server that registered `canonicalName` through `registerMcpServerTools`,
@@ -321,6 +387,14 @@ export interface DeferralActivation {
321
387
  };
322
388
  providerSupportsToolSearch: boolean;
323
389
  deferrableContextShare: number;
390
+ /**
391
+ * The host's `deferTools` (`RuntimeConfig.deferTools`): names that START DEFERRED while deferral is
392
+ * active -- the one way a BUILT-IN defers (claude's own per-tool `shouldDefer`, chosen by the host here).
393
+ * `ToolSearch` is never deferred whatever this says, and an MCP tool's `alwaysLoad` still wins. Lives
394
+ * on the activation because every reader of `resolveDeferral` (the partition, the load-first boundary,
395
+ * ToolSearch's pool) already takes it, so none of them can disagree about it.
396
+ */
397
+ deferTools?: readonly string[];
324
398
  }
325
399
  export declare function isDeferralActive(activation: DeferralActivation): boolean;
326
400
  export declare function resolveDeferral(descriptor: ToolDescriptor, mode: PermissionMode, activation: DeferralActivation): "eager" | "deferred" | "hidden";
@@ -359,6 +433,21 @@ export declare function deriveRuntimeCapabilities(facts: SessionCapabilityFacts)
359
433
  export declare function resolveSessionCapabilities(hostSupplied: readonly string[] | undefined, facts: SessionCapabilityFacts): string[];
360
434
  export declare function isBareDenied(canonicalName: string, disallowedTools: readonly string[] | undefined): boolean;
361
435
  export declare function buildAdvertisedSet(cfg: AdvertisedSetInputs): ToolDescriptor[];
436
+ /**
437
+ * `cfg.tools` (claude's own `tools` option -- `RuntimeConfig.tools`): it names the BUILT-IN set, so it
438
+ * filters built-ins only. An MCP server's tools (a plain-named in-process one included), the standing
439
+ * server's twins and host-generated tools are never named there and never removed by it -- the twins
440
+ * follow their native spelling through the alias pass instead (`hideAliasExcludedTwins`: a native the
441
+ * list leaves out is not advertised, so its twin is hidden). An OPT-IN built-in (`availability.optIn`)
442
+ * needs the list to name it; absent, the list is claude's default set, which carries none.
443
+ */
444
+ export declare function isRequestedTool(descriptor: ToolDescriptor, tools: readonly string[] | undefined): boolean;
445
+ /**
446
+ * A bare deny on the descriptor's own name OR on the `mcp__<server>__<tool>` spelling a plain-named
447
+ * in-process tool keeps (`ToolDescriptor.mcpName`): either spelling hides it, from advertisement and the
448
+ * ToolSearch pool alike (both read `buildAdvertisedSet`).
449
+ */
450
+ export declare function isDescriptorBareDenied(descriptor: ToolDescriptor, disallowedTools: readonly string[] | undefined): boolean;
362
451
  export interface EngineToolCall {
363
452
  id: string;
364
453
  name: string;
@@ -366,6 +455,8 @@ export interface EngineToolCall {
366
455
  }
367
456
  export interface EngineToolResult {
368
457
  output: string;
458
+ /** See `ToolResultPayload.blocks`: when present, the model-facing content of the `tool_result`. */
459
+ blocks?: ToolResultBlock[];
369
460
  /**
370
461
  * Spawn-surface parity (R-S4, research gap 6): the executor's own `isError`, carried to the engine
371
462
  * so the model-facing `tool_result` block says `is_error: true` -- claude's shape for every tool
@@ -373,6 +464,8 @@ export interface EngineToolResult {
373
464
  * success.
374
465
  */
375
466
  isError?: boolean;
467
+ /** See `ToolResultPayload.siteIcons`: host-only, written onto the host-facing frame and nowhere else. */
468
+ siteIcons?: ToolResultSiteIcon[];
376
469
  }
377
470
  export interface EngineFacingToolExecutor {
378
471
  /** Phase 6 Task 3 (R6-6): `opts.signal` is the engine's per-turn abort. It reaches a real executor as `ToolExecutionContext.signal`. */
@@ -381,6 +474,8 @@ export interface EngineFacingToolExecutor {
381
474
  export interface RegistryToolExecutorDeps {
382
475
  sessionId: string;
383
476
  home: string;
477
+ /** Code-mode images: read per call, so a `set_model` since the last call is seen -- see `ToolExecutionContext.modelReadsImages`. */
478
+ modelReadsImages?: () => boolean;
384
479
  /** Phase 5 fix wave, I1: the resolved winter root -- see `ToolExecutionContext.winterHome`. */
385
480
  winterHome?: string;
386
481
  /** WS-21 fix round 1, item 4: the shared store home -- see `ToolExecutionContext.storeHome`. */
@@ -0,0 +1,41 @@
1
+ import type { ToolExecutionContext } from "./registry.js";
2
+ import { type GlobDenyEntry } from "../permissions/file-rules.js";
3
+ import type { SandboxBrand } from "../sandbox/profile.js";
4
+ /** Everything about the SESSION the seatbelt profile needs, for any tool that runs a shell command. */
5
+ export interface SandboxRunInputs {
6
+ cwd: string;
7
+ env: NodeJS.ProcessEnv;
8
+ settings: ToolExecutionContext["sandboxSettings"];
9
+ writableRoots: string[];
10
+ denyWritePaths?: string[];
11
+ denyReadPaths?: string[];
12
+ /** Fix round 11: glob-shaped denyWrite/denyRead entries, pre-converted to SBPL regex source -- see `splitDenyPathsByGlobShape`'s own header. */
13
+ denyWriteRegexes?: string[];
14
+ denyReadRegexes?: string[];
15
+ /** Fix round 12: each glob-shaped denyWrite/denyRead entry's own canonicalized fixed-prefix directory -- feeds the ancestor-rename-bypass fix. */
16
+ denyWriteGlobFixedPrefixes?: string[];
17
+ denyReadGlobFixedPrefixes?: string[];
18
+ /** Fix round 13: each glob-shaped denyRead entry, its regex PAIRED with its own fixed prefix -- feeds the read-deny-keep-in-place fix. */
19
+ denyReadGlobEntries?: GlobDenyEntry[];
20
+ home: string;
21
+ /** Phase 5 fix wave, I1: the resolved winter root, distinct from the OS home above. */
22
+ winterHome?: string;
23
+ /** WS-21 fix round 1, item 4: the shared store home -- see `ToolExecutionContext.storeHome`. */
24
+ storeHome?: string;
25
+ /** P7a (D19): the session's brand -- the dot-dir names the seatbelt fences. */
26
+ brand?: SandboxBrand;
27
+ /** Fix round 16, item 2: `sandbox.filesystem.allowGitConfig` -- see `SandboxFilesystemSettings.allowGitConfig`'s own header. */
28
+ allowGitConfigWrites?: boolean;
29
+ }
30
+ /**
31
+ * Where a spawn may write: the session scratch dir, the session's bounded roots (the cwd among them),
32
+ * the outputs dir when configured, and `filesystem.allowWrite` (WS-12 §12 Q5: additive, never a
33
+ * replacement).
34
+ */
35
+ export declare function computeWritableRoots(ctx: ToolExecutionContext): string[];
36
+ /** The child's environment: this process's, with `TMPDIR` (and `OUTDIR` when configured) pointed at the session's own dirs. */
37
+ export declare function buildSandboxChildEnv(ctx: ToolExecutionContext): NodeJS.ProcessEnv;
38
+ /** `filesystem.denyWrite`/`denyRead`, split into plain paths and glob-shaped regexes (C1 / fix rounds 11-13). */
39
+ export declare function computeDenyPaths(ctx: ToolExecutionContext): Pick<SandboxRunInputs, "denyWritePaths" | "denyReadPaths" | "denyWriteRegexes" | "denyReadRegexes" | "denyWriteGlobFixedPrefixes" | "denyReadGlobFixedPrefixes" | "denyReadGlobEntries">;
40
+ /** The session's sandbox inputs, identical for every tool that runs a shell command. */
41
+ export declare function sandboxRunInputs(ctx: ToolExecutionContext): SandboxRunInputs;
@@ -6,6 +6,8 @@ export interface ExposureQuery {
6
6
  platform?: NodeJS.Platform;
7
7
  capabilities?: readonly string[];
8
8
  disallowedTools?: readonly string[];
9
+ /** The host's `tools` (claude's option, `RuntimeConfig.tools`): the built-in set -- the same filter `init.tools` applies. */
10
+ tools?: readonly string[];
9
11
  insideSubagent?: boolean;
10
12
  familyMetadata?: {
11
13
  taskNative?: boolean;
@@ -1,5 +1,5 @@
1
1
  import type { PermissionMode } from "@yanlinglabs/winter-agent-sdk";
2
- import type { DeferralActivation, ToolDescriptor } from "../tools/registry.js";
2
+ import { type DeferralActivation, type ToolDescriptor } from "../tools/registry.js";
3
3
  import type { McpServerStateSource } from "../mcp/state.js";
4
4
  export interface FailedMcpServerEntry {
5
5
  name: string;
@@ -26,6 +26,10 @@ export interface ToolSearchDeps {
26
26
  platform?: NodeJS.Platform;
27
27
  capabilities?: readonly string[];
28
28
  disallowedTools?: readonly string[];
29
+ /** The host's `tools` (`RuntimeConfig.tools`): the built-in set, filtered exactly as `init.tools` is. */
30
+ tools?: readonly string[];
31
+ /** The engine's `currentToolName`: an old name (a renamed tool's MCP spelling, a host `legacyToolNames` entry) to the tool it means now. Default: the MCP-spelling half alone. */
32
+ resolveName?: (name: string) => string;
29
33
  insideSubagent?: boolean;
30
34
  familyMetadata?: {
31
35
  taskNative?: boolean;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const RUNTIME_VERSION = "0.0.35";
1
+ export declare const RUNTIME_VERSION = "0.0.38";
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var RUNTIME_VERSION = "0.0.35";
2
+ var RUNTIME_VERSION = "0.0.38";
3
3
  export {
4
4
  RUNTIME_VERSION
5
5
  };
@@ -74,6 +74,14 @@ export declare function inheritedWebSessionFacts(sessionId: string): Pick<WebSes
74
74
  export declare function resetWebSessionRuntimesForTest(): void;
75
75
  /** `winter.search-backend`'s session fact: the host has not switched the backend off. */
76
76
  export declare function searchBackendUsable(runtime: Pick<WebSessionRuntime, "web">): boolean;
77
+ /**
78
+ * `winter.search-answer`'s session fact (the `Search` built-in): the backend is not switched off AND a
79
+ * key is NAMED (`web.search.authRef`) and there is a resolver to read it through. `/answer` has no
80
+ * anonymous tier, so a session without a key must never be offered a tool whose every call fails --
81
+ * whether the named key actually HOLDS material is only knowable asynchronously, and a missing one is a
82
+ * typed, actionable result at the first call.
83
+ */
84
+ export declare function searchAnswerUsable(runtime: Pick<WebSessionRuntime, "web" | "resolveToolSecret">): boolean;
77
85
  /**
78
86
  * `winter.fetch-extractor`'s session fact: a digest model resolves.
79
87
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanlinglabs/winter-agent-runtime",
3
- "version": "0.0.35",
3
+ "version": "0.0.38",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -73,9 +73,9 @@
73
73
  "@modelcontextprotocol/client": "^2.1.0",
74
74
  "ajv": "^8.20.0",
75
75
  "ignore": "7.0.5",
76
- "@yanlinglabs/winter-agent-sdk": "0.0.35",
77
- "@yanlinglabs/winter-provider-catalog": "0.0.35",
78
- "@yanlinglabs/winter-provider-runtime": "0.0.35"
76
+ "@yanlinglabs/winter-agent-sdk": "0.0.38",
77
+ "@yanlinglabs/winter-provider-runtime": "0.0.38",
78
+ "@yanlinglabs/winter-provider-catalog": "0.0.38"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@modelcontextprotocol/server": "^2.1.0"