anygrab-mcp 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 botshen & AnyGrab Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+
4
+ interface McpLoaderOptions {
5
+ fetchTimeoutMs?: number;
6
+ cssTimeoutMs?: number;
7
+ fetchImpl?: typeof fetch;
8
+ headers?: Record<string, string>;
9
+ }
10
+
11
+ type AnyGrabMcpServerOptions = McpLoaderOptions;
12
+ /**
13
+ * Creates and configures an AnyGrab Model Context Protocol (`McpServer`) instance
14
+ * exposing `grab_url`, `grab_html`, and `extract_design_tokens`.
15
+ */
16
+ declare function createAnyGrabMcpServer(options?: AnyGrabMcpServerOptions): McpServer;
17
+ /**
18
+ * Connects an AnyGrab MCP server to standard input/output (`StdioServerTransport`)
19
+ * for local Coding Agent hosts (Claude Code, Cursor, Antigravity, Codex).
20
+ */
21
+ declare function startMcpStdioServer(options?: AnyGrabMcpServerOptions): Promise<McpServer>;
22
+
23
+ export { createAnyGrabMcpServer, startMcpStdioServer };
24
+ export type { AnyGrabMcpServerOptions };
package/dist/index.js ADDED
@@ -0,0 +1,523 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { realpathSync } from "fs";
5
+ import { fileURLToPath } from "url";
6
+ import {
7
+ extractDesignTokens,
8
+ grab,
9
+ VERSION
10
+ } from "anygrab-core";
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { z } from "zod";
14
+
15
+ // src/loader.ts
16
+ import { parseHTML } from "linkedom";
17
+ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
18
+ var DEFAULT_CSS_TIMEOUT_MS = 4e3;
19
+ var MAX_STYLESHEETS = 24;
20
+ var MAX_CSS_BYTES = 4 * 1024 * 1024;
21
+ var DEFAULT_USER_AGENT = "Mozilla/5.0 (compatible; AnyGrab-MCP/0.2; +https://github.com/botshen/anygrab)";
22
+ function normalizeHtmlDocument(html) {
23
+ const trimmed = html.trim();
24
+ if (!trimmed) {
25
+ return "<!doctype html><html><head></head><body></body></html>";
26
+ }
27
+ if (/<html[\s>]/i.test(trimmed)) {
28
+ return /<!doctype/i.test(trimmed) ? trimmed : `<!doctype html>
29
+ ${trimmed}`;
30
+ }
31
+ return `<!doctype html><html><head></head><body>${trimmed}</body></html>`;
32
+ }
33
+ function parseHtmlDocument(html) {
34
+ const { document } = parseHTML(normalizeHtmlDocument(html));
35
+ return document;
36
+ }
37
+ function decodeDataCssUri(href) {
38
+ const match = /^data:text\/css(?:;charset=[^;,]+)?(;base64)?,([\s\S]*)$/i.exec(href.trim());
39
+ if (!match) return null;
40
+ const isBase64 = Boolean(match[1]);
41
+ const payload = match[2] ?? "";
42
+ try {
43
+ return isBase64 ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ function rewriteRelativeCssUrls(cssText, stylesheetUrl) {
49
+ return cssText.replace(/url\(\s*(['"]?)([^'")]+)\1\s*\)/gi, (full, quote, rawRef) => {
50
+ const ref = rawRef.trim();
51
+ if (!ref || ref.startsWith("data:") || ref.startsWith("#") || ref.startsWith("http://") || ref.startsWith("https://")) {
52
+ return full;
53
+ }
54
+ try {
55
+ const resolved = new URL(ref, stylesheetUrl).href;
56
+ return `url(${quote}${resolved}${quote})`;
57
+ } catch {
58
+ return full;
59
+ }
60
+ });
61
+ }
62
+ function findStylesheetLinks(document) {
63
+ return Array.from(document.querySelectorAll("link")).filter((link) => {
64
+ if (link.hasAttribute("disabled")) return false;
65
+ const relTokens = (link.getAttribute("rel") ?? "").toLowerCase().trim().split(/\s+/).filter(Boolean);
66
+ if (!relTokens.includes("stylesheet") || relTokens.includes("alternate")) {
67
+ return false;
68
+ }
69
+ const media = (link.getAttribute("media") ?? "").trim().toLowerCase();
70
+ if (media === "print") {
71
+ return false;
72
+ }
73
+ return Boolean((link.getAttribute("href") ?? "").trim());
74
+ });
75
+ }
76
+ function replaceLinkWithStyle(document, link, cssText, sourceRef) {
77
+ const styleEl = document.createElement("style");
78
+ styleEl.setAttribute("data-anygrab-inlined", sourceRef);
79
+ styleEl.textContent = cssText;
80
+ if (link.parentNode) {
81
+ link.parentNode.replaceChild(styleEl, link);
82
+ } else {
83
+ (document.head ?? document.documentElement)?.appendChild(styleEl);
84
+ }
85
+ }
86
+ function inlineDataStylesheets(document) {
87
+ let count = 0;
88
+ for (const link of findStylesheetLinks(document)) {
89
+ const href = (link.getAttribute("href") ?? "").trim();
90
+ const cssText = decodeDataCssUri(href);
91
+ if (cssText !== null) {
92
+ replaceLinkWithStyle(document, link, cssText, "data:text/css");
93
+ count++;
94
+ }
95
+ }
96
+ return count;
97
+ }
98
+ async function fetchTextWithTimeout(url, timeoutMs, fetchFn, extraHeaders, accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") {
99
+ const controller = new AbortController();
100
+ const timer = setTimeout(() => {
101
+ controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));
102
+ }, timeoutMs);
103
+ try {
104
+ const response = await fetchFn(url, {
105
+ method: "GET",
106
+ redirect: "follow",
107
+ signal: controller.signal,
108
+ headers: {
109
+ "user-agent": DEFAULT_USER_AGENT,
110
+ accept,
111
+ ...extraHeaders
112
+ }
113
+ });
114
+ if (!response.ok) {
115
+ throw new Error(`HTTP ${response.status} ${response.statusText}`.trim());
116
+ }
117
+ const contentLength = Number(response.headers.get("content-length") ?? "0");
118
+ if (contentLength > MAX_CSS_BYTES * 2) {
119
+ throw new Error(`Response payload too large (${contentLength} bytes)`);
120
+ }
121
+ const text = await response.text();
122
+ return { text, finalUrl: response.url || url };
123
+ } catch (error) {
124
+ if (controller.signal.aborted) {
125
+ throw new Error(`Timed out after ${timeoutMs}ms fetching ${url}`);
126
+ }
127
+ throw error;
128
+ } finally {
129
+ clearTimeout(timer);
130
+ }
131
+ }
132
+ async function loadUrlDocument(url, options = {}) {
133
+ let parsedUrl;
134
+ try {
135
+ parsedUrl = new URL(url);
136
+ } catch {
137
+ throw new Error(`Invalid URL "${url}". Expected an http:// or https:// URL.`);
138
+ }
139
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
140
+ throw new Error(`Unsupported URL protocol "${parsedUrl.protocol}". Expected http: or https:.`);
141
+ }
142
+ const fetchFn = options.fetchImpl ?? globalThis.fetch;
143
+ const fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
144
+ const cssTimeoutMs = options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS;
145
+ const { text: html, finalUrl } = await fetchTextWithTimeout(
146
+ parsedUrl.href,
147
+ fetchTimeoutMs,
148
+ fetchFn,
149
+ options.headers
150
+ );
151
+ const document = parseHtmlDocument(html);
152
+ const links = findStylesheetLinks(document).slice(0, MAX_STYLESHEETS);
153
+ const warnings = [];
154
+ let inlinedStylesheets = 0;
155
+ await Promise.all(
156
+ links.map(async (link) => {
157
+ const rawHref = (link.getAttribute("href") ?? "").trim();
158
+ const dataCss = decodeDataCssUri(rawHref);
159
+ if (dataCss !== null) {
160
+ replaceLinkWithStyle(document, link, dataCss, "data:text/css");
161
+ inlinedStylesheets++;
162
+ return;
163
+ }
164
+ let resolvedCssUrl;
165
+ try {
166
+ resolvedCssUrl = new URL(rawHref, finalUrl);
167
+ } catch {
168
+ warnings.push(`Skipped invalid stylesheet URL: ${rawHref}`);
169
+ return;
170
+ }
171
+ if (resolvedCssUrl.protocol !== "http:" && resolvedCssUrl.protocol !== "https:") {
172
+ return;
173
+ }
174
+ try {
175
+ const { text: cssText, finalUrl: cssFinalUrl } = await fetchTextWithTimeout(
176
+ resolvedCssUrl.href,
177
+ cssTimeoutMs,
178
+ fetchFn,
179
+ options.headers,
180
+ "text/css,*/*;q=0.1"
181
+ );
182
+ if (cssText.length > MAX_CSS_BYTES) {
183
+ warnings.push(`Skipped oversized stylesheet (${cssText.length} chars): ${cssFinalUrl}`);
184
+ return;
185
+ }
186
+ if (cssText.trim().length > 0) {
187
+ replaceLinkWithStyle(
188
+ document,
189
+ link,
190
+ rewriteRelativeCssUrls(cssText, cssFinalUrl),
191
+ cssFinalUrl
192
+ );
193
+ inlinedStylesheets++;
194
+ } else {
195
+ link.parentNode?.removeChild(link);
196
+ }
197
+ } catch (error) {
198
+ const reason = error instanceof Error ? error.message : String(error);
199
+ warnings.push(`Failed to inline stylesheet ${resolvedCssUrl.href}: ${reason}`);
200
+ }
201
+ })
202
+ );
203
+ return {
204
+ document,
205
+ finalUrl,
206
+ inlinedStylesheets,
207
+ warnings
208
+ };
209
+ }
210
+ function scopeDocumentToSelector(document, selector) {
211
+ const trimmed = selector?.trim();
212
+ if (!trimmed) return;
213
+ let matched;
214
+ try {
215
+ matched = document.querySelector(trimmed);
216
+ } catch (error) {
217
+ const detail = error instanceof Error ? error.message : String(error);
218
+ throw new Error(`Invalid CSS selector "${trimmed}": ${detail}`);
219
+ }
220
+ if (!matched) {
221
+ throw new Error(`No element matched CSS selector "${trimmed}".`);
222
+ }
223
+ if (matched === document.documentElement || matched === document.body || matched === document.head) {
224
+ return;
225
+ }
226
+ const head = document.head ?? document.documentElement;
227
+ if (document.body && head) {
228
+ for (const styleNode of Array.from(document.body.querySelectorAll("style"))) {
229
+ if (!matched.contains(styleNode)) {
230
+ head.appendChild(styleNode);
231
+ }
232
+ }
233
+ }
234
+ const ancestorSpine = /* @__PURE__ */ new Set();
235
+ let cursor = matched;
236
+ while (cursor) {
237
+ ancestorSpine.add(cursor);
238
+ cursor = cursor.parentNode;
239
+ }
240
+ let container = document.body;
241
+ while (container && container !== matched) {
242
+ for (const child of Array.from(container.childNodes)) {
243
+ if (!ancestorSpine.has(child)) {
244
+ container.removeChild(child);
245
+ }
246
+ }
247
+ const nextInSpine = Array.from(container.childNodes).find(
248
+ (child) => ancestorSpine.has(child)
249
+ );
250
+ container = nextInSpine ?? null;
251
+ }
252
+ }
253
+
254
+ // src/index.ts
255
+ var OUTPUT_FORMAT_SCHEMA = z.enum(["prompt", "tailwind-html", "jsx", "markdown"]).default("prompt").describe(
256
+ 'Target serialization format: "prompt" (structured AI context bundle), "tailwind-html" (clean semantic HTML with Tailwind utilities), "jsx" (React JSX component snippet), or "markdown" (high-fidelity GFM preserving tables and code).'
257
+ );
258
+ function createAnyGrabMcpServer(options = {}) {
259
+ const server = new McpServer({
260
+ name: "anygrab-mcp",
261
+ version: VERSION
262
+ });
263
+ server.registerTool(
264
+ "grab_url",
265
+ {
266
+ title: "Grab & Distill Webpage URL",
267
+ description: 'Fetch a public HTTP/HTTPS webpage URL, automatically inline external <link rel="stylesheet"> CSS into <style> nodes, parse with LinkeDOM, and distill the target DOM into token-budget-aware AI context (prompt, tailwind-html, jsx, or markdown) with compression telemetry.',
268
+ annotations: {
269
+ readOnlyHint: true,
270
+ destructiveHint: false,
271
+ idempotentHint: true,
272
+ openWorldHint: true
273
+ },
274
+ inputSchema: {
275
+ url: z.string().url().describe("Public HTTP or HTTPS webpage URL to fetch and distill."),
276
+ selector: z.string().min(1).optional().describe(
277
+ 'Optional CSS selector to scope extraction to a specific component or section (e.g. "#pricing", "main article").'
278
+ ),
279
+ format: OUTPUT_FORMAT_SCHEMA,
280
+ maxTokens: z.number().int().positive().optional().describe(
281
+ "Optional token budget ceiling (e.g. 1200). Repetitive or low-salience subtrees fold cleanly without breaking HTML/JSX tag closure."
282
+ ),
283
+ compactSvg: z.boolean().default(true).describe(
284
+ "Collapse verbose inline <svg> path data into compact semantic placeholders while keeping dimensions and colors (default: true)."
285
+ ),
286
+ includeHoverStyles: z.boolean().default(false).describe(
287
+ "Include interactive :hover Tailwind utility hints matched from page stylesheets (default: false)."
288
+ ),
289
+ adapter: z.string().min(1).optional().describe(
290
+ 'Optional AnyGrab site/framework adapter name (e.g. "docs-mdx", "github-ui", "arxiv-paper", "marketing-pricing").'
291
+ ),
292
+ cssTimeoutMs: z.number().int().positive().max(3e4).optional().describe(
293
+ `Timeout in milliseconds when fetching external <link rel="stylesheet"> CSS assets (default: ${DEFAULT_CSS_TIMEOUT_MS}).`
294
+ )
295
+ }
296
+ },
297
+ async ({
298
+ url,
299
+ selector,
300
+ format,
301
+ maxTokens,
302
+ compactSvg,
303
+ includeHoverStyles,
304
+ adapter,
305
+ cssTimeoutMs
306
+ }) => {
307
+ const { document, finalUrl, inlinedStylesheets, warnings: loaderWarnings } = await loadUrlDocument(url, {
308
+ ...options,
309
+ cssTimeoutMs: cssTimeoutMs ?? options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS,
310
+ fetchTimeoutMs: options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS
311
+ });
312
+ scopeDocumentToSelector(document, selector);
313
+ const result = grab(document, {
314
+ format: format ?? "prompt",
315
+ selector,
316
+ maxTokens,
317
+ compactSvg: compactSvg ?? true,
318
+ includeHoverStyles: includeHoverStyles ?? false,
319
+ adapter,
320
+ sourceUrl: finalUrl
321
+ });
322
+ const allWarnings = [...result.warnings, ...loaderWarnings];
323
+ const telemetrySummary = {
324
+ sourceUrl: finalUrl,
325
+ format: result.format,
326
+ inlinedStylesheets,
327
+ metrics: result.metrics,
328
+ designTokens: result.designTokens,
329
+ warnings: allWarnings
330
+ };
331
+ return {
332
+ content: [
333
+ {
334
+ type: "text",
335
+ text: result.output
336
+ },
337
+ {
338
+ type: "text",
339
+ text: JSON.stringify(telemetrySummary, null, 2)
340
+ }
341
+ ],
342
+ structuredContent: {
343
+ ...telemetrySummary,
344
+ output: result.output
345
+ }
346
+ };
347
+ }
348
+ );
349
+ server.registerTool(
350
+ "grab_html",
351
+ {
352
+ title: "Distill Raw HTML Snippet",
353
+ description: "Distill a raw HTML document or component fragment (including inline <style> blocks) into clean prompt context, Tailwind HTML, React JSX, or GitHub-Flavored Markdown.",
354
+ annotations: {
355
+ readOnlyHint: true,
356
+ destructiveHint: false,
357
+ idempotentHint: true,
358
+ openWorldHint: false
359
+ },
360
+ inputSchema: {
361
+ html: z.string().min(1).describe("Raw HTML document or UI snippet (may include <style> tags) to distill."),
362
+ format: OUTPUT_FORMAT_SCHEMA,
363
+ selector: z.string().min(1).optional().describe("Optional CSS selector to scope extraction to a specific element."),
364
+ maxTokens: z.number().int().positive().optional().describe("Optional token budget ceiling (e.g. 800)."),
365
+ compactSvg: z.boolean().default(true).describe("Collapse verbose inline <svg> path data into compact semantic placeholders."),
366
+ includeHoverStyles: z.boolean().default(false).describe("Include interactive :hover Tailwind utility hints from <style> blocks."),
367
+ adapter: z.string().min(1).optional().describe("Optional AnyGrab adapter name to apply."),
368
+ sourceUrl: z.string().url().optional().describe("Optional source URL context for relative links and adapter matching.")
369
+ }
370
+ },
371
+ async ({
372
+ html,
373
+ format,
374
+ selector,
375
+ maxTokens,
376
+ compactSvg,
377
+ includeHoverStyles,
378
+ adapter,
379
+ sourceUrl
380
+ }) => {
381
+ const document = parseHtmlDocument(html);
382
+ inlineDataStylesheets(document);
383
+ scopeDocumentToSelector(document, selector);
384
+ const result = grab(document, {
385
+ format: format ?? "prompt",
386
+ selector,
387
+ maxTokens,
388
+ compactSvg: compactSvg ?? true,
389
+ includeHoverStyles: includeHoverStyles ?? false,
390
+ adapter,
391
+ sourceUrl
392
+ });
393
+ const telemetrySummary = {
394
+ format: result.format,
395
+ metrics: result.metrics,
396
+ designTokens: result.designTokens,
397
+ warnings: result.warnings
398
+ };
399
+ return {
400
+ content: [
401
+ {
402
+ type: "text",
403
+ text: result.output
404
+ },
405
+ {
406
+ type: "text",
407
+ text: JSON.stringify(telemetrySummary, null, 2)
408
+ }
409
+ ],
410
+ structuredContent: {
411
+ ...telemetrySummary,
412
+ output: result.output
413
+ }
414
+ };
415
+ }
416
+ );
417
+ server.registerTool(
418
+ "extract_design_tokens",
419
+ {
420
+ title: "Extract UI Design Tokens",
421
+ description: "Extract visual design tokens (color palette, font stacks, border radii, box shadows, and spacing scale) as structured JSON from either a public webpage URL or a raw HTML+CSS snippet.",
422
+ annotations: {
423
+ readOnlyHint: true,
424
+ destructiveHint: false,
425
+ idempotentHint: true,
426
+ openWorldHint: true
427
+ },
428
+ inputSchema: {
429
+ url: z.string().url().optional().describe(
430
+ 'Public HTTP/HTTPS webpage URL to inspect (fetches HTML and inlines external <link rel="stylesheet"> CSS). Provide either "url" or "html".'
431
+ ),
432
+ html: z.string().min(1).optional().describe(
433
+ 'Raw HTML string (with <style> or inline styles) to inspect. Provide either "url" or "html".'
434
+ ),
435
+ selector: z.string().min(1).optional().describe("Optional CSS selector to scope token extraction to a target subtree."),
436
+ cssTimeoutMs: z.number().int().positive().max(3e4).optional().describe(
437
+ `Timeout in milliseconds when fetching external stylesheets for "url" (default: ${DEFAULT_CSS_TIMEOUT_MS}).`
438
+ )
439
+ }
440
+ },
441
+ async ({ url, html, selector, cssTimeoutMs }) => {
442
+ if (!url && !html) {
443
+ return {
444
+ isError: true,
445
+ content: [
446
+ {
447
+ type: "text",
448
+ text: 'Invalid input: provide either "url" (public webpage URL) or "html" (raw HTML snippet) to extract design tokens.'
449
+ }
450
+ ]
451
+ };
452
+ }
453
+ let document;
454
+ let resolvedUrl;
455
+ let warnings = [];
456
+ if (html) {
457
+ document = parseHtmlDocument(html);
458
+ inlineDataStylesheets(document);
459
+ resolvedUrl = url;
460
+ } else {
461
+ const loaded = await loadUrlDocument(url, {
462
+ ...options,
463
+ cssTimeoutMs: cssTimeoutMs ?? options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS,
464
+ fetchTimeoutMs: options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS
465
+ });
466
+ document = loaded.document;
467
+ resolvedUrl = loaded.finalUrl;
468
+ warnings = loaded.warnings;
469
+ }
470
+ scopeDocumentToSelector(document, selector);
471
+ const tokens = extractDesignTokens(document, {
472
+ selector,
473
+ sourceUrl: resolvedUrl
474
+ });
475
+ const payload = {
476
+ ...resolvedUrl ? { sourceUrl: resolvedUrl } : {},
477
+ designTokens: tokens,
478
+ ...warnings.length > 0 ? { warnings } : {}
479
+ };
480
+ return {
481
+ content: [
482
+ {
483
+ type: "text",
484
+ text: JSON.stringify(tokens, null, 2)
485
+ }
486
+ ],
487
+ structuredContent: payload
488
+ };
489
+ }
490
+ );
491
+ return server;
492
+ }
493
+ async function startMcpStdioServer(options = {}) {
494
+ const server = createAnyGrabMcpServer(options);
495
+ const transport = new StdioServerTransport();
496
+ await server.connect(transport);
497
+ return server;
498
+ }
499
+ function checkDirectExecution() {
500
+ if (typeof process === "undefined" || !Array.isArray(process.argv) || !process.argv[1]) {
501
+ return false;
502
+ }
503
+ try {
504
+ const invokedPath = realpathSync(process.argv[1]);
505
+ const currentPath = realpathSync(fileURLToPath(import.meta.url));
506
+ return invokedPath === currentPath;
507
+ } catch {
508
+ return process.argv[1].endsWith("anygrab-mcp") || process.argv[1].endsWith("/mcp/dist/index.js") || process.argv[1].endsWith("\\mcp\\dist\\index.js");
509
+ }
510
+ }
511
+ if (checkDirectExecution()) {
512
+ startMcpStdioServer().catch((error) => {
513
+ const message = error instanceof Error ? error.message : String(error);
514
+ process.stderr.write(`AnyGrab MCP Server error: ${message}
515
+ `);
516
+ process.exitCode = 1;
517
+ });
518
+ }
519
+ export {
520
+ createAnyGrabMcpServer,
521
+ startMcpStdioServer
522
+ };
523
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/loader.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport {\n extractDesignTokens,\n grab,\n VERSION,\n type DesignTokens,\n type OutputFormat,\n} from 'anygrab-core';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport {\n DEFAULT_CSS_TIMEOUT_MS,\n DEFAULT_FETCH_TIMEOUT_MS,\n inlineDataStylesheets,\n loadUrlDocument,\n parseHtmlDocument,\n scopeDocumentToSelector,\n type McpLoaderOptions,\n} from './loader';\n\nexport type AnyGrabMcpServerOptions = McpLoaderOptions;\n\nconst OUTPUT_FORMAT_SCHEMA = z\n .enum(['prompt', 'tailwind-html', 'jsx', 'markdown'])\n .default('prompt')\n .describe(\n 'Target serialization format: \"prompt\" (structured AI context bundle), \"tailwind-html\" (clean semantic HTML with Tailwind utilities), \"jsx\" (React JSX component snippet), or \"markdown\" (high-fidelity GFM preserving tables and code).',\n );\n\n/**\n * Creates and configures an AnyGrab Model Context Protocol (`McpServer`) instance\n * exposing `grab_url`, `grab_html`, and `extract_design_tokens`.\n */\nexport function createAnyGrabMcpServer(options: AnyGrabMcpServerOptions = {}): McpServer {\n const server = new McpServer({\n name: 'anygrab-mcp',\n version: VERSION,\n });\n\n server.registerTool(\n 'grab_url',\n {\n title: 'Grab & Distill Webpage URL',\n description:\n 'Fetch a public HTTP/HTTPS webpage URL, automatically inline external <link rel=\"stylesheet\"> CSS into <style> nodes, parse with LinkeDOM, and distill the target DOM into token-budget-aware AI context (prompt, tailwind-html, jsx, or markdown) with compression telemetry.',\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n inputSchema: {\n url: z\n .string()\n .url()\n .describe('Public HTTP or HTTPS webpage URL to fetch and distill.'),\n selector: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'Optional CSS selector to scope extraction to a specific component or section (e.g. \"#pricing\", \"main article\").',\n ),\n format: OUTPUT_FORMAT_SCHEMA,\n maxTokens: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'Optional token budget ceiling (e.g. 1200). Repetitive or low-salience subtrees fold cleanly without breaking HTML/JSX tag closure.',\n ),\n compactSvg: z\n .boolean()\n .default(true)\n .describe(\n 'Collapse verbose inline <svg> path data into compact semantic placeholders while keeping dimensions and colors (default: true).',\n ),\n includeHoverStyles: z\n .boolean()\n .default(false)\n .describe(\n 'Include interactive :hover Tailwind utility hints matched from page stylesheets (default: false).',\n ),\n adapter: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'Optional AnyGrab site/framework adapter name (e.g. \"docs-mdx\", \"github-ui\", \"arxiv-paper\", \"marketing-pricing\").',\n ),\n cssTimeoutMs: z\n .number()\n .int()\n .positive()\n .max(30_000)\n .optional()\n .describe(\n `Timeout in milliseconds when fetching external <link rel=\"stylesheet\"> CSS assets (default: ${DEFAULT_CSS_TIMEOUT_MS}).`,\n ),\n },\n },\n async ({\n url,\n selector,\n format,\n maxTokens,\n compactSvg,\n includeHoverStyles,\n adapter,\n cssTimeoutMs,\n }) => {\n const { document, finalUrl, inlinedStylesheets, warnings: loaderWarnings } =\n await loadUrlDocument(url, {\n ...options,\n cssTimeoutMs: cssTimeoutMs ?? options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS,\n fetchTimeoutMs: options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS,\n });\n\n scopeDocumentToSelector(document, selector);\n\n const result = grab(document, {\n format: (format ?? 'prompt') as OutputFormat,\n selector,\n maxTokens,\n compactSvg: compactSvg ?? true,\n includeHoverStyles: includeHoverStyles ?? false,\n adapter,\n sourceUrl: finalUrl,\n });\n\n const allWarnings = [...result.warnings, ...loaderWarnings];\n const telemetrySummary = {\n sourceUrl: finalUrl,\n format: result.format,\n inlinedStylesheets,\n metrics: result.metrics,\n designTokens: result.designTokens,\n warnings: allWarnings,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: result.output,\n },\n {\n type: 'text' as const,\n text: JSON.stringify(telemetrySummary, null, 2),\n },\n ],\n structuredContent: {\n ...telemetrySummary,\n output: result.output,\n },\n };\n },\n );\n\n server.registerTool(\n 'grab_html',\n {\n title: 'Distill Raw HTML Snippet',\n description:\n 'Distill a raw HTML document or component fragment (including inline <style> blocks) into clean prompt context, Tailwind HTML, React JSX, or GitHub-Flavored Markdown.',\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n inputSchema: {\n html: z\n .string()\n .min(1)\n .describe('Raw HTML document or UI snippet (may include <style> tags) to distill.'),\n format: OUTPUT_FORMAT_SCHEMA,\n selector: z\n .string()\n .min(1)\n .optional()\n .describe('Optional CSS selector to scope extraction to a specific element.'),\n maxTokens: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Optional token budget ceiling (e.g. 800).'),\n compactSvg: z\n .boolean()\n .default(true)\n .describe('Collapse verbose inline <svg> path data into compact semantic placeholders.'),\n includeHoverStyles: z\n .boolean()\n .default(false)\n .describe('Include interactive :hover Tailwind utility hints from <style> blocks.'),\n adapter: z\n .string()\n .min(1)\n .optional()\n .describe('Optional AnyGrab adapter name to apply.'),\n sourceUrl: z\n .string()\n .url()\n .optional()\n .describe('Optional source URL context for relative links and adapter matching.'),\n },\n },\n async ({\n html,\n format,\n selector,\n maxTokens,\n compactSvg,\n includeHoverStyles,\n adapter,\n sourceUrl,\n }) => {\n const document = parseHtmlDocument(html);\n inlineDataStylesheets(document);\n scopeDocumentToSelector(document, selector);\n\n const result = grab(document, {\n format: (format ?? 'prompt') as OutputFormat,\n selector,\n maxTokens,\n compactSvg: compactSvg ?? true,\n includeHoverStyles: includeHoverStyles ?? false,\n adapter,\n sourceUrl,\n });\n\n const telemetrySummary = {\n format: result.format,\n metrics: result.metrics,\n designTokens: result.designTokens,\n warnings: result.warnings,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: result.output,\n },\n {\n type: 'text' as const,\n text: JSON.stringify(telemetrySummary, null, 2),\n },\n ],\n structuredContent: {\n ...telemetrySummary,\n output: result.output,\n },\n };\n },\n );\n\n server.registerTool(\n 'extract_design_tokens',\n {\n title: 'Extract UI Design Tokens',\n description:\n 'Extract visual design tokens (color palette, font stacks, border radii, box shadows, and spacing scale) as structured JSON from either a public webpage URL or a raw HTML+CSS snippet.',\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n inputSchema: {\n url: z\n .string()\n .url()\n .optional()\n .describe(\n 'Public HTTP/HTTPS webpage URL to inspect (fetches HTML and inlines external <link rel=\"stylesheet\"> CSS). Provide either \"url\" or \"html\".',\n ),\n html: z\n .string()\n .min(1)\n .optional()\n .describe(\n 'Raw HTML string (with <style> or inline styles) to inspect. Provide either \"url\" or \"html\".',\n ),\n selector: z\n .string()\n .min(1)\n .optional()\n .describe('Optional CSS selector to scope token extraction to a target subtree.'),\n cssTimeoutMs: z\n .number()\n .int()\n .positive()\n .max(30_000)\n .optional()\n .describe(\n `Timeout in milliseconds when fetching external stylesheets for \"url\" (default: ${DEFAULT_CSS_TIMEOUT_MS}).`,\n ),\n },\n },\n async ({ url, html, selector, cssTimeoutMs }) => {\n if (!url && !html) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: 'Invalid input: provide either \"url\" (public webpage URL) or \"html\" (raw HTML snippet) to extract design tokens.',\n },\n ],\n };\n }\n\n let document: Document;\n let resolvedUrl: string | undefined;\n let warnings: string[] = [];\n\n if (html) {\n document = parseHtmlDocument(html);\n inlineDataStylesheets(document);\n resolvedUrl = url;\n } else {\n const loaded = await loadUrlDocument(url!, {\n ...options,\n cssTimeoutMs: cssTimeoutMs ?? options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS,\n fetchTimeoutMs: options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS,\n });\n document = loaded.document;\n resolvedUrl = loaded.finalUrl;\n warnings = loaded.warnings;\n }\n\n scopeDocumentToSelector(document, selector);\n\n const tokens: DesignTokens = extractDesignTokens(document, {\n selector,\n sourceUrl: resolvedUrl,\n });\n\n const payload = {\n ...(resolvedUrl ? { sourceUrl: resolvedUrl } : {}),\n designTokens: tokens,\n ...(warnings.length > 0 ? { warnings } : {}),\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(tokens, null, 2),\n },\n ],\n structuredContent: payload,\n };\n },\n );\n\n return server;\n}\n\n/**\n * Connects an AnyGrab MCP server to standard input/output (`StdioServerTransport`)\n * for local Coding Agent hosts (Claude Code, Cursor, Antigravity, Codex).\n */\nexport async function startMcpStdioServer(options: AnyGrabMcpServerOptions = {}): Promise<McpServer> {\n const server = createAnyGrabMcpServer(options);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n return server;\n}\n\nfunction checkDirectExecution(): boolean {\n if (typeof process === 'undefined' || !Array.isArray(process.argv) || !process.argv[1]) {\n return false;\n }\n try {\n const invokedPath = realpathSync(process.argv[1]);\n const currentPath = realpathSync(fileURLToPath(import.meta.url));\n return invokedPath === currentPath;\n } catch {\n return (\n process.argv[1].endsWith('anygrab-mcp') ||\n process.argv[1].endsWith('/mcp/dist/index.js') ||\n process.argv[1].endsWith('\\\\mcp\\\\dist\\\\index.js')\n );\n }\n}\n\nif (checkDirectExecution()) {\n startMcpStdioServer().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`AnyGrab MCP Server error: ${message}\\n`);\n process.exitCode = 1;\n });\n}\n","import { parseHTML } from 'linkedom';\n\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10_000;\nexport const DEFAULT_CSS_TIMEOUT_MS = 4_000;\nconst MAX_STYLESHEETS = 24;\nconst MAX_CSS_BYTES = 4 * 1024 * 1024;\n\nconst DEFAULT_USER_AGENT =\n 'Mozilla/5.0 (compatible; AnyGrab-MCP/0.2; +https://github.com/botshen/anygrab)';\n\nexport interface McpLoaderOptions {\n fetchTimeoutMs?: number;\n cssTimeoutMs?: number;\n fetchImpl?: typeof fetch;\n headers?: Record<string, string>;\n}\n\nexport interface LoadedUrlDocument {\n document: Document;\n finalUrl: string;\n inlinedStylesheets: number;\n warnings: string[];\n}\n\nexport function normalizeHtmlDocument(html: string): string {\n const trimmed = html.trim();\n if (!trimmed) {\n return '<!doctype html><html><head></head><body></body></html>';\n }\n if (/<html[\\s>]/i.test(trimmed)) {\n return /<!doctype/i.test(trimmed) ? trimmed : `<!doctype html>\\n${trimmed}`;\n }\n return `<!doctype html><html><head></head><body>${trimmed}</body></html>`;\n}\n\nexport function parseHtmlDocument(html: string): Document {\n const { document } = parseHTML(normalizeHtmlDocument(html));\n return document as unknown as Document;\n}\n\nfunction decodeDataCssUri(href: string): string | null {\n const match = /^data:text\\/css(?:;charset=[^;,]+)?(;base64)?,([\\s\\S]*)$/i.exec(href.trim());\n if (!match) return null;\n const isBase64 = Boolean(match[1]);\n const payload = match[2] ?? '';\n try {\n return isBase64 ? Buffer.from(payload, 'base64').toString('utf8') : decodeURIComponent(payload);\n } catch {\n return null;\n }\n}\n\nfunction rewriteRelativeCssUrls(cssText: string, stylesheetUrl: string): string {\n return cssText.replace(/url\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/gi, (full, quote: string, rawRef: string) => {\n const ref = rawRef.trim();\n if (\n !ref ||\n ref.startsWith('data:') ||\n ref.startsWith('#') ||\n ref.startsWith('http://') ||\n ref.startsWith('https://')\n ) {\n return full;\n }\n try {\n const resolved = new URL(ref, stylesheetUrl).href;\n return `url(${quote}${resolved}${quote})`;\n } catch {\n return full;\n }\n });\n}\n\nfunction findStylesheetLinks(document: Document): Element[] {\n return Array.from(document.querySelectorAll('link')).filter((link) => {\n if (link.hasAttribute('disabled')) return false;\n const relTokens = (link.getAttribute('rel') ?? '')\n .toLowerCase()\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n if (!relTokens.includes('stylesheet') || relTokens.includes('alternate')) {\n return false;\n }\n const media = (link.getAttribute('media') ?? '').trim().toLowerCase();\n if (media === 'print') {\n return false;\n }\n return Boolean((link.getAttribute('href') ?? '').trim());\n });\n}\n\nfunction replaceLinkWithStyle(\n document: Document,\n link: Element,\n cssText: string,\n sourceRef: string,\n): void {\n const styleEl = document.createElement('style');\n styleEl.setAttribute('data-anygrab-inlined', sourceRef);\n styleEl.textContent = cssText;\n if (link.parentNode) {\n link.parentNode.replaceChild(styleEl, link);\n } else {\n (document.head ?? document.documentElement)?.appendChild(styleEl);\n }\n}\n\nexport function inlineDataStylesheets(document: Document): number {\n let count = 0;\n for (const link of findStylesheetLinks(document)) {\n const href = (link.getAttribute('href') ?? '').trim();\n const cssText = decodeDataCssUri(href);\n if (cssText !== null) {\n replaceLinkWithStyle(document, link, cssText, 'data:text/css');\n count++;\n }\n }\n return count;\n}\n\nasync function fetchTextWithTimeout(\n url: string,\n timeoutMs: number,\n fetchFn: typeof fetch,\n extraHeaders?: Record<string, string>,\n accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n): Promise<{ text: string; finalUrl: string }> {\n const controller = new AbortController();\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n try {\n const response = await fetchFn(url, {\n method: 'GET',\n redirect: 'follow',\n signal: controller.signal,\n headers: {\n 'user-agent': DEFAULT_USER_AGENT,\n accept,\n ...extraHeaders,\n },\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} ${response.statusText}`.trim());\n }\n\n const contentLength = Number(response.headers.get('content-length') ?? '0');\n if (contentLength > MAX_CSS_BYTES * 2) {\n throw new Error(`Response payload too large (${contentLength} bytes)`);\n }\n\n const text = await response.text();\n return { text, finalUrl: response.url || url };\n } catch (error: unknown) {\n if (controller.signal.aborted) {\n throw new Error(`Timed out after ${timeoutMs}ms fetching ${url}`);\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function loadUrlDocument(\n url: string,\n options: McpLoaderOptions = {},\n): Promise<LoadedUrlDocument> {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(url);\n } catch {\n throw new Error(`Invalid URL \"${url}\". Expected an http:// or https:// URL.`);\n }\n\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new Error(`Unsupported URL protocol \"${parsedUrl.protocol}\". Expected http: or https:.`);\n }\n\n const fetchFn = options.fetchImpl ?? globalThis.fetch;\n const fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n const cssTimeoutMs = options.cssTimeoutMs ?? DEFAULT_CSS_TIMEOUT_MS;\n\n const { text: html, finalUrl } = await fetchTextWithTimeout(\n parsedUrl.href,\n fetchTimeoutMs,\n fetchFn,\n options.headers,\n );\n\n const document = parseHtmlDocument(html);\n const links = findStylesheetLinks(document).slice(0, MAX_STYLESHEETS);\n const warnings: string[] = [];\n let inlinedStylesheets = 0;\n\n await Promise.all(\n links.map(async (link) => {\n const rawHref = (link.getAttribute('href') ?? '').trim();\n const dataCss = decodeDataCssUri(rawHref);\n if (dataCss !== null) {\n replaceLinkWithStyle(document, link, dataCss, 'data:text/css');\n inlinedStylesheets++;\n return;\n }\n\n let resolvedCssUrl: URL;\n try {\n resolvedCssUrl = new URL(rawHref, finalUrl);\n } catch {\n warnings.push(`Skipped invalid stylesheet URL: ${rawHref}`);\n return;\n }\n\n if (resolvedCssUrl.protocol !== 'http:' && resolvedCssUrl.protocol !== 'https:') {\n return;\n }\n\n try {\n const { text: cssText, finalUrl: cssFinalUrl } = await fetchTextWithTimeout(\n resolvedCssUrl.href,\n cssTimeoutMs,\n fetchFn,\n options.headers,\n 'text/css,*/*;q=0.1',\n );\n if (cssText.length > MAX_CSS_BYTES) {\n warnings.push(`Skipped oversized stylesheet (${cssText.length} chars): ${cssFinalUrl}`);\n return;\n }\n if (cssText.trim().length > 0) {\n replaceLinkWithStyle(\n document,\n link,\n rewriteRelativeCssUrls(cssText, cssFinalUrl),\n cssFinalUrl,\n );\n inlinedStylesheets++;\n } else {\n link.parentNode?.removeChild(link);\n }\n } catch (error: unknown) {\n const reason = error instanceof Error ? error.message : String(error);\n warnings.push(`Failed to inline stylesheet ${resolvedCssUrl.href}: ${reason}`);\n }\n }),\n );\n\n return {\n document,\n finalUrl,\n inlinedStylesheets,\n warnings,\n };\n}\n\nexport function scopeDocumentToSelector(document: Document, selector?: string): void {\n const trimmed = selector?.trim();\n if (!trimmed) return;\n\n let matched: Element | null;\n try {\n matched = document.querySelector(trimmed);\n } catch (error: unknown) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid CSS selector \"${trimmed}\": ${detail}`);\n }\n\n if (!matched) {\n throw new Error(`No element matched CSS selector \"${trimmed}\".`);\n }\n\n if (\n matched === document.documentElement ||\n matched === document.body ||\n matched === document.head\n ) {\n return;\n }\n\n const head = document.head ?? document.documentElement;\n if (document.body && head) {\n for (const styleNode of Array.from(document.body.querySelectorAll('style'))) {\n if (!matched.contains(styleNode)) {\n head.appendChild(styleNode);\n }\n }\n }\n\n const ancestorSpine = new Set<Node>();\n let cursor: Node | null = matched;\n while (cursor) {\n ancestorSpine.add(cursor);\n cursor = cursor.parentNode;\n }\n\n let container: Node | null = document.body;\n while (container && container !== matched) {\n for (const child of Array.from(container.childNodes)) {\n if (!ancestorSpine.has(child)) {\n container.removeChild(child);\n }\n }\n const nextInSpine: ChildNode | undefined = Array.from(container.childNodes).find((child) =>\n ancestorSpine.has(child),\n );\n container = nextInSpine ?? null;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "anygrab-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Model Context Protocol (MCP) server for AnyGrab DOM & UI context distillation.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "botshen & AnyGrab Contributors",
8
+ "homepage": "https://github.com/botshen/anygrab#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/botshen/anygrab.git",
12
+ "directory": "packages/mcp"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/botshen/anygrab/issues"
16
+ },
17
+ "bin": {
18
+ "anygrab-mcp": "./dist/index.js"
19
+ },
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.32.2",
33
+ "linkedom": "^0.18.15",
34
+ "zod": "^3.25.0",
35
+ "anygrab-core": "0.2.0"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "vitest run",
42
+ "clean": "rm -rf dist *.tsbuildinfo"
43
+ }
6
44
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.