🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@metalift/mcp

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@metalift/mcp - npm Package Compare versions

Comparing version
1.0.5
to
1.0.6
+38
-11
dist/client.js

@@ -22,2 +22,31 @@ const B2B_ATTESTATION_HEADER = "X-B2B-Attestation";

}
function formatApiError(body, status) {
if (typeof body === "object" && body !== null) {
const record = body;
const detail = record.detail;
if (typeof detail === "string" && detail) {
return detail;
}
if (typeof detail === "object" && detail !== null) {
const detailRecord = detail;
const message = detailRecord.message;
const code = detailRecord.code;
if (typeof message === "string" && message) {
return typeof code === "string" ? `${message} (${code})` : message;
}
}
const error = record.error;
if (typeof error === "string" && error) {
return error;
}
}
if (typeof body === "string" && body.trim()) {
const trimmed = body.trim();
if (trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html")) {
return `Request failed: ${status} (received HTML instead of JSON — check METALIFT_API_URL points to the scrape API, e.g. http://localhost:8080)`;
}
return trimmed.slice(0, 500);
}
return `Request failed: ${status}`;
}
export class MetaliftClient {

@@ -46,14 +75,12 @@ apiUrl;

});
const body = (await response.json());
const rawBody = await response.text();
let body;
try {
body = rawBody ? JSON.parse(rawBody) : {};
}
catch {
throw new Error(formatApiError(rawBody, response.status));
}
if (!response.ok) {
const detail = typeof body === "object" && body !== null && "detail" in body
? body.detail
: undefined;
const error = typeof body === "object" && body !== null && "error" in body
? body.error
: undefined;
const message = (typeof detail === "string" && detail) ||
(typeof error === "string" && error) ||
`Request failed: ${response.status}`;
throw new Error(message);
throw new Error(formatApiError(body, response.status));
}

@@ -60,0 +87,0 @@ return withBilling(body, billingFromResponse(response));

@@ -15,3 +15,3 @@ #!/usr/bin/env node

title: "Scrape URL",
description: `Scrape a single URL into markdown, HTML, or text for LLM context. Default: fast direct static article extraction (strategy=article, render=static, proxy=direct, 10s timeout). For WAF, SPA, retail, or JS-heavy pages pass strategy=auto or a specific strategy (spa, cloudflare, retail). Response includes credits_charged based on actual usage (static=1, JS=5, premium=10+). ${COMPLIANCE_NOTICE}`,
description: `Scrape a single URL into markdown, HTML, or text for LLM context. Default: fast direct static article extraction (strategy=article, render=static, proxy=direct, 10s timeout). For full page HTML on static sites use strategy=download with formats=["html"] (1 credit, all tiers). strategy=raw and full-page HTML without download require Enterprise tier. For WAF, SPA, retail, or JS-heavy pages pass strategy=auto or a specific strategy (spa, cloudflare, retail). Response includes credits_charged based on actual usage (static=1, JS=5, premium=10+). ${COMPLIANCE_NOTICE}`,
inputSchema: {

@@ -29,3 +29,3 @@ url: z.string().url(),

.optional()
.describe("Scrape strategy: auto, article, spa, cloudflare, authenticated, listing, retail, jsonld, raw, or comma-separated chain. See /v1/strategies for protection levels and credit estimates."),
.describe("Scrape strategy: auto, article, spa, cloudflare, authenticated, listing, retail, jsonld, download, raw, or comma-separated chain. Use download for full static HTML. See /v1/strategies for protection levels and credit estimates."),
cookies: z.record(z.string()).optional(),

@@ -32,0 +32,0 @@ cookie_header: z

/** Default timeout for the fast direct static markdown path. */
export const FAST_SCRAPE_TIMEOUT_MS = 10_000;
function wantsFullPageHtml(args) {
return args.formats?.includes("html") === true && args.only_main_content === false;
}
function shouldAutoDownload(args) {
if (!wantsFullPageHtml(args)) {
return false;
}
const primary = (args.strategy ?? "auto").split(",")[0]?.trim().toLowerCase();
if (primary === "raw" || primary === "download") {
return false;
}
if (primary && primary !== "auto" && primary !== "article") {
return false;
}
return true;
}
function isMarkdownOnlyFormats(formats) {

@@ -11,2 +27,11 @@ return formats === undefined || (formats.length === 1 && formats[0] === "markdown");

export function normalizeScrapeArgs(args) {
if (shouldAutoDownload(args)) {
return {
...args,
strategy: "download",
render: args.render ?? "static",
proxy: args.proxy ?? "direct",
timeout_ms: args.timeout_ms ?? FAST_SCRAPE_TIMEOUT_MS,
};
}
const hasAdvancedOpts = args.strategy !== undefined ||

@@ -27,6 +52,5 @@ args.render !== undefined ||

strategy: "article",
render: "static",
proxy: "direct",
render: "auto",
timeout_ms: args.timeout_ms ?? FAST_SCRAPE_TIMEOUT_MS,
};
}

@@ -10,4 +10,3 @@ import assert from "node:assert/strict";

strategy: "article",
render: "static",
proxy: "direct",
render: "auto",
timeout_ms: FAST_SCRAPE_TIMEOUT_MS,

@@ -64,2 +63,24 @@ });

});
test("normalizeScrapeArgs auto-selects download for full-page HTML", () => {
const args = normalizeScrapeArgs({
url: BASE_URL,
formats: ["html"],
only_main_content: false,
});
assert.equal(args.strategy, "download");
assert.equal(args.render, "static");
assert.equal(args.proxy, "direct");
assert.equal(args.timeout_ms, FAST_SCRAPE_TIMEOUT_MS);
});
test("normalizeScrapeArgs preserves explicit cloudflare for full-page HTML", () => {
const args = normalizeScrapeArgs({
url: BASE_URL,
formats: ["html"],
only_main_content: false,
strategy: "cloudflare",
render: "dynamic",
});
assert.equal(args.strategy, "cloudflare");
assert.equal(args.render, "dynamic");
});
test("normalizeScrapeArgs preserves non-markdown formats", () => {

@@ -77,4 +98,3 @@ const args = normalizeScrapeArgs({ url: BASE_URL, formats: ["html"] });

assert.equal(args.strategy, "article");
assert.equal(args.render, "static");
assert.equal(args.proxy, "direct");
assert.equal(args.render, "auto");
});
{
"name": "@metalift/mcp",
"mcpName": "io.github.MetaLift-AI/metalift",
"version": "1.0.5",
"version": "1.0.6",
"description": "Metalift MCP server for AI agents",

@@ -6,0 +6,0 @@ "license": "SEE LICENSE IN LICENSE",