bcs-mcp 0.2.5

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,144 @@
1
+ import { BcsApiError } from "./client.js";
2
+ // Single reusable formatter (creating one per call is the costly part of toLocaleString)
3
+ const MSK_FMT = new Intl.DateTimeFormat("sv-SE", {
4
+ timeZone: "Europe/Moscow",
5
+ year: "numeric",
6
+ month: "2-digit",
7
+ day: "2-digit",
8
+ hour: "2-digit",
9
+ minute: "2-digit",
10
+ second: "2-digit",
11
+ hour12: false,
12
+ });
13
+ /**
14
+ * Date/string → ISO string in Moscow wall-time with the ACTUAL offset for that date
15
+ * (UTC+4/DST before 2014). null/undefined → ""; an unparseable string is returned
16
+ * AS IS (information is never lost — the convention every call site relies on).
17
+ */
18
+ export function toMsk(d) {
19
+ if (!d)
20
+ return "";
21
+ const date = typeof d === "string" ? new Date(d) : d;
22
+ if (isNaN(date.getTime()))
23
+ return typeof d === "string" ? d : "";
24
+ const wall = MSK_FMT.format(date).replace(" ", "T"); // "2026-06-12T13:58:22"
25
+ // offset = (wall interpreted as UTC) − actual instant; correct for any historical MSK offset
26
+ const offsetMin = Math.round((Date.parse(wall + "Z") - date.getTime()) / 60_000);
27
+ const sign = offsetMin < 0 ? "-" : "+";
28
+ const abs = Math.abs(offsetMin);
29
+ const hh = String(Math.floor(abs / 60)).padStart(2, "0");
30
+ const mm = String(abs % 60).padStart(2, "0");
31
+ return `${wall}${sign}${hh}:${mm}`;
32
+ }
33
+ /** ISO string → Date, with fallback for omitted params */
34
+ export function parseDate(s, fallback) {
35
+ if (!s)
36
+ return fallback;
37
+ const d = new Date(s);
38
+ if (isNaN(d.getTime())) {
39
+ throw new Error(`Invalid date: "${s}" (expected ISO 8601, e.g. 2026-01-31)`);
40
+ }
41
+ return d;
42
+ }
43
+ export function daysFromNow(days) {
44
+ return new Date(Date.now() + days * 86_400_000);
45
+ }
46
+ export function ok(data) {
47
+ return { content: [{ type: "text", text: JSON.stringify(data ?? null, null, 2) }] };
48
+ }
49
+ /** Hints for common HTTP statuses from the BCS Trade API */
50
+ const HTTP_HINTS = {
51
+ 400: "Bad request: check parameter values (ticker/classCode/dates).",
52
+ 401: "Unauthenticated: the refresh token is invalid or expired (BCS tokens live 90 days) — reissue it in the BCS web app (Профиль → Токены API).",
53
+ 403: "Permission denied: the token lacks the required scope (a read-only token cannot trade), or API trading is not enabled for this trade code — BCS enables it separately.",
54
+ 404: "Not found: check ticker/classCode/orderId (use find_instrument to verify instruments).",
55
+ 429: "Rate limit exceeded: wait a few seconds and retry.",
56
+ };
57
+ /**
58
+ * Error → ToolResult with an actionable hint. `extraHints` lets a tool override
59
+ * the hint for a specific status (e.g. a 404 that means "endpoint not rolled out").
60
+ */
61
+ export function fail(error, extraHints = {}) {
62
+ let msg = error instanceof Error ? error.message : String(error);
63
+ if (error instanceof BcsApiError) {
64
+ if (error.kind === "too-large") {
65
+ msg += " — the response exceeds the BCS backend's ~4 MB message cap (this is NOT a rate limit; retrying will not help) — narrow the time window (from/to) or request fewer records.";
66
+ }
67
+ else if (extraHints[error.status]) {
68
+ msg += ` — ${extraHints[error.status]}`;
69
+ }
70
+ else if (error.kind === "no-data" && error.status === 404) {
71
+ // The API wraps 400/403/422 in the same displayOptions JSON — the
72
+ // "no data right now" reading is only true for a 404
73
+ msg += " — the API reports no data for this request right now (e.g. outside the trading session); this is usually not a wrong parameter.";
74
+ }
75
+ else if (error.kind === "no-route" && error.status === 404) {
76
+ msg += " — the route does not exist on this service (the API may have moved the endpoint).";
77
+ }
78
+ else if (HTTP_HINTS[error.status]) {
79
+ msg += ` — ${HTTP_HINTS[error.status]}`;
80
+ }
81
+ }
82
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
83
+ }
84
+ /** Unwrap a possibly-enveloped list response: bare array or { <key>: [...] } */
85
+ export function pickList(res, key) {
86
+ if (Array.isArray(res))
87
+ return res;
88
+ const wrapped = res?.[key];
89
+ return Array.isArray(wrapped) ? wrapped : [];
90
+ }
91
+ /** Shallow copy without the listed keys */
92
+ export function omit(obj, keys) {
93
+ const out = { ...obj };
94
+ for (const k of keys)
95
+ delete out[k];
96
+ return out;
97
+ }
98
+ /**
99
+ * Map over items with bounded concurrency (caps parallel API fan-out on large batches).
100
+ * The first rejection aborts remaining work — no background calls keep firing
101
+ * after the tool has already returned an error.
102
+ */
103
+ export async function mapPool(items, limit, fn) {
104
+ const out = new Array(items.length);
105
+ let next = 0;
106
+ let aborted = false;
107
+ const worker = async () => {
108
+ for (let i = next++; i < items.length && !aborted; i = next++) {
109
+ try {
110
+ out[i] = await fn(items[i], i);
111
+ }
112
+ catch (e) {
113
+ aborted = true;
114
+ throw e;
115
+ }
116
+ }
117
+ };
118
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
119
+ return out;
120
+ }
121
+ /** Best-effort MCP progress notification (no-op when the client sent no progressToken) */
122
+ export async function notifyProgress(extra, progress, total, message) {
123
+ const progressToken = extra._meta?.progressToken;
124
+ if (progressToken == null)
125
+ return;
126
+ try {
127
+ await extra.sendNotification({
128
+ method: "notifications/progress",
129
+ params: { progressToken, progress, ...(total != null ? { total } : {}), message },
130
+ });
131
+ }
132
+ catch {
133
+ // progress is cosmetic — never fail the call
134
+ }
135
+ }
136
+ /** Split [from, to] into chunks of at most maxMs (for the 1000-bars-per-request candle limit) */
137
+ export function computeChunks(from, to, maxMs) {
138
+ const chunks = [];
139
+ for (let t = from.getTime(); t < to.getTime(); t += maxMs) {
140
+ chunks.push({ from: new Date(t), to: new Date(Math.min(t + maxMs, to.getTime())) });
141
+ }
142
+ return chunks;
143
+ }
144
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP server for BCS Trade API (БКС Мир инвестиций).
4
+ *
5
+ * Env:
6
+ * BCS_REFRESH_TOKEN — required, refresh token from the BCS web app (Профиль → Токены API)
7
+ * BCS_ALLOW_TRADING — "true" to register place_order/edit_order/cancel_order (needs a trade+read token)
8
+ * BCS_CONFIRM — "off" to disable the elicitation trade confirmation (not recommended: no sandbox at BCS)
9
+ * BCS_OUTPUT_DIR — root for outputPath file dumps (default: server cwd)
10
+ */
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { config } from "./client.js";
14
+ import { registerReadTools } from "./tools/read.js";
15
+ import { registerTradingTools } from "./tools/trading.js";
16
+ import { registerInfoTools } from "./tools/info.js";
17
+ import { registerPrompts } from "./prompts.js";
18
+ import { registerResources } from "./resources.js";
19
+ import { VERSION } from "./version.js";
20
+ if (!config.refreshToken) {
21
+ console.error("ERROR: BCS_REFRESH_TOKEN environment variable is required");
22
+ process.exit(1);
23
+ }
24
+ const server = new McpServer({ name: "bcs-mcp-server", version: VERSION });
25
+ registerReadTools(server);
26
+ registerInfoTools(server);
27
+ registerPrompts(server);
28
+ registerResources(server);
29
+ if (config.allowTrading)
30
+ registerTradingTools(server);
31
+ await server.connect(new StdioServerTransport());
32
+ console.error(`bcs-mcp: trading=${config.allowTrading ? "ENABLED (real account!)" : "disabled"}`);
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;IACzB,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAE3E,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,eAAe,CAAC,MAAM,CAAC,CAAC;AACxB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,IAAI,MAAM,CAAC,YAAY;IAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAEtD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACjD,OAAO,CAAC,KAAK,CAAC,oBAAoB,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC"}
@@ -0,0 +1,119 @@
1
+ import { API } from "./endpoints.js";
2
+ import { bcsFetch } from "./client.js";
3
+ import { pickList, mapPool } from "./helpers.js";
4
+ const TTL_MS = 24 * 60 * 60 * 1000;
5
+ const MAX_ENTRIES = 5000;
6
+ const BATCH_SIZE = 50; // /instruments/by-tickers batch limit
7
+ /** POST /instruments/by-tickers returns a bare array of instrument cards */
8
+ export async function fetchInstruments(tickers) {
9
+ const res = await bcsFetch(API.information, "/instruments/by-tickers", { method: "POST", body: { tickers } });
10
+ return pickList(res, "instruments");
11
+ }
12
+ function toRef(raw) {
13
+ // The same ticker can be listed on several exchanges/boards — prefer the
14
+ // instrument's primaryBoard on MOEX (GAZP has an SMAL small-lot board too)
15
+ const boards = raw.boards ?? [];
16
+ const board = boards.find((b) => b.exchange === "MOEX" && b.classCode === raw.primaryBoard) ??
17
+ boards.find((b) => b.exchange === "MOEX") ??
18
+ boards.find((b) => b.classCode === raw.primaryBoard) ??
19
+ boards[0] ??
20
+ {};
21
+ return {
22
+ ticker: raw.ticker ?? "",
23
+ name: raw.displayName ?? "",
24
+ classCode: board.classCode ?? "",
25
+ exchange: board.exchange ?? "",
26
+ instrumentType: raw.instrumentType ?? "",
27
+ lotSize: raw.lotSize ?? 1,
28
+ faceValue: raw.faceValue ?? null,
29
+ isin: raw.isin ?? "",
30
+ };
31
+ }
32
+ /**
33
+ * Preference among several cards of one instrument: MOEX cards first, then the
34
+ * card that itself carries its primaryBoard. GAZP comes as SMAL + TQBR + SPBRU
35
+ * cards with primaryBoard=TQBR on all of them — a plain "first MOEX" pick would
36
+ * land on the SMAL small-lot board.
37
+ */
38
+ export function pickPrimaryCard(cards) {
39
+ const read = (c) => c;
40
+ const moex = cards.filter((c) => read(c).boards?.some((b) => b.exchange === "MOEX"));
41
+ const pool = moex.length ? moex : cards;
42
+ return (pool.find((c) => {
43
+ const r = read(c);
44
+ return !!r.primaryBoard && !!r.boards?.some((b) => b.classCode === r.primaryBoard);
45
+ }) ?? pool[0]);
46
+ }
47
+ /** Several cards per ticker are possible (MOEX + SPB listings) — prefer the primary MOEX one */
48
+ function pickCard(cards, ticker) {
49
+ return pickPrimaryCard(cards.filter((i) => i.ticker?.toUpperCase() === ticker));
50
+ }
51
+ // Cache the in-flight promise (not the resolved value) so concurrent lookups of the
52
+ // same ticker share one request. Resolves to null for a genuine not-found; rejects on API error.
53
+ const cache = new Map();
54
+ function store(key, promise) {
55
+ promise.catch(() => cache.delete(key)); // failures don't stick — next call retries
56
+ // delete-then-set keeps Map insertion order meaningful: the FIRST key really is
57
+ // the least recently written one, so eviction below removes the right entry
58
+ cache.delete(key);
59
+ cache.set(key, { promise, at: Date.now() });
60
+ if (cache.size > MAX_ENTRIES) {
61
+ const oldest = cache.keys().next().value;
62
+ if (oldest !== undefined)
63
+ cache.delete(oldest);
64
+ }
65
+ }
66
+ async function load(ticker) {
67
+ const hit = pickCard(await fetchInstruments([ticker]), ticker);
68
+ return hit ? toRef(hit) : null;
69
+ }
70
+ function cached(ticker) {
71
+ const key = ticker.toUpperCase();
72
+ const hit = cache.get(key);
73
+ if (hit && Date.now() - hit.at < TTL_MS)
74
+ return hit.promise;
75
+ const promise = load(key);
76
+ store(key, promise);
77
+ return promise;
78
+ }
79
+ /** ticker → basic instrument info, memoized. Best-effort: returns null on not-found AND on API error. */
80
+ export async function getInstrumentRef(ticker) {
81
+ try {
82
+ return await cached(ticker);
83
+ }
84
+ catch {
85
+ return null; // enrichment is best-effort, never fail the parent tool
86
+ }
87
+ }
88
+ /** Resolve classCode for a ticker, using the explicit value when given */
89
+ export async function resolveClassCode(ticker, classCode) {
90
+ if (classCode)
91
+ return classCode;
92
+ const ref = await cached(ticker);
93
+ if (!ref?.classCode) {
94
+ throw new Error(`Cannot resolve classCode for "${ticker}" — pass classCode explicitly (e.g. TQBR for MOEX shares)`);
95
+ }
96
+ return ref.classCode;
97
+ }
98
+ /**
99
+ * Batch variant for tools that take many tickers: uncached tickers are fetched in
100
+ * chunks of ${BATCH_SIZE} via ONE by-tickers request per chunk (not N single requests),
101
+ * the cache is seeded, and per-index explicit classCodes are honored.
102
+ * Throws when any ticker cannot be resolved (same contract as resolveClassCode).
103
+ */
104
+ export async function resolveClassCodes(tickers, classCodes) {
105
+ const missing = tickers.filter((t, i) => !classCodes?.[i] && !cache.has(t.toUpperCase()));
106
+ const chunks = [];
107
+ for (let i = 0; i < missing.length; i += BATCH_SIZE)
108
+ chunks.push(missing.slice(i, i + BATCH_SIZE));
109
+ await mapPool(chunks, 2, async (chunk) => {
110
+ const cards = await fetchInstruments(chunk);
111
+ for (const t of chunk) {
112
+ const key = t.toUpperCase();
113
+ const card = pickCard(cards, key);
114
+ store(key, Promise.resolve(card ? toRef(card) : null));
115
+ }
116
+ });
117
+ return mapPool(tickers, 8, (t, i) => resolveClassCode(t, classCodes?.[i]));
118
+ }
119
+ //# sourceMappingURL=instruments-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instruments-cache.js","sourceRoot":"","sources":["../src/instruments-cache.ts"],"names":[],"mappings":"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"}
package/dist/output.js ADDED
@@ -0,0 +1,139 @@
1
+ import { mkdir, writeFile, realpath, lstat, open, unlink } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { ok } from "./helpers.js";
5
+ /** Inline responses larger than this get a hint to use outputPath */
6
+ const HINT_THRESHOLD = 25_000;
7
+ /** Shared schema fragment: every read tool accepts optional file output */
8
+ export const outputParams = {
9
+ outputPath: z
10
+ .string()
11
+ .optional()
12
+ .describe("Write the full result to this file (path relative to the output root: BCS_OUTPUT_DIR or server cwd) instead of returning it inline. The response becomes a short summary {savedTo, records, bytes, sample}. Use for bulk data to keep the context clean. For get_candles this also enables full-history fetching (chunking beyond the 1000-bar API limit)."),
13
+ outputFormat: z
14
+ .enum(["json", "csv"])
15
+ .optional()
16
+ .describe("File format; default json (or csv if outputPath ends with .csv). csv writes the main flat array of the response."),
17
+ };
18
+ export function outputRoot() {
19
+ return process.env.BCS_OUTPUT_DIR || process.cwd();
20
+ }
21
+ /** Resolve a user-supplied relative path strictly inside the output root (no .. / symlink escapes) */
22
+ async function resolveSafe(rel) {
23
+ const root = path.resolve(outputRoot());
24
+ const target = path.resolve(root, rel);
25
+ if (target !== root && !target.startsWith(root + path.sep)) {
26
+ throw new Error(`outputPath escapes the output root (${root}) — use a relative path inside it`);
27
+ }
28
+ await mkdir(path.dirname(target), { recursive: true });
29
+ const [realParent, realRoot] = await Promise.all([realpath(path.dirname(target)), realpath(root)]);
30
+ if (realParent !== realRoot && !realParent.startsWith(realRoot + path.sep)) {
31
+ throw new Error("outputPath escapes the output root via a symlink");
32
+ }
33
+ const finalPath = path.join(realParent, path.basename(target));
34
+ // Refuse to write THROUGH a symlink at the final component (it could point outside the root)
35
+ try {
36
+ const st = await lstat(finalPath);
37
+ if (st.isSymbolicLink()) {
38
+ throw new Error("outputPath refers to a symlink — refusing to write through it");
39
+ }
40
+ if (st.isDirectory()) {
41
+ throw new Error("outputPath points to a directory — include a file name (e.g. data/report.json)");
42
+ }
43
+ }
44
+ catch (e) {
45
+ if (e.code !== "ENOENT")
46
+ throw e; // non-existent target is fine
47
+ }
48
+ return finalPath;
49
+ }
50
+ export function toCsv(rows) {
51
+ if (!rows.length)
52
+ return "";
53
+ // Union of keys across ALL rows — heterogeneous rows (e.g. found/not-found) keep every column
54
+ const cols = [];
55
+ const seen = new Set();
56
+ for (const row of rows) {
57
+ for (const k of Object.keys(row)) {
58
+ if (!seen.has(k)) {
59
+ seen.add(k);
60
+ cols.push(k);
61
+ }
62
+ }
63
+ }
64
+ const esc = (v) => {
65
+ if (v == null)
66
+ return "";
67
+ const s = typeof v === "object" ? JSON.stringify(v) : String(v);
68
+ return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
69
+ };
70
+ return [cols.join(","), ...rows.map((r) => cols.map((c) => esc(r[c])).join(","))].join("\n");
71
+ }
72
+ /** Write raw text into the output root */
73
+ export async function writeRaw(rel, body) {
74
+ const file = await resolveSafe(rel);
75
+ await writeFile(file, body, "utf8");
76
+ return { savedTo: file, bytes: Buffer.byteLength(body, "utf8") };
77
+ }
78
+ /**
79
+ * Streaming writer for large outputs: write chunks incrementally so peak memory stays small.
80
+ * close() returns total bytes; remove() deletes the file (e.g. on rollback / empty result).
81
+ */
82
+ export async function createSafeWriter(rel) {
83
+ const file = await resolveSafe(rel);
84
+ const fh = await open(file, "w");
85
+ let bytes = 0;
86
+ let closed = false;
87
+ return {
88
+ savedTo: file,
89
+ write: async (chunk) => {
90
+ const buf = Buffer.from(chunk, "utf8");
91
+ await fh.write(buf);
92
+ bytes += buf.length;
93
+ },
94
+ close: async () => {
95
+ if (!closed) {
96
+ closed = true;
97
+ await fh.close();
98
+ }
99
+ return bytes;
100
+ },
101
+ remove: async () => {
102
+ await unlink(file).catch(() => { });
103
+ },
104
+ };
105
+ }
106
+ /**
107
+ * Deliver a tool result: inline (with a size hint) or to a file with a compact summary.
108
+ * `rows` is the flat array used for csv and record counting; null = csv unsupported.
109
+ */
110
+ export async function deliver(data, rows, out, extras = {}) {
111
+ if (!out.outputPath) {
112
+ const res = ok(data);
113
+ if (res.content[0].text.length > HINT_THRESHOLD) {
114
+ // separate content item — keeps content[0].text valid JSON for programmatic consumers
115
+ res.content.push({
116
+ type: "text",
117
+ text: "NOTE: large response — pass outputPath to write it to a file and keep the context clean.",
118
+ });
119
+ }
120
+ return res;
121
+ }
122
+ // Nested responses (rows === null) default to json even for *.csv paths — but an
123
+ // EXPLICIT csv request still fails loudly below instead of silently writing JSON.
124
+ const format = out.outputFormat ?? (rows && out.outputPath.endsWith(".csv") ? "csv" : "json");
125
+ if (format === "csv" && !rows) {
126
+ throw new Error("csv is not supported for this tool's nested response — use outputFormat: json");
127
+ }
128
+ // compact JSON for files (pretty-print can double memory / hit V8's string length cap on big data)
129
+ const body = format === "csv" ? toCsv(rows) + "\n" : JSON.stringify(data) + "\n";
130
+ const { savedTo, bytes } = await writeRaw(out.outputPath, body);
131
+ return ok({
132
+ savedTo,
133
+ format,
134
+ ...(rows ? { records: rows.length, sample: rows.slice(0, 2) } : {}),
135
+ bytes,
136
+ ...extras,
137
+ });
138
+ }
139
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}