@prefecthq/fastmcp-ts 0.0.6 β 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -14
- package/dist/cli/entrypoint-runtime.cjs +87 -0
- package/dist/cli/entrypoint-runtime.cjs.map +1 -0
- package/dist/cli/index.cjs +18832 -18235
- package/dist/cli/index.cjs.map +1 -1
- package/dist/client.d.ts +568 -45
- package/dist/client.js +740 -182
- package/dist/client.js.map +1 -1
- package/dist/server.d.ts +419 -30
- package/dist/server.js +762 -321
- package/dist/server.js.map +1 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
The TypeScript framework for building [Model Context Protocol](https://modelcontextprotocol.io) servers, clients, and apps. The official TypeScript counterpart to [FastMCP for Python](https://github.com/PrefectHQ/fastmcp) - built and maintained with π by the same team at [Prefect](https://prefect.io).
|
|
4
4
|
|
|
5
|
-
Built on the official [
|
|
5
|
+
Built on version 2 of the official [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) β the scoped `@modelcontextprotocol/server` and `@modelcontextprotocol/client` packages. FastMCP handles the protocol plumbing so you can focus on what your server actually does.
|
|
6
|
+
|
|
7
|
+
FastMCP 1.0 speaks both MCP protocol generations: the 2025 legacy era and the 2026-07-28 modern era. A server serves both at once, and a client negotiates one. Upgrading from 0.x keeps your existing code running β the client defaults to the legacy era until you opt into the modern one. The [migration guide](docs/migration.mdx) covers the upgrade path.
|
|
6
8
|
|
|
7
9
|
## Installation
|
|
8
10
|
|
|
@@ -58,6 +60,13 @@ await server.run() // stdio (default)
|
|
|
58
60
|
// await server.run({ transport: 'http', port: 3000 })
|
|
59
61
|
```
|
|
60
62
|
|
|
63
|
+
Save this file as `server.ts`. Installing `@prefecthq/fastmcp-ts` also installs the `fastmcp` CLI. Use it to inspect the server and call a tool, with no build step:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx fastmcp inspect --file server.ts
|
|
67
|
+
npx fastmcp call add --file server.ts a=1 b=2
|
|
68
|
+
```
|
|
69
|
+
|
|
61
70
|
### Context
|
|
62
71
|
|
|
63
72
|
Handlers access logging, progress, LLM sampling, user elicitation, and per-session state through an ambient context with no prop-drilling.
|
|
@@ -85,7 +94,7 @@ server.tool(
|
|
|
85
94
|
maxTokens: 512,
|
|
86
95
|
})
|
|
87
96
|
|
|
88
|
-
return content.text
|
|
97
|
+
return content.type === 'text' ? content.text : ''
|
|
89
98
|
}
|
|
90
99
|
)
|
|
91
100
|
```
|
|
@@ -121,7 +130,7 @@ const weather = new FastMCP({ name: 'weather' })
|
|
|
121
130
|
weather.tool({ name: 'forecast', description: 'Get a forecast', input: z.object({ city: z.string() }) }, ({ city }) => `Forecast for ${city}`)
|
|
122
131
|
|
|
123
132
|
// Wrap a remote server as a mountable instance
|
|
124
|
-
const maps = await createProxy({
|
|
133
|
+
const maps = await createProxy({ type: 'http', url: 'http://maps-service/mcp' })
|
|
125
134
|
|
|
126
135
|
const gateway = new FastMCP({ name: 'gateway' })
|
|
127
136
|
gateway.mount(weather, 'weather') // β weather_forecast
|
|
@@ -168,7 +177,7 @@ import Anthropic from '@anthropic-ai/sdk'
|
|
|
168
177
|
|
|
169
178
|
const client = await Client.connect('http://localhost:3000', {
|
|
170
179
|
handlers: {
|
|
171
|
-
sampling:
|
|
180
|
+
sampling: new AnthropicSamplingAdapter(new Anthropic()).asHandler(),
|
|
172
181
|
},
|
|
173
182
|
})
|
|
174
183
|
```
|
|
@@ -201,7 +210,6 @@ FastMCP ships a server-side component library for building interactive UIs rende
|
|
|
201
210
|
|
|
202
211
|
```typescript
|
|
203
212
|
import { FastMCPApp, Column, Row, Text, Input, Button, Table } from '@prefecthq/fastmcp-ts/server'
|
|
204
|
-
import { z } from 'zod'
|
|
205
213
|
|
|
206
214
|
const app = new FastMCPApp({ name: 'search-app', version: '1.0.0' })
|
|
207
215
|
|
|
@@ -213,19 +221,15 @@ app.entrypoint(
|
|
|
213
221
|
Text('Product Search'),
|
|
214
222
|
Row({}, [
|
|
215
223
|
Input({ name: 'query', placeholder: 'Search productsβ¦' }),
|
|
216
|
-
Button({ label: 'Search',
|
|
224
|
+
Button({ label: 'Search', action: app.toolRef('run_search') }),
|
|
217
225
|
]),
|
|
218
226
|
])
|
|
219
227
|
)
|
|
220
228
|
|
|
221
229
|
// Backend tool: hidden from the LLM, callable only from within the rendered UI
|
|
222
230
|
app.backendTool(
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
description: 'Execute the search query',
|
|
226
|
-
input: z.object({ query: z.string() }),
|
|
227
|
-
},
|
|
228
|
-
async ({ query }) => {
|
|
231
|
+
{ name: 'run_search', description: 'Execute the search query' },
|
|
232
|
+
async ({ query }: { query: string }) => {
|
|
229
233
|
const rows = await db.search(query)
|
|
230
234
|
return Table({ columns: ['Name', 'Price', 'Stock'], rows })
|
|
231
235
|
}
|
|
@@ -247,7 +251,15 @@ const server = new FastMCP({ name: 'my-server' })
|
|
|
247
251
|
server.addProvider(new Approval()) // confirm/deny card injected back into the conversation
|
|
248
252
|
server.addProvider(new Choice()) // clickable option list
|
|
249
253
|
server.addProvider(new FileUpload()) // drag-and-drop file picker; file bytes never pass through the LLM
|
|
250
|
-
|
|
254
|
+
|
|
255
|
+
// Auto-generated, validated form from any Standard Schema
|
|
256
|
+
server.addProvider(
|
|
257
|
+
new FormInput({
|
|
258
|
+
name: 'contact_form',
|
|
259
|
+
description: 'Contact form',
|
|
260
|
+
schema: z.object({ name: z.string(), email: z.string() }),
|
|
261
|
+
}),
|
|
262
|
+
)
|
|
251
263
|
```
|
|
252
264
|
|
|
253
265
|
### Generative UI
|
|
@@ -277,6 +289,8 @@ fastmcp run server.ts --transport http --port 3000
|
|
|
277
289
|
fastmcp inspect --file server.ts
|
|
278
290
|
fastmcp inspect --url http://localhost:3000
|
|
279
291
|
fastmcp inspect --file server.ts --json
|
|
292
|
+
fastmcp inspect --file server.ts --modern # negotiate the 2026-07-28 protocol era over stdio
|
|
293
|
+
fastmcp inspect --file server.ts --pin 2026-07-28 # require that exact era
|
|
280
294
|
|
|
281
295
|
# Call a tool, read a resource, or get a prompt
|
|
282
296
|
fastmcp call add --file server.ts a=1 b=2
|
|
@@ -298,12 +312,15 @@ fastmcp install claude-desktop server.ts
|
|
|
298
312
|
fastmcp discover
|
|
299
313
|
```
|
|
300
314
|
|
|
315
|
+
`fastmcp inspect`, `fastmcp list`, and `fastmcp call` connect over stdio by default, when you pass `--file` or `--command`. Add `--modern` to negotiate the newer 2026-07-28 protocol era. A `--url` connection negotiates the protocol version automatically, so `--modern` is not needed for HTTP. `--pin <version>` works on every transport, including HTTP. It forces that exact protocol revision. The connection fails if the server does not offer it.
|
|
316
|
+
|
|
301
317
|
---
|
|
302
318
|
|
|
303
319
|
## Ecosystem
|
|
304
320
|
|
|
305
321
|
| Package | Role |
|
|
306
322
|
|---|---|
|
|
307
|
-
| [`@modelcontextprotocol/
|
|
323
|
+
| [`@modelcontextprotocol/server`](https://www.npmjs.com/package/@modelcontextprotocol/server) | Official MCP TypeScript SDK v2 β the server-side protocol implementation FastMCP builds on |
|
|
324
|
+
| [`@modelcontextprotocol/client`](https://www.npmjs.com/package/@modelcontextprotocol/client) | Official MCP TypeScript SDK v2 β the client-side protocol implementation FastMCP builds on |
|
|
308
325
|
| [`fastmcp` (PyPI)](https://github.com/PrefectHQ/fastmcp) | The Python original this project models its API after |
|
|
309
326
|
| [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) | Official MCP Apps extension β foundation for the Apps pillar |
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/cli/entrypoint-runtime.ts
|
|
5
|
+
var import_node_url = require("url");
|
|
6
|
+
var AUTO_DETECT_NAMES = ["default", "mcp", "server", "app"];
|
|
7
|
+
function isFastMCPInstance(value) {
|
|
8
|
+
if (!value || typeof value !== "object") return false;
|
|
9
|
+
const obj = value;
|
|
10
|
+
return typeof obj["run"] === "function" && typeof obj["tool"] === "function" && typeof obj["resource"] === "function" && typeof obj["prompt"] === "function" && typeof obj["connect"] === "function";
|
|
11
|
+
}
|
|
12
|
+
function describe(value) {
|
|
13
|
+
if (value === null) return "null";
|
|
14
|
+
if (Array.isArray(value)) return "an array";
|
|
15
|
+
return `a ${typeof value}`;
|
|
16
|
+
}
|
|
17
|
+
function fail(message) {
|
|
18
|
+
console.error(`fastmcp: ${message}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
async function resolveFactory(value, exportName, filePath) {
|
|
22
|
+
if (typeof value !== "function") return value;
|
|
23
|
+
try {
|
|
24
|
+
return await value();
|
|
25
|
+
} catch (err) {
|
|
26
|
+
fail(
|
|
27
|
+
`Failed to call entrypoint factory "${exportName}" in ${filePath}: ` + (err instanceof Error ? err.message : String(err))
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function main() {
|
|
32
|
+
const filePath = process.env["FASTMCP_ENTRYPOINT_FILE"];
|
|
33
|
+
if (!filePath) {
|
|
34
|
+
fail("internal error: FASTMCP_ENTRYPOINT_FILE was not set for the entrypoint bootstrap");
|
|
35
|
+
}
|
|
36
|
+
const explicitExport = process.env["FASTMCP_ENTRYPOINT_EXPORT"];
|
|
37
|
+
let mod;
|
|
38
|
+
try {
|
|
39
|
+
mod = await import((0, import_node_url.pathToFileURL)(filePath).href);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
fail(
|
|
42
|
+
`Failed to load entrypoint file "${filePath}": ` + (err instanceof Error ? err.stack ?? err.message : String(err))
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
let resolved;
|
|
46
|
+
if (explicitExport) {
|
|
47
|
+
if (!(explicitExport in mod)) {
|
|
48
|
+
const available = Object.keys(mod).filter((k) => k !== "__esModule");
|
|
49
|
+
fail(
|
|
50
|
+
`Entrypoint export "${explicitExport}" not found in ${filePath}.` + (available.length > 0 ? ` Available exports: ${available.join(", ")}.` : " The file has no exports.")
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const target = await resolveFactory(mod[explicitExport], explicitExport, filePath);
|
|
54
|
+
if (!isFastMCPInstance(target)) {
|
|
55
|
+
fail(
|
|
56
|
+
`Entrypoint export "${explicitExport}" in ${filePath} is not a FastMCP server (or a function that returns one). Got ${describe(target)}.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
resolved = target;
|
|
60
|
+
} else {
|
|
61
|
+
for (const name of AUTO_DETECT_NAMES) {
|
|
62
|
+
if (!(name in mod)) continue;
|
|
63
|
+
const candidate = mod[name];
|
|
64
|
+
if (isFastMCPInstance(candidate)) {
|
|
65
|
+
resolved = candidate;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (resolved === void 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (resolved.isRunning) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
await resolved.run();
|
|
77
|
+
if (resolved.address) {
|
|
78
|
+
const { host, port, path } = resolved.address;
|
|
79
|
+
process.stderr.write(`listening on http://${host}:${port}${path}
|
|
80
|
+
`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
main().catch((err) => {
|
|
84
|
+
console.error(err);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=entrypoint-runtime.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/entrypoint-runtime.ts"],"sourcesContent":["/**\n * Entrypoint bootstrap β spawned by the CLI (`run`, `inspect`, `list`, `call`,\n * `dev inspector`) instead of the user's server file directly.\n *\n * This mirrors Python FastMCP's `FileSystemSource` entrypoint resolution\n * (`fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py`): import the\n * user's file, resolve a named export (or a factory function that returns a\n * server), and start it. Keeping this in a spawned subprocess (rather than\n * importing the user's file in the CLI's own process) preserves two things the\n * existing CLI already relies on: `tsx` transpilation for TypeScript files, and\n * process isolation so a crashing user server doesn't take down the CLI.\n *\n * Contract (set by the parent CLI process via environment variables):\n * - FASTMCP_ENTRYPOINT_FILE: absolute path to the user's server file (required)\n * - FASTMCP_ENTRYPOINT_EXPORT: export name to resolve. Omitted when the user\n * didn't specify one, in which case a conventional name is auto-detected.\n *\n * Resolution order when FASTMCP_ENTRYPOINT_EXPORT is not set:\n * `default`, `mcp`, `server`, `app` β the first of these that is already a\n * FastMCP instance wins. Auto-detection deliberately does NOT call factory\n * functions (mirroring Python FastMCP's `_find_server_object`, which only\n * resolves factories for an explicit entrypoint) β invoking an arbitrary\n * exported function as a side effect of guessing a name would be surprising,\n * and a name match that isn't a FastMCP instance is skipped in favor of the\n * next candidate rather than committing to it. Factory functions are only\n * resolved when the export name is given explicitly (`file:export` or\n * `--export`).\n *\n * Backwards compatibility: if no explicit export was requested and none of the\n * conventional names resolve to a valid entrypoint, this script assumes the\n * file already started its own server via top-level side effects (the\n * historical fastmcp-ts contract, e.g. `server.run()` at module scope) and\n * exits without error. An explicit export request that can't be resolved is\n * always a hard error.\n */\n\nimport { pathToFileURL } from 'node:url'\n\nconst AUTO_DETECT_NAMES = ['default', 'mcp', 'server', 'app']\n\ninterface RunnableFastMCP {\n run(): Promise<void>\n isRunning: boolean\n address: { host: string; port: number; path: string } | null\n}\n\nfunction isFastMCPInstance(value: unknown): value is RunnableFastMCP {\n if (!value || typeof value !== 'object') return false\n const obj = value as Record<string, unknown>\n return (\n typeof obj['run'] === 'function' &&\n typeof obj['tool'] === 'function' &&\n typeof obj['resource'] === 'function' &&\n typeof obj['prompt'] === 'function' &&\n typeof obj['connect'] === 'function'\n )\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'an array'\n return `a ${typeof value}`\n}\n\nfunction fail(message: string): never {\n console.error(`fastmcp: ${message}`)\n process.exit(1)\n}\n\nasync function resolveFactory(\n value: unknown,\n exportName: string,\n filePath: string,\n): Promise<unknown> {\n if (typeof value !== 'function') return value\n try {\n return await (value as () => unknown)()\n } catch (err) {\n fail(\n `Failed to call entrypoint factory \"${exportName}\" in ${filePath}: ` +\n (err instanceof Error ? err.message : String(err)),\n )\n }\n}\n\nasync function main(): Promise<void> {\n const filePath = process.env['FASTMCP_ENTRYPOINT_FILE']\n if (!filePath) {\n fail('internal error: FASTMCP_ENTRYPOINT_FILE was not set for the entrypoint bootstrap')\n }\n\n const explicitExport = process.env['FASTMCP_ENTRYPOINT_EXPORT']\n\n let mod: Record<string, unknown>\n try {\n mod = (await import(pathToFileURL(filePath).href)) as Record<string, unknown>\n } catch (err) {\n fail(\n `Failed to load entrypoint file \"${filePath}\": ` +\n (err instanceof Error ? (err.stack ?? err.message) : String(err)),\n )\n }\n\n let resolved: RunnableFastMCP | undefined\n\n if (explicitExport) {\n if (!(explicitExport in mod)) {\n const available = Object.keys(mod).filter((k) => k !== '__esModule')\n fail(\n `Entrypoint export \"${explicitExport}\" not found in ${filePath}.` +\n (available.length > 0\n ? ` Available exports: ${available.join(', ')}.`\n : ' The file has no exports.'),\n )\n }\n const target = await resolveFactory(mod[explicitExport], explicitExport, filePath)\n if (!isFastMCPInstance(target)) {\n fail(\n `Entrypoint export \"${explicitExport}\" in ${filePath} is not a FastMCP server ` +\n `(or a function that returns one). Got ${describe(target)}.`,\n )\n }\n resolved = target\n } else {\n for (const name of AUTO_DETECT_NAMES) {\n if (!(name in mod)) continue\n const candidate = mod[name]\n // Only a direct FastMCP instance is auto-detected β a name match that\n // isn't one (including a function, which would require calling it) is\n // skipped in favor of the next conventional name rather than erroring\n // or invoking it speculatively.\n if (isFastMCPInstance(candidate)) {\n resolved = candidate\n break\n }\n }\n if (resolved === undefined) {\n // No conventional export resolved to a FastMCP instance. Assume this is\n // a legacy self-running file (it started its own server via top-level\n // side effects when imported above) and exit quietly.\n return\n }\n }\n\n if (resolved.isRunning) {\n // The file already started this server itself (e.g. a top-level\n // `server.run()` call) before we got a chance to. Don't start it twice.\n return\n }\n\n await resolved.run()\n\n // Mirrors the \"listening on <url>\" line the raw-SDK CLI test fixtures print\n // themselves β FastMCP's own run() doesn't emit a startup banner, but the\n // CLI's `run` command watches stderr for this text to detect a successful\n // HTTP start (see spawnServer()'s \"started\" detection in commands/run.ts).\n if (resolved.address) {\n const { host, port, path } = resolved.address\n process.stderr.write(`listening on http://${host}:${port}${path}\\n`)\n }\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":";;;;AAoCA,sBAA8B;AAE9B,IAAM,oBAAoB,CAAC,WAAW,OAAO,UAAU,KAAK;AAQ5D,SAAS,kBAAkB,OAA0C;AACnE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,KAAK,MAAM,cACtB,OAAO,IAAI,MAAM,MAAM,cACvB,OAAO,IAAI,UAAU,MAAM,cAC3B,OAAO,IAAI,QAAQ,MAAM,cACzB,OAAO,IAAI,SAAS,MAAM;AAE9B;AAEA,SAAS,SAAS,OAAwB;AACxC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,SAAO,KAAK,OAAO,KAAK;AAC1B;AAEA,SAAS,KAAK,SAAwB;AACpC,UAAQ,MAAM,YAAY,OAAO,EAAE;AACnC,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,eACb,OACA,YACA,UACkB;AAClB,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,MAAI;AACF,WAAO,MAAO,MAAwB;AAAA,EACxC,SAAS,KAAK;AACZ;AAAA,MACE,sCAAsC,UAAU,QAAQ,QAAQ,QAC7D,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACpD;AAAA,EACF;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,WAAW,QAAQ,IAAI,yBAAyB;AACtD,MAAI,CAAC,UAAU;AACb,SAAK,kFAAkF;AAAA,EACzF;AAEA,QAAM,iBAAiB,QAAQ,IAAI,2BAA2B;AAE9D,MAAI;AACJ,MAAI;AACF,UAAO,MAAM,WAAO,+BAAc,QAAQ,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ;AAAA,MACE,mCAAmC,QAAQ,SACxC,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG;AAAA,IACnE;AAAA,EACF;AAEA,MAAI;AAEJ,MAAI,gBAAgB;AAClB,QAAI,EAAE,kBAAkB,MAAM;AAC5B,YAAM,YAAY,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,MAAM,YAAY;AACnE;AAAA,QACE,sBAAsB,cAAc,kBAAkB,QAAQ,OAC3D,UAAU,SAAS,IAChB,uBAAuB,UAAU,KAAK,IAAI,CAAC,MAC3C;AAAA,MACR;AAAA,IACF;AACA,UAAM,SAAS,MAAM,eAAe,IAAI,cAAc,GAAG,gBAAgB,QAAQ;AACjF,QAAI,CAAC,kBAAkB,MAAM,GAAG;AAC9B;AAAA,QACE,sBAAsB,cAAc,QAAQ,QAAQ,kEACT,SAAS,MAAM,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,eAAW;AAAA,EACb,OAAO;AACL,eAAW,QAAQ,mBAAmB;AACpC,UAAI,EAAE,QAAQ,KAAM;AACpB,YAAM,YAAY,IAAI,IAAI;AAK1B,UAAI,kBAAkB,SAAS,GAAG;AAChC,mBAAW;AACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,aAAa,QAAW;AAI1B;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,WAAW;AAGtB;AAAA,EACF;AAEA,QAAM,SAAS,IAAI;AAMnB,MAAI,SAAS,SAAS;AACpB,UAAM,EAAE,MAAM,MAAM,KAAK,IAAI,SAAS;AACtC,YAAQ,OAAO,MAAM,uBAAuB,IAAI,IAAI,IAAI,GAAG,IAAI;AAAA,CAAI;AAAA,EACrE;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|